<commit_before>/*
 * libiio - Library for interfacing industrial I/O (IIO) devices
 *
 * Copyright (C) 2014 Analog Devices, Inc.
 * Author: <NAME> <<EMAIL>>
 *
 * 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 "iio-private.h"

#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/utsname.h>

#define DEFAULT_TIMEOUT_MS 1000

#define NB_BLOCKS 4

#define BLOCK_ALLOC_IOCTL   _IOWR('i', 0xa0, struct block_alloc_req)
#define BLOCK_FREE_IOCTL      _IO('i', 0xa1)
#define BLOCK_QUERY_IOCTL   _IOWR('i', 0xa2, struct block)
#define BLOCK_ENQUEUE_IOCTL _IOWR('i', 0xa3, struct block)
#define BLOCK_DEQUEUE_IOCTL _IOWR('i', 0xa4, struct block)

#define BLOCK_FLAG_CYCLIC BIT(1)

/* Forward declarations */
static ssize_t local_read_dev_attr(const struct iio_device *dev,
		const char *attr, char *dst, size_t len, bool is_debug);
static ssize_t local_read_chn_attr(const struct iio_channel *chn,
		const char *attr, char *dst, size_t len);
static ssize_t local_write_dev_attr(const struct iio_device *dev,
		const char *attr, const char *src, size_t len, bool is_debug);
static ssize_t local_write_chn_attr(const struct iio_channel *chn,
		const char *attr, const char *src, size_t len);

struct block_alloc_req {
	uint32_t type,
		 size,
		 count,
		 id;
};

struct block {
	uint32_t id,
		 size,
		 bytes_used,
		 type,
		 flags,
		 offset;
	uint64_t timestamp;
};

struct iio_device_pdata {
	FILE *f;
	unsigned int samples_count, nb_blocks;

	struct block blocks[NB_BLOCKS];
	void *addrs[NB_BLOCKS];
	int last_dequeued;
	bool is_high_speed, cyclic, cyclic_buffer_enqueued, buffer_enabled;
};

static const char * const device_attrs_blacklist[] = {
	"dev",
	"uevent",
};

static const char * const modifier_names[] = {
	[IIO_MOD_X] = "x",
	[IIO_MOD_Y] = "y",
	[IIO_MOD_Z] = "z",
	[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
	[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
	[IIO_MOD_LIGHT_BOTH] = "both",
	[IIO_MOD_LIGHT_IR] = "ir",
	[IIO_MOD_LIGHT_CLEAR] = "clear",
	[IIO_MOD_LIGHT_RED] = "red",
	[IIO_MOD_LIGHT_GREEN] = "green",
	[IIO_MOD_LIGHT_BLUE] = "blue",
	[IIO_MOD_I] = "i",
	[IIO_MOD_Q] = "q",
};

/*
 * Looks for a IIO channel modifier at the beginning of the string s. If a
 * modifier was found the symbolic constant (IIO_MOD_*) is returned, otherwise
 * IIO_NO_MOD is returned. If a modifier was found len_p will be update with the
 * length of the modifier.
 */
static unsigned int find_modifier(const char *s, size_t *len_p)
{
	unsigned int i;
	size_t len;

	for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
		if (!modifier_names[i])
			continue;
		len = strlen(modifier_names[i]);
		if (strncmp(s, modifier_names[i], len) == 0 && s[len] == '_') {
			if (len_p)
				*len_p = len;
			return i;
		}
	}

	return IIO_NO_MOD;
}

static void local_shutdown(struct iio_context *ctx)
{
	/* Free the backend data stored in every device structure */
	unsigned int i;
	for (i = 0; i < ctx->nb_devices; i++)
		free(ctx->devices[i]->pdata);
}

/** Shrinks the first nb characters of a string
 * e.g. strcut("foobar", 4) replaces the content with "ar". */
static void strcut(char *str, int nb)
{
	char *ptr = str + nb;
	while (*ptr)
		*str++ = *ptr++;
	*str = 0;
}

static int set_channel_name(struct iio_channel *chn)
{
	size_t prefix_len = 0;
	const char *attr0;
	const char *ptr;
	unsigned int i;

	if (chn->nb_attrs < 2)
		return 0;

	attr0 = ptr = chn->attrs[0].name;

	while (true) {
		bool can_fix = true;
		size_t len;

		ptr = strchr(ptr, '_');
		if (!ptr)
			break;

		len = ptr - attr0;
		for (i = 1; can_fix && i < chn->nb_attrs; i++)
			can_fix = !strncmp(attr0, chn->attrs[i].name, len);

		if (!can_fix)
			break;

		prefix_len = len;
		ptr = ptr + 1;
	}

	if (prefix_len) {
		char *name;

		name = malloc(prefix_len + 1);
		if (!name)
			return -ENOMEM;
		strncpy(name, attr0, prefix_len);
		name[prefix_len] = '\0';
		DEBUG("Setting name of channel %s to %s\n", chn->id, name);
		chn->name = name;

		/* Shrink the attribute name */
		for (i = 0; i < chn->nb_attrs; i++)
			strcut(chn->attrs[i].name, prefix_len + 1);
	}

	return 0;
}

static int device_check_ready(const struct iio_device *dev, bool do_write)
{
	struct pollfd pollfd = {
		.fd = fileno(dev->pdata->f),
		.events = do_write ? POLLOUT : POLLIN,
	};
	int ret = poll(&pollfd, 1, dev->ctx->rw_timeout_ms);
	if (ret < 0)
		return -errno;
	if (!ret)
		return -ETIMEDOUT;
	if (pollfd.revents & POLLNVAL)
		return -EBADF;
	if (!(pollfd.revents & (do_write ? POLLOUT : POLLIN)))
		return -EIO;
	return 0;
}

static ssize_t local_read(const struct iio_device *dev,
		void *dst, size_t len, uint32_t *mask, size_t words)
{
	ssize_t ret;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	if (!f)
		return -EBADF;
	if (words != dev->words)
		return -EINVAL;

	if (!pdata->buffer_enabled) {
		ret = local_write_dev_attr(dev,
				"buffer/enable", "1", 2, false);
		if (ret < 0)
			return ret;
		else
			pdata->buffer_enabled = true;
	}

	ret = device_check_ready(dev, false);
	if (ret < 0)
		return ret;

	memcpy(mask, dev->mask, words);
	ret = fread(dst, 1, len, f);
	fflush(f);
	if (ferror(f))
		ret = -errno;
	return ret ? ret : -EIO;
}

static ssize_t local_write(const struct iio_device *dev,
		const void *src, size_t len)
{
	ssize_t ret;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	if (!f)
		return -EBADF;

	/* Writing is forbidden in cyclic mode with devices without the
	 * high-speed mmap interface, except for the devices starting with
	 * "cf-": in this case only cyclic mode is allowed. */
	if (!pdata->is_high_speed && pdata->cyclic !=
			(dev->name && !strncmp(dev->name, "cf-", 3)))
		return -EACCES;

	/* We use write() instead of fwrite() here, to avoid the internal
	 * buffering of the FILE API. This ensures that a waveform written in
	 * cyclic mode will be written in only one system call, as the kernel
	 * sizes the buffer according to the length of the first write. */
	if (pdata->cyclic) {
		ret = device_check_ready(dev, true);
		if (ret < 0)
			return ret;

		ret = write(fileno(f), src, len);
		if (ret < 0)
			return -errno;
	}

	if (!pdata->buffer_enabled) {
		ssize_t err = local_write_dev_attr(dev,
				"buffer/enable", "1", 2, false);
		if (err < 0)
			return err;
		else
			pdata->buffer_enabled = true;
	}

	/* In cyclic mode, the buffer must be enabled after writing the samples.
	 * In non-cyclic mode, it must be enabled before writing the samples. */
	if (!pdata->cyclic) {
		ret = device_check_ready(dev, true);
		if (ret < 0)
			return ret;

		ret = write(fileno(f), src, len);
	}

	return ret ? ret : -EIO;
}

static ssize_t local_get_buffer(const struct iio_device *dev,
		void **addr_ptr, size_t bytes_used,
		uint32_t *mask, size_t words)
{
	struct block block;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	ssize_t ret;

	if (!pdata->is_high_speed)
		return -ENOSYS;
	if (!f)
		return -EBADF;
	if (!addr_ptr)
		return -EINVAL;

	if (pdata->last_dequeued >= 0) {
		struct block *last_block = &pdata->blocks[pdata->last_dequeued];

		if (pdata->cyclic) {
			if (pdata->cyclic_buffer_enqueued)
				return -EBUSY;
			pdata->blocks[0].flags |= BLOCK_FLAG_CYCLIC;
			pdata->cyclic_buffer_enqueued = true;
		}

		last_block->bytes_used = bytes_used;
		ret = (ssize_t) ioctl(fileno(f),
				BLOCK_ENQUEUE_IOCTL, last_block);
		if (ret) {
			ret = (ssize_t) -errno;
			ERROR("Unable to enqueue block: %s\n", strerror(errno));
			return ret;
		}

		if (pdata->cyclic) {
			*addr_ptr = pdata->addrs[pdata->last_dequeued];
			return (ssize_t) last_block->bytes_used;
		}
	}

	ret = (ssize_t) ioctl(fileno(f), BLOCK_DEQUEUE_IOCTL, &block);<commit_msg>Initialize block in local_get_buffer() to 0s before dequeuing to prevent "Syscall param ioctl(generic) points to uninitialized byte(s)" Valgrind error.<commit_after>/*
 * libiio - Library for interfacing industrial I/O (IIO) devices
 *
 * Copyright (C) 2014 Analog Devices, Inc.
 * Author: <NAME> <<EMAIL>>
 *
 * 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 "iio-private.h"

#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/utsname.h>

#define DEFAULT_TIMEOUT_MS 1000

#define NB_BLOCKS 4

#define BLOCK_ALLOC_IOCTL   _IOWR('i', 0xa0, struct block_alloc_req)
#define BLOCK_FREE_IOCTL      _IO('i', 0xa1)
#define BLOCK_QUERY_IOCTL   _IOWR('i', 0xa2, struct block)
#define BLOCK_ENQUEUE_IOCTL _IOWR('i', 0xa3, struct block)
#define BLOCK_DEQUEUE_IOCTL _IOWR('i', 0xa4, struct block)

#define BLOCK_FLAG_CYCLIC BIT(1)

/* Forward declarations */
static ssize_t local_read_dev_attr(const struct iio_device *dev,
		const char *attr, char *dst, size_t len, bool is_debug);
static ssize_t local_read_chn_attr(const struct iio_channel *chn,
		const char *attr, char *dst, size_t len);
static ssize_t local_write_dev_attr(const struct iio_device *dev,
		const char *attr, const char *src, size_t len, bool is_debug);
static ssize_t local_write_chn_attr(const struct iio_channel *chn,
		const char *attr, const char *src, size_t len);

struct block_alloc_req {
	uint32_t type,
		 size,
		 count,
		 id;
};

struct block {
	uint32_t id,
		 size,
		 bytes_used,
		 type,
		 flags,
		 offset;
	uint64_t timestamp;
};

struct iio_device_pdata {
	FILE *f;
	unsigned int samples_count, nb_blocks;

	struct block blocks[NB_BLOCKS];
	void *addrs[NB_BLOCKS];
	int last_dequeued;
	bool is_high_speed, cyclic, cyclic_buffer_enqueued, buffer_enabled;
};

static const char * const device_attrs_blacklist[] = {
	"dev",
	"uevent",
};

static const char * const modifier_names[] = {
	[IIO_MOD_X] = "x",
	[IIO_MOD_Y] = "y",
	[IIO_MOD_Z] = "z",
	[IIO_MOD_ROOT_SUM_SQUARED_X_Y] = "sqrt(x^2+y^2)",
	[IIO_MOD_SUM_SQUARED_X_Y_Z] = "x^2+y^2+z^2",
	[IIO_MOD_LIGHT_BOTH] = "both",
	[IIO_MOD_LIGHT_IR] = "ir",
	[IIO_MOD_LIGHT_CLEAR] = "clear",
	[IIO_MOD_LIGHT_RED] = "red",
	[IIO_MOD_LIGHT_GREEN] = "green",
	[IIO_MOD_LIGHT_BLUE] = "blue",
	[IIO_MOD_I] = "i",
	[IIO_MOD_Q] = "q",
};

/*
 * Looks for a IIO channel modifier at the beginning of the string s. If a
 * modifier was found the symbolic constant (IIO_MOD_*) is returned, otherwise
 * IIO_NO_MOD is returned. If a modifier was found len_p will be update with the
 * length of the modifier.
 */
static unsigned int find_modifier(const char *s, size_t *len_p)
{
	unsigned int i;
	size_t len;

	for (i = 0; i < ARRAY_SIZE(modifier_names); i++) {
		if (!modifier_names[i])
			continue;
		len = strlen(modifier_names[i]);
		if (strncmp(s, modifier_names[i], len) == 0 && s[len] == '_') {
			if (len_p)
				*len_p = len;
			return i;
		}
	}

	return IIO_NO_MOD;
}

static void local_shutdown(struct iio_context *ctx)
{
	/* Free the backend data stored in every device structure */
	unsigned int i;
	for (i = 0; i < ctx->nb_devices; i++)
		free(ctx->devices[i]->pdata);
}

/** Shrinks the first nb characters of a string
 * e.g. strcut("foobar", 4) replaces the content with "ar". */
static void strcut(char *str, int nb)
{
	char *ptr = str + nb;
	while (*ptr)
		*str++ = *ptr++;
	*str = 0;
}

static int set_channel_name(struct iio_channel *chn)
{
	size_t prefix_len = 0;
	const char *attr0;
	const char *ptr;
	unsigned int i;

	if (chn->nb_attrs < 2)
		return 0;

	attr0 = ptr = chn->attrs[0].name;

	while (true) {
		bool can_fix = true;
		size_t len;

		ptr = strchr(ptr, '_');
		if (!ptr)
			break;

		len = ptr - attr0;
		for (i = 1; can_fix && i < chn->nb_attrs; i++)
			can_fix = !strncmp(attr0, chn->attrs[i].name, len);

		if (!can_fix)
			break;

		prefix_len = len;
		ptr = ptr + 1;
	}

	if (prefix_len) {
		char *name;

		name = malloc(prefix_len + 1);
		if (!name)
			return -ENOMEM;
		strncpy(name, attr0, prefix_len);
		name[prefix_len] = '\0';
		DEBUG("Setting name of channel %s to %s\n", chn->id, name);
		chn->name = name;

		/* Shrink the attribute name */
		for (i = 0; i < chn->nb_attrs; i++)
			strcut(chn->attrs[i].name, prefix_len + 1);
	}

	return 0;
}

static int device_check_ready(const struct iio_device *dev, bool do_write)
{
	struct pollfd pollfd = {
		.fd = fileno(dev->pdata->f),
		.events = do_write ? POLLOUT : POLLIN,
	};
	int ret = poll(&pollfd, 1, dev->ctx->rw_timeout_ms);
	if (ret < 0)
		return -errno;
	if (!ret)
		return -ETIMEDOUT;
	if (pollfd.revents & POLLNVAL)
		return -EBADF;
	if (!(pollfd.revents & (do_write ? POLLOUT : POLLIN)))
		return -EIO;
	return 0;
}

static ssize_t local_read(const struct iio_device *dev,
		void *dst, size_t len, uint32_t *mask, size_t words)
{
	ssize_t ret;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	if (!f)
		return -EBADF;
	if (words != dev->words)
		return -EINVAL;

	if (!pdata->buffer_enabled) {
		ret = local_write_dev_attr(dev,
				"buffer/enable", "1", 2, false);
		if (ret < 0)
			return ret;
		else
			pdata->buffer_enabled = true;
	}

	ret = device_check_ready(dev, false);
	if (ret < 0)
		return ret;

	memcpy(mask, dev->mask, words);
	ret = fread(dst, 1, len, f);
	fflush(f);
	if (ferror(f))
		ret = -errno;
	return ret ? ret : -EIO;
}

static ssize_t local_write(const struct iio_device *dev,
		const void *src, size_t len)
{
	ssize_t ret;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	if (!f)
		return -EBADF;

	/* Writing is forbidden in cyclic mode with devices without the
	 * high-speed mmap interface, except for the devices starting with
	 * "cf-": in this case only cyclic mode is allowed. */
	if (!pdata->is_high_speed && pdata->cyclic !=
			(dev->name && !strncmp(dev->name, "cf-", 3)))
		return -EACCES;

	/* We use write() instead of fwrite() here, to avoid the internal
	 * buffering of the FILE API. This ensures that a waveform written in
	 * cyclic mode will be written in only one system call, as the kernel
	 * sizes the buffer according to the length of the first write. */
	if (pdata->cyclic) {
		ret = device_check_ready(dev, true);
		if (ret < 0)
			return ret;

		ret = write(fileno(f), src, len);
		if (ret < 0)
			return -errno;
	}

	if (!pdata->buffer_enabled) {
		ssize_t err = local_write_dev_attr(dev,
				"buffer/enable", "1", 2, false);
		if (err < 0)
			return err;
		else
			pdata->buffer_enabled = true;
	}

	/* In cyclic mode, the buffer must be enabled after writing the samples.
	 * In non-cyclic mode, it must be enabled before writing the samples. */
	if (!pdata->cyclic) {
		ret = device_check_ready(dev, true);
		if (ret < 0)
			return ret;

		ret = write(fileno(f), src, len);
	}

	return ret ? ret : -EIO;
}

static ssize_t local_get_buffer(const struct iio_device *dev,
		void **addr_ptr, size_t bytes_used,
		uint32_t *mask, size_t words)
{
	struct block block;
	struct iio_device_pdata *pdata = dev->pdata;
	FILE *f = pdata->f;
	ssize_t ret;

	if (!pdata->is_high_speed)
		return -ENOSYS;
	if (!f)
		return -EBADF;
	if (!addr_ptr)
		return -EINVAL;

	if (pdata->last_dequeued >= 0) {
		struct block *last_block = &pdata->blocks[pdata->last_dequeued];

		if (pdata->cyclic) {
			if (pdata->cyclic_buffer_enqueued)
				return -EBUSY;
			pdata->blocks[0].flags |= BLOCK_FLAG_CYCLIC;
			pdata->cyclic_buffer_enqueued = true;
		}

		last_block->bytes_used = bytes_used;
		ret = (ssize_t) ioctl(fileno(f),
				BLOCK_ENQUEUE_IOCTL, last_block);
		if (ret) {
			ret = (ssize_t) -errno;
			ERROR("Unable to enqueue block: %s\n", strerror(errno));
			return ret;
		}

		if (pdata->cyclic) {
			*addr_ptr = pdata->addrs[pdata->last_dequeued];
			return (ssize_t) last_block->bytes_used;
		}
	}

	memset(&block, 0, sizeof(block));
	ret = (ssize_t) ioctl(fileno(f), BLOCK_DEQUEUE_IOCTL, &block);
{'fraction_non_alphanumeric': 0.13465288035450518, 'fraction_numerical': 0.0089807976366322, 'mean_word_length': 3.886258660508083, 'pattern_counts': {'":': 2, '<': 63, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1227140', 'n_tokens_mistral': 7056, 'n_tokens_neox': 6558, 'n_words': 1888}