675 lines
15 KiB
C
675 lines
15 KiB
C
/*
|
|
* libiio - Library for interfacing industrial I/O (IIO) devices
|
|
*
|
|
* Copyright (C) 2014-2016 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 "iiod-client.h"
|
|
#include "iio-lock.h"
|
|
#include "iio-private.h"
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
struct iiod_client {
|
|
struct iio_context_pdata *pdata;
|
|
const struct iiod_client_ops *ops;
|
|
struct iio_mutex *lock;
|
|
};
|
|
|
|
static ssize_t iiod_client_read_integer(struct iiod_client *client,
|
|
void *desc, int *val)
|
|
{
|
|
unsigned int i;
|
|
char buf[1024], *ptr = NULL, *end;
|
|
ssize_t ret;
|
|
int value;
|
|
|
|
do {
|
|
ret = client->ops->read_line(client->pdata,
|
|
desc, buf, sizeof(buf));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
for (i = 0; i < (unsigned int) ret; i++) {
|
|
if (buf[i] != '\n') {
|
|
if (!ptr)
|
|
ptr = &buf[i];
|
|
} else if (!!ptr) {
|
|
break;
|
|
}
|
|
}
|
|
} while (!ptr);
|
|
|
|
buf[i] = '\0';
|
|
|
|
value = (int) strtol(ptr, &end, 10);
|
|
if (ptr == end)
|
|
return -EINVAL;
|
|
|
|
*val = value;
|
|
return 0;
|
|
}
|
|
|
|
static int iiod_client_exec_command(struct iiod_client *client,
|
|
void *desc, const char *cmd)
|
|
{
|
|
int resp;
|
|
ssize_t ret;
|
|
|
|
ret = client->ops->write(client->pdata, desc, cmd, strlen(cmd));
|
|
if (ret < 0)
|
|
return (int) ret;
|
|
|
|
ret = iiod_client_read_integer(client, desc, &resp);
|
|
return ret < 0 ? (int) ret : resp;
|
|
}
|
|
|
|
static ssize_t iiod_client_write_all(struct iiod_client *client,
|
|
void *desc, const void *src, size_t len)
|
|
{
|
|
struct iio_context_pdata *pdata = client->pdata;
|
|
const struct iiod_client_ops *ops = client->ops;
|
|
uintptr_t ptr = (uintptr_t) src;
|
|
|
|
while (len) {
|
|
ssize_t ret = ops->write(pdata, desc, (const void *) ptr, len);
|
|
|
|
if (ret < 0) {
|
|
if (ret == -EINTR)
|
|
continue;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return -EPIPE;
|
|
|
|
ptr += ret;
|
|
len -= ret;
|
|
}
|
|
|
|
return (ssize_t) (ptr - (uintptr_t) src);
|
|
}
|
|
|
|
static ssize_t iiod_client_read_all(struct iiod_client *client,
|
|
void *desc, void *dst, size_t len)
|
|
{
|
|
struct iio_context_pdata *pdata = client->pdata;
|
|
const struct iiod_client_ops *ops = client->ops;
|
|
uintptr_t ptr = (uintptr_t) dst;
|
|
|
|
while (len) {
|
|
ssize_t ret = ops->read(pdata, desc, (void *) ptr, len);
|
|
|
|
if (ret < 0) {
|
|
if (ret == -EINTR)
|
|
continue;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return -EPIPE;
|
|
|
|
ptr += ret;
|
|
len -= ret;
|
|
}
|
|
|
|
return (ssize_t) (ptr - (uintptr_t) dst);
|
|
}
|
|
|
|
struct iiod_client * iiod_client_new(struct iio_context_pdata *pdata,
|
|
struct iio_mutex *lock, const struct iiod_client_ops *ops)
|
|
{
|
|
struct iiod_client *client;
|
|
|
|
client = malloc(sizeof(*client));
|
|
if (!client) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
client->lock = lock;
|
|
client->pdata = pdata;
|
|
client->ops = ops;
|
|
return client;
|
|
}
|
|
|
|
void iiod_client_destroy(struct iiod_client *client)
|
|
{
|
|
free(client);
|
|
}
|
|
|
|
int iiod_client_get_version(struct iiod_client *client, void *desc,
|
|
unsigned int *major, unsigned int *minor, char *git_tag)
|
|
{
|
|
struct iio_context_pdata *pdata = client->pdata;
|
|
const struct iiod_client_ops *ops = client->ops;
|
|
char buf[256], *ptr = buf, *end;
|
|
long maj, min;
|
|
int ret;
|
|
|
|
iio_mutex_lock(client->lock);
|
|
|
|
ret = ops->write(pdata, desc, "VERSION\r\n", sizeof("VERSION\r\n") - 1);
|
|
if (ret < 0) {
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
ret = ops->read_line(pdata, desc, buf, sizeof(buf));
|
|
iio_mutex_unlock(client->lock);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
maj = strtol(ptr, &end, 10);
|
|
if (ptr == end)
|
|
return -EIO;
|
|
|
|
ptr = end + 1;
|
|
min = strtol(ptr, &end, 10);
|
|
if (ptr == end)
|
|
return -EIO;
|
|
|
|
ptr = end + 1;
|
|
if (buf + ret < ptr + 8)
|
|
return -EIO;
|
|
|
|
/* Strip the \n */
|
|
ptr[buf + ret - ptr - 1] = '\0';
|
|
|
|
if (major)
|
|
*major = (unsigned int) maj;
|
|
if (minor)
|
|
*minor = (unsigned int) min;
|
|
if (git_tag)
|
|
strncpy(git_tag, ptr, 8);
|
|
return 0;
|
|
}
|
|
|
|
int iiod_client_get_trigger(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev, const struct iio_device **trigger)
|
|
{
|
|
const struct iio_context *ctx = iio_device_get_context(dev);
|
|
unsigned int i, nb_devices = iio_context_get_devices_count(ctx);
|
|
char buf[1024];
|
|
unsigned int name_len;
|
|
int ret;
|
|
|
|
iio_snprintf(buf, sizeof(buf), "GETTRIG %s\r\n",
|
|
iio_device_get_id(dev));
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = iiod_client_exec_command(client, desc, buf);
|
|
|
|
if (ret == 0)
|
|
*trigger = NULL;
|
|
if (ret <= 0)
|
|
goto out_unlock;
|
|
|
|
if ((unsigned int) ret > sizeof(buf) - 1) {
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
|
|
name_len = ret;
|
|
|
|
ret = (int) iiod_client_read_all(client, desc, buf, name_len + 1);
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
ret = -ENXIO;
|
|
|
|
for (i = 0; i < nb_devices; i++) {
|
|
struct iio_device *cur = iio_context_get_device(ctx, i);
|
|
|
|
if (iio_device_is_trigger(cur)) {
|
|
const char *name = iio_device_get_name(cur);
|
|
|
|
if (!name)
|
|
continue;
|
|
|
|
if (!strncmp(name, buf, name_len)) {
|
|
*trigger = cur;
|
|
ret = 0;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
out_unlock:
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
int iiod_client_set_trigger(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev, const struct iio_device *trigger)
|
|
{
|
|
char buf[1024];
|
|
int ret;
|
|
|
|
if (trigger) {
|
|
iio_snprintf(buf, sizeof(buf), "SETTRIG %s %s\r\n",
|
|
iio_device_get_id(dev),
|
|
iio_device_get_id(trigger));
|
|
} else {
|
|
iio_snprintf(buf, sizeof(buf), "SETTRIG %s\r\n",
|
|
iio_device_get_id(dev));
|
|
}
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = iiod_client_exec_command(client, desc, buf);
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
int iiod_client_set_kernel_buffers_count(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev, unsigned int nb_blocks)
|
|
{
|
|
int ret;
|
|
char buf[1024];
|
|
|
|
iio_snprintf(buf, sizeof(buf), "SET %s BUFFERS_COUNT %u\r\n",
|
|
iio_device_get_id(dev), nb_blocks);
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = iiod_client_exec_command(client, desc, buf);
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
int iiod_client_set_timeout(struct iiod_client *client,
|
|
void *desc, unsigned int timeout)
|
|
{
|
|
int ret;
|
|
char buf[1024];
|
|
|
|
iio_snprintf(buf, sizeof(buf), "TIMEOUT %u\r\n", timeout);
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = iiod_client_exec_command(client, desc, buf);
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int iiod_client_discard(struct iiod_client *client, void *desc,
|
|
char *buf, size_t buf_len, size_t to_discard)
|
|
{
|
|
do {
|
|
size_t read_len;
|
|
ssize_t ret;
|
|
|
|
if (to_discard > buf_len)
|
|
read_len = buf_len;
|
|
else
|
|
read_len = to_discard;
|
|
|
|
ret = iiod_client_read_all(client, desc, buf, read_len);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
to_discard -= (size_t) ret;
|
|
} while (to_discard);
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t 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)
|
|
{
|
|
const char *id = iio_device_get_id(dev);
|
|
char buf[1024];
|
|
ssize_t ret;
|
|
|
|
if (attr) {
|
|
if (chn) {
|
|
if (!iio_channel_find_attr(chn, attr))
|
|
return -ENOENT;
|
|
} else {
|
|
switch (type) {
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
if (!iio_device_find_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
if (!iio_device_find_debug_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
if (!iio_device_find_buffer_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (chn) {
|
|
iio_snprintf(buf, sizeof(buf), "READ %s %s %s %s\r\n", id,
|
|
iio_channel_is_output(chn) ? "OUTPUT" : "INPUT",
|
|
iio_channel_get_id(chn), attr ? attr : "");
|
|
} else {
|
|
switch (type) {
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
iio_snprintf(buf, sizeof(buf), "READ %s %s\r\n",
|
|
id, attr ? attr : "");
|
|
break;
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
iio_snprintf(buf, sizeof(buf), "READ %s DEBUG %s\r\n",
|
|
id, attr ? attr : "");
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
iio_snprintf(buf, sizeof(buf), "READ %s BUFFER %s\r\n",
|
|
id, attr ? attr : "");
|
|
break;
|
|
}
|
|
}
|
|
|
|
iio_mutex_lock(client->lock);
|
|
|
|
ret = (ssize_t) iiod_client_exec_command(client, desc, buf);
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
if ((size_t) ret + 1 > len) {
|
|
iiod_client_discard(client, desc, dest, len, ret + 1);
|
|
ret = -EIO;
|
|
goto out_unlock;
|
|
}
|
|
|
|
/* +1: Also read the trailing \n */
|
|
ret = iiod_client_read_all(client, desc, dest, ret + 1);
|
|
|
|
if (ret > 0) {
|
|
/* Discard the trailing \n */
|
|
ret--;
|
|
|
|
/* Replace it with a \0 just in case */
|
|
dest[ret] = '\0';
|
|
}
|
|
|
|
out_unlock:
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t 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)
|
|
{
|
|
struct iio_context_pdata *pdata = client->pdata;
|
|
const struct iiod_client_ops *ops = client->ops;
|
|
const char *id = iio_device_get_id(dev);
|
|
char buf[1024];
|
|
ssize_t ret;
|
|
int resp;
|
|
|
|
if (attr) {
|
|
if (chn) {
|
|
if (!iio_channel_find_attr(chn, attr))
|
|
return -ENOENT;
|
|
} else {
|
|
switch (type) {
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
if (!iio_device_find_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
if (!iio_device_find_debug_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
if (!iio_device_find_buffer_attr(dev, attr))
|
|
return -ENOENT;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (chn) {
|
|
iio_snprintf(buf, sizeof(buf), "WRITE %s %s %s %s %lu\r\n", id,
|
|
iio_channel_is_output(chn) ? "OUTPUT" : "INPUT",
|
|
iio_channel_get_id(chn), attr ? attr : "",
|
|
(unsigned long) len);
|
|
} else {
|
|
switch (type) {
|
|
case IIO_ATTR_TYPE_DEVICE:
|
|
iio_snprintf(buf, sizeof(buf), "WRITE %s %s %lu\r\n",
|
|
id, attr ? attr : "", (unsigned long) len);
|
|
break;
|
|
case IIO_ATTR_TYPE_DEBUG:
|
|
iio_snprintf(buf, sizeof(buf), "WRITE %s DEBUG %s %lu\r\n",
|
|
id, attr ? attr : "", (unsigned long) len);
|
|
break;
|
|
case IIO_ATTR_TYPE_BUFFER:
|
|
iio_snprintf(buf, sizeof(buf), "WRITE %s BUFFER %s %lu\r\n",
|
|
id, attr ? attr : "", (unsigned long) len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = ops->write(pdata, desc, buf, strlen(buf));
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
ret = iiod_client_write_all(client, desc, src, len);
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
ret = iiod_client_read_integer(client, desc, &resp);
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
ret = (ssize_t) resp;
|
|
|
|
out_unlock:
|
|
iio_mutex_unlock(client->lock);
|
|
return ret;
|
|
}
|
|
|
|
struct iio_context * iiod_client_create_context(
|
|
struct iiod_client *client, void *desc)
|
|
{
|
|
struct iio_context *ctx = NULL;
|
|
size_t xml_len;
|
|
char *xml;
|
|
int ret;
|
|
|
|
iio_mutex_lock(client->lock);
|
|
ret = iiod_client_exec_command(client, desc, "PRINT\r\n");
|
|
if (ret < 0)
|
|
goto out_unlock;
|
|
|
|
xml_len = (size_t) ret;
|
|
xml = malloc(xml_len + 1);
|
|
if (!xml) {
|
|
ret = -ENOMEM;
|
|
goto out_unlock;
|
|
}
|
|
|
|
/* +1: Also read the trailing \n */
|
|
ret = (int) iiod_client_read_all(client, desc, xml, xml_len + 1);
|
|
if (ret < 0)
|
|
goto out_free_xml;
|
|
|
|
ctx = iio_create_xml_context_mem(xml, xml_len);
|
|
if (!ctx)
|
|
ret = -errno;
|
|
|
|
out_free_xml:
|
|
free(xml);
|
|
out_unlock:
|
|
iio_mutex_unlock(client->lock);
|
|
if (!ctx)
|
|
errno = -ret;
|
|
return ctx;
|
|
}
|
|
|
|
int iiod_client_open_unlocked(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev, size_t samples_count, bool cyclic)
|
|
{
|
|
char buf[1024], *ptr;
|
|
size_t i;
|
|
|
|
iio_snprintf(buf, sizeof(buf), "OPEN %s %lu ",
|
|
iio_device_get_id(dev), (unsigned long) samples_count);
|
|
ptr = buf + strlen(buf);
|
|
|
|
for (i = dev->words; i > 0; i--, ptr += 8) {
|
|
iio_snprintf(ptr, (ptr - buf) + i * 8, "%08" PRIx32,
|
|
dev->mask[i - 1]);
|
|
}
|
|
|
|
strcpy(ptr, cyclic ? " CYCLIC\r\n" : "\r\n");
|
|
|
|
return iiod_client_exec_command(client, desc, buf);
|
|
}
|
|
|
|
int iiod_client_close_unlocked(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev)
|
|
{
|
|
char buf[1024];
|
|
|
|
iio_snprintf(buf, sizeof(buf), "CLOSE %s\r\n", iio_device_get_id(dev));
|
|
return iiod_client_exec_command(client, desc, buf);
|
|
}
|
|
|
|
static int iiod_client_read_mask(struct iiod_client *client,
|
|
void *desc, uint32_t *mask, size_t words)
|
|
{
|
|
size_t i;
|
|
ssize_t ret;
|
|
char *buf, *ptr;
|
|
|
|
buf = malloc(words * 8 + 1);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = iiod_client_read_all(client, desc, buf, words * 8 + 1);
|
|
if (ret < 0)
|
|
goto out_buf_free;
|
|
else
|
|
ret = 0;
|
|
|
|
buf[words*8] = '\0';
|
|
|
|
DEBUG("Reading mask\n");
|
|
|
|
for (i = words, ptr = buf; i > 0; i--) {
|
|
sscanf(ptr, "%08" PRIx32, &mask[i - 1]);
|
|
DEBUG("mask[%lu] = 0x%08" PRIx32 "\n",
|
|
(unsigned long)(i - 1), mask[i - 1]);
|
|
|
|
ptr = (char *) ((uintptr_t) ptr + 8);
|
|
}
|
|
|
|
out_buf_free:
|
|
free(buf);
|
|
return (int) ret;
|
|
}
|
|
|
|
ssize_t 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)
|
|
{
|
|
unsigned int nb_channels = iio_device_get_channels_count(dev);
|
|
uintptr_t ptr = (uintptr_t) dst;
|
|
char buf[1024];
|
|
ssize_t ret, read = 0;
|
|
|
|
if (!len || words != (nb_channels + 31) / 32)
|
|
return -EINVAL;
|
|
|
|
iio_snprintf(buf, sizeof(buf), "READBUF %s %lu\r\n",
|
|
iio_device_get_id(dev), (unsigned long) len);
|
|
|
|
ret = iiod_client_write_all(client, desc, buf, strlen(buf));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
do {
|
|
int to_read;
|
|
|
|
ret = iiod_client_read_integer(client, desc, &to_read);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (to_read < 0)
|
|
return (ssize_t) to_read;
|
|
if (!to_read)
|
|
break;
|
|
|
|
if (mask) {
|
|
ret = iiod_client_read_mask(client, desc, mask, words);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
mask = NULL; /* We read the mask only once */
|
|
}
|
|
|
|
ret = iiod_client_read_all(client, desc, (char *) ptr, to_read);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ptr += ret;
|
|
read += ret;
|
|
len -= ret;
|
|
} while (len);
|
|
|
|
return read;
|
|
}
|
|
|
|
ssize_t iiod_client_write_unlocked(struct iiod_client *client, void *desc,
|
|
const struct iio_device *dev, const void *src, size_t len)
|
|
{
|
|
ssize_t ret;
|
|
char buf[1024];
|
|
int val;
|
|
|
|
iio_snprintf(buf, sizeof(buf), "WRITEBUF %s %lu\r\n",
|
|
dev->id, (unsigned long) len);
|
|
|
|
ret = iiod_client_write_all(client, desc, buf, strlen(buf));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = iiod_client_read_integer(client, desc, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (val < 0)
|
|
return (ssize_t) val;
|
|
|
|
ret = iiod_client_write_all(client, desc, src, len);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = iiod_client_read_integer(client, desc, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (val < 0)
|
|
return (ssize_t) val;
|
|
|
|
return (ssize_t) len;
|
|
}
|