/* * libiio - Library for interfacing industrial I/O (IIO) devices * * Copyright (C) 2014-2015 Analog Devices, Inc. * Author: Paul Cercueil * * 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 "iio-config.h" #include "iio-private.h" #include #include struct callback_wrapper_data { ssize_t (*callback)(const struct iio_channel *, void *, size_t, void *); void *data; uint32_t *mask; }; static bool device_is_high_speed(const struct iio_device *dev) { /* Little trick: We call the backend's get_buffer() function, which is * for now only implemented in the Local backend, with a NULL pointer. * It will return -ENOSYS if the device is not high speed, and either * -EBADF or -EINVAL otherwise. */ const struct iio_backend_ops *ops = dev->ctx->ops; return !!ops->get_buffer && (ops->get_buffer(dev, NULL, 0, NULL, 0) != -ENOSYS); } struct iio_buffer * iio_device_create_buffer(const struct iio_device *dev, size_t samples_count, bool cyclic) { int ret = -EINVAL; struct iio_buffer *buf; unsigned int sample_size = iio_device_get_sample_size(dev); if (!sample_size || !samples_count) goto err_set_errno; buf = malloc(sizeof(*buf)); if (!buf) { ret = -ENOMEM; goto err_set_errno; } buf->dev_sample_size = sample_size; buf->length = sample_size * samples_count; buf->dev = dev; buf->mask = calloc(dev->words, sizeof(*buf->mask)); if (!buf->mask) { ret = -ENOMEM; goto err_free_buf; } /* Set the default channel mask to the one used by the device. * While input buffers will erase this as soon as the refill function * is used, it is useful for output buffers, as it permits * iio_buffer_foreach_sample to be used. */ memcpy(buf->mask, dev->mask, dev->words * sizeof(*buf->mask)); ret = iio_device_open(dev, samples_count, cyclic); if (ret < 0) goto err_free_mask; buf->dev_is_high_speed = device_is_high_speed(dev); if (buf->dev_is_high_speed) { /* Dequeue the first buffer, so that buf->buffer is correctly * initialized */ buf->buffer = NULL; if (iio_device_is_tx(dev)) { ret = dev->ctx->ops->get_buffer(dev, &buf->buffer, buf->length, buf->mask, dev->words); if (ret < 0) goto err_close_device; } } else { buf->buffer = malloc(buf->length); if (!buf->buffer) { ret = -ENOMEM; goto err_close_device; } } buf->sample_size = iio_device_get_sample_size_mask(dev, buf->mask, dev->words); buf->data_length = buf->length; return buf; err_close_device: iio_device_close(dev); err_free_mask: free(buf->mask); err_free_buf: free(buf); err_set_errno: errno = -ret; return NULL; } void iio_buffer_destroy(struct iio_buffer *buffer) { iio_device_close(buffer->dev); if (!buffer->dev_is_high_speed) free(buffer->buffer); free(buffer->mask); free(buffer); } int iio_buffer_get_poll_fd(struct iio_buffer *buffer) { return iio_device_get_poll_fd(buffer->dev); } int iio_buffer_set_blocking_mode(struct iio_buffer *buffer, bool blocking) { return iio_device_set_blocking_mode(buffer->dev, blocking); } ssize_t iio_buffer_refill(struct iio_buffer *buffer) { ssize_t read; const struct iio_device *dev = buffer->dev; if (buffer->dev_is_high_speed) { read = dev->ctx->ops->get_buffer(dev, &buffer->buffer, buffer->length, buffer->mask, dev->words); } else { read = iio_device_read_raw(dev, buffer->buffer, buffer->length, buffer->mask, dev->words); } if (read >= 0) { buffer->data_length = read; buffer->sample_size = iio_device_get_sample_size_mask(dev, buffer->mask, dev->words); } return read; } ssize_t iio_buffer_push(struct iio_buffer *buffer) { const struct iio_device *dev = buffer->dev; ssize_t ret; if (buffer->dev_is_high_speed) { void *buf; ret = dev->ctx->ops->get_buffer(dev, &buf, buffer->data_length, buffer->mask, dev->words); if (ret >= 0) { buffer->buffer = buf; ret = (ssize_t) buffer->data_length; } } else { void *ptr = buffer->buffer; size_t tmp_len; /* iio_device_write_raw doesn't guarantee that all bytes are * written */ for (tmp_len = buffer->data_length; tmp_len; ) { ret = iio_device_write_raw(dev, ptr, tmp_len); if (ret < 0) goto out_reset_data_length; tmp_len -= ret; ptr = (void *) ((uintptr_t) ptr + ret); } ret = (ssize_t) buffer->data_length; } out_reset_data_length: buffer->data_length = buffer->length; return ret; } ssize_t iio_buffer_push_partial(struct iio_buffer *buffer, size_t samples_count) { size_t new_len = samples_count * buffer->dev_sample_size; if (new_len == 0 || new_len > buffer->length) return -EINVAL; buffer->data_length = new_len; return iio_buffer_push(buffer); } ssize_t iio_buffer_foreach_sample(struct iio_buffer *buffer, ssize_t (*callback)(const struct iio_channel *, void *, size_t, void *), void *d) { uintptr_t ptr = (uintptr_t) buffer->buffer, start = ptr, end = ptr + buffer->data_length; const struct iio_device *dev = buffer->dev; ssize_t processed = 0; if (buffer->sample_size == 0) return -EINVAL; if (buffer->data_length < buffer->dev_sample_size) return 0; while (end - ptr >= (size_t) buffer->sample_size) { unsigned int i; for (i = 0; i < dev->nb_channels; i++) { const struct iio_channel *chn = dev->channels[i]; unsigned int length = chn->format.length / 8; if (chn->index < 0) break; /* Test if the buffer has samples for this channel */ if (!TEST_BIT(buffer->mask, chn->number)) continue; if ((ptr - start) % length) ptr += length - ((ptr - start) % length); /* Test if the client wants samples from this channel */ if (TEST_BIT(dev->mask, chn->number)) { ssize_t ret = callback(chn, (void *) ptr, length, d); if (ret < 0) return ret; else processed += ret; } if (i == dev->nb_channels - 1 || dev->channels[ i + 1]->index != chn->index) ptr += length * chn->format.repeat; } } return processed; } void * iio_buffer_start(const struct iio_buffer *buffer) { return buffer->buffer; } void * iio_buffer_first(const struct iio_buffer *buffer, const struct iio_channel *chn) { size_t len; unsigned int i; uintptr_t ptr = (uintptr_t) buffer->buffer, start = ptr; if (!iio_channel_is_enabled(chn)) return iio_buffer_end(buffer); for (i = 0; i < buffer->dev->nb_channels; i++) { struct iio_channel *cur = buffer->dev->channels[i]; len = cur->format.length / 8 * cur->format.repeat; /* NOTE: dev->channels are ordered by index */ if (cur->index < 0 || cur->index == chn->index) break; /* Test if the buffer has samples for this channel */ if (!TEST_BIT(buffer->mask, cur->number)) continue; /* Two channels with the same index use the same samples */ if (i > 0 && cur->index == buffer->dev->channels[i - 1]->index) continue; if ((ptr - start) % len) ptr += len - ((ptr - start) % len); ptr += len; } len = chn->format.length / 8; if ((ptr - start) % len) ptr += len - ((ptr - start) % len); return (void *) ptr; } ptrdiff_t iio_buffer_step(const struct iio_buffer *buffer) { return (ptrdiff_t) buffer->sample_size; } void * iio_buffer_end(const struct iio_buffer *buffer) { return (void *) ((uintptr_t) buffer->buffer + buffer->data_length); } void iio_buffer_set_data(struct iio_buffer *buf, void *data) { buf->userdata = data; } void * iio_buffer_get_data(const struct iio_buffer *buf) { return buf->userdata; } const struct iio_device * iio_buffer_get_device(const struct iio_buffer *buf) { return buf->dev; } void iio_buffer_cancel(struct iio_buffer *buf) { const struct iio_backend_ops *ops = buf->dev->ctx->ops; if (ops->cancel) ops->cancel(buf->dev); }