| /* |
| * libiio - Library for interfacing industrial I/O (IIO) devices |
| * |
| * Copyright (C) 2014 Analog Devices, Inc. |
| * Author: Paul Cercueil <[email protected]> |
| * |
| * 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 <errno.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| static const char * const iio_chan_type_name_spec[] = { |
| [IIO_VOLTAGE] = "voltage", |
| [IIO_CURRENT] = "current", |
| [IIO_POWER] = "power", |
| [IIO_ACCEL] = "accel", |
| [IIO_ANGL_VEL] = "anglvel", |
| [IIO_MAGN] = "magn", |
| [IIO_LIGHT] = "illuminance", |
| [IIO_INTENSITY] = "intensity", |
| [IIO_PROXIMITY] = "proximity", |
| [IIO_TEMP] = "temp", |
| [IIO_INCLI] = "incli", |
| [IIO_ROT] = "rot", |
| [IIO_ANGL] = "angl", |
| [IIO_TIMESTAMP] = "timestamp", |
| [IIO_CAPACITANCE] = "capacitance", |
| [IIO_ALTVOLTAGE] = "altvoltage", |
| [IIO_CCT] = "cct", |
| [IIO_PRESSURE] = "pressure", |
| [IIO_HUMIDITYRELATIVE] = "humidityrelative", |
| [IIO_ACTIVITY] = "activity", |
| [IIO_STEPS] = "steps", |
| [IIO_ENERGY] = "energy", |
| [IIO_DISTANCE] = "distance", |
| [IIO_VELOCITY] = "velocity", |
| [IIO_CONCENTRATION] = "concentration", |
| [IIO_RESISTANCE] = "resistance", |
| [IIO_PH] = "ph", |
| [IIO_UVINDEX] = "uvindex", |
| [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", |
| [IIO_COUNT] = "count", |
| [IIO_INDEX] = "index", |
| [IIO_GRAVITY] = "gravity", |
| }; |
| |
| static const char * const modifier_names[] = { |
| [IIO_MOD_X] = "x", |
| [IIO_MOD_Y] = "y", |
| [IIO_MOD_Z] = "z", |
| [IIO_MOD_X_AND_Y] = "x&y", |
| [IIO_MOD_X_AND_Z] = "x&z", |
| [IIO_MOD_Y_AND_Z] = "y&z", |
| [IIO_MOD_X_AND_Y_AND_Z] = "x&y&z", |
| [IIO_MOD_X_OR_Y] = "x|y", |
| [IIO_MOD_X_OR_Z] = "x|z", |
| [IIO_MOD_Y_OR_Z] = "y|z", |
| [IIO_MOD_X_OR_Y_OR_Z] = "x|y|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_LIGHT_UV] = "uv", |
| [IIO_MOD_QUATERNION] = "quaternion", |
| [IIO_MOD_TEMP_AMBIENT] = "ambient", |
| [IIO_MOD_TEMP_OBJECT] = "object", |
| [IIO_MOD_NORTH_MAGN] = "from_north_magnetic", |
| [IIO_MOD_NORTH_TRUE] = "from_north_true", |
| [IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp", |
| [IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp", |
| [IIO_MOD_RUNNING] = "running", |
| [IIO_MOD_JOGGING] = "jogging", |
| [IIO_MOD_WALKING] = "walking", |
| [IIO_MOD_STILL] = "still", |
| [IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)", |
| [IIO_MOD_I] = "i", |
| [IIO_MOD_Q] = "q", |
| [IIO_MOD_CO2] = "co2", |
| [IIO_MOD_VOC] = "voc", |
| }; |
| |
| /* |
| * 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 updated with |
| * the length of the modifier. |
| */ |
| unsigned int find_channel_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] == '\0' || s[len] == '_')) { |
| if (len_p) |
| *len_p = len; |
| return i; |
| } |
| } |
| |
| return IIO_NO_MOD; |
| } |
| |
| /* |
| * Initializes all auto-detected fields of the channel struct. Must be called |
| * after the channel has been otherwise fully initialized. |
| */ |
| void iio_channel_init_finalize(struct iio_channel *chn) |
| { |
| unsigned int i; |
| size_t len; |
| char *mod; |
| |
| chn->type = IIO_CHAN_TYPE_UNKNOWN; |
| chn->modifier = IIO_NO_MOD; |
| |
| for (i = 0; i < ARRAY_SIZE(iio_chan_type_name_spec); i++) { |
| len = strlen(iio_chan_type_name_spec[i]); |
| if (strncmp(iio_chan_type_name_spec[i], chn->id, len) != 0) |
| continue; |
| /* Type must be followed by either a '_' or a digit */ |
| if (chn->id[len] != '_' && (chn->id[len] < '0' || chn->id[len] > '9')) |
| continue; |
| |
| chn->type = (enum iio_chan_type) i; |
| } |
| |
| mod = strchr(chn->id, '_'); |
| if (!mod) |
| return; |
| |
| mod++; |
| |
| for (i = 0; i < ARRAY_SIZE(modifier_names); i++) { |
| if (!modifier_names[i]) |
| continue; |
| len = strlen(modifier_names[i]); |
| if (strncmp(modifier_names[i], mod, len) != 0) |
| continue; |
| |
| chn->modifier = (enum iio_modifier) i; |
| break; |
| } |
| } |
| |
| static char *get_attr_xml(struct iio_channel_attr *attr, size_t *length) |
| { |
| char *str; |
| size_t len = strlen(attr->name) + sizeof("<attribute name=\"\" />"); |
| if (attr->filename) |
| len += strlen(attr->filename) + sizeof("filename=\"\""); |
| |
| str = malloc(len); |
| if (!str) |
| return NULL; |
| |
| *length = len - 1; /* Skip the \0 */ |
| if (attr->filename) |
| iio_snprintf(str, len, "<attribute name=\"%s\" filename=\"%s\" />", |
| attr->name, attr->filename); |
| else |
| iio_snprintf(str, len, "<attribute name=\"%s\" />", attr->name); |
| return str; |
| } |
| |
| static char * get_scan_element(const struct iio_channel *chn, size_t *length) |
| { |
| char buf[1024], repeat[12] = "", *str; |
| char processed = (chn->format.is_fully_defined ? 'A' - 'a' : 0); |
| |
| if (chn->format.repeat > 1) |
| iio_snprintf(repeat, sizeof(repeat), "X%u", chn->format.repeat); |
| |
| iio_snprintf(buf, sizeof(buf), "<scan-element index=\"%li\" " |
| "format=\"%ce:%c%u/%u%s>>%u\" />", |
| chn->index, chn->format.is_be ? 'b' : 'l', |
| chn->format.is_signed ? 's' + processed : 'u' + processed, |
| chn->format.bits, chn->format.length, repeat, |
| chn->format.shift); |
| |
| if (chn->format.with_scale) { |
| char *ptr = strrchr(buf, '\0'); |
| iio_snprintf(ptr - 2, buf + sizeof(buf) - ptr + 2, |
| "scale=\"%f\" />", chn->format.scale); |
| } |
| |
| str = iio_strdup(buf); |
| if (str) |
| *length = strlen(str); |
| return str; |
| } |
| |
| /* Returns a string containing the XML representation of this channel */ |
| char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length) |
| { |
| size_t len = sizeof("<channel id=\"\" name=\"\" " |
| "type=\"output\" ></channel>") |
| + strlen(chn->id) + (chn->name ? strlen(chn->name) : 0); |
| char *ptr, *str, **attrs, *scan_element = NULL; |
| size_t *attrs_len, scan_element_len = 0; |
| unsigned int i; |
| |
| if (chn->is_scan_element) { |
| scan_element = get_scan_element(chn, &scan_element_len); |
| if (!scan_element) |
| return NULL; |
| else |
| len += scan_element_len; |
| } |
| |
| attrs_len = malloc(chn->nb_attrs * sizeof(*attrs_len)); |
| if (!attrs_len) |
| goto err_free_scan_element; |
| |
| attrs = malloc(chn->nb_attrs * sizeof(*attrs)); |
| if (!attrs) |
| goto err_free_attrs_len; |
| |
| for (i = 0; i < chn->nb_attrs; i++) { |
| char *xml = get_attr_xml(&chn->attrs[i], &attrs_len[i]); |
| if (!xml) |
| goto err_free_attrs; |
| attrs[i] = xml; |
| len += attrs_len[i]; |
| } |
| |
| str = malloc(len); |
| if (!str) |
| goto err_free_attrs; |
| |
| iio_snprintf(str, len, "<channel id=\"%s\"", chn->id); |
| ptr = strrchr(str, '\0'); |
| |
| if (chn->name) { |
| sprintf(ptr, " name=\"%s\"", chn->name); |
| ptr = strrchr(ptr, '\0'); |
| } |
| |
| sprintf(ptr, " type=\"%s\" >", chn->is_output ? "output" : "input"); |
| ptr = strrchr(ptr, '\0'); |
| |
| if (chn->is_scan_element) { |
| strcpy(ptr, scan_element); |
| ptr += scan_element_len; |
| } |
| |
| for (i = 0; i < chn->nb_attrs; i++) { |
| strcpy(ptr, attrs[i]); |
| ptr += attrs_len[i]; |
| free(attrs[i]); |
| } |
| |
| free(scan_element); |
| free(attrs); |
| free(attrs_len); |
| |
| strcpy(ptr, "</channel>"); |
| *length = ptr - str + sizeof("</channel>") - 1; |
| return str; |
| |
| err_free_attrs: |
| while (i--) |
| free(attrs[i]); |
| free(attrs); |
| err_free_attrs_len: |
| free(attrs_len); |
| err_free_scan_element: |
| if (chn->is_scan_element) |
| free(scan_element); |
| return NULL; |
| } |
| |
| const char * iio_channel_get_id(const struct iio_channel *chn) |
| { |
| return chn->id; |
| } |
| |
| const char * iio_channel_get_name(const struct iio_channel *chn) |
| { |
| return chn->name; |
| } |
| |
| bool iio_channel_is_output(const struct iio_channel *chn) |
| { |
| return chn->is_output; |
| } |
| |
| bool iio_channel_is_scan_element(const struct iio_channel *chn) |
| { |
| return chn->is_scan_element; |
| } |
| |
| enum iio_modifier iio_channel_get_modifier(const struct iio_channel *chn) |
| { |
| return chn->modifier; |
| } |
| |
| enum iio_chan_type iio_channel_get_type(const struct iio_channel *chn) |
| { |
| return chn->type; |
| } |
| |
| unsigned int iio_channel_get_attrs_count(const struct iio_channel *chn) |
| { |
| return chn->nb_attrs; |
| } |
| |
| const char * iio_channel_get_attr(const struct iio_channel *chn, |
| unsigned int index) |
| { |
| if (index >= chn->nb_attrs) |
| return NULL; |
| else |
| return chn->attrs[index].name; |
| } |
| |
| const char * iio_channel_find_attr(const struct iio_channel *chn, |
| const char *name) |
| { |
| unsigned int i; |
| for (i = 0; i < chn->nb_attrs; i++) { |
| const char *attr = chn->attrs[i].name; |
| if (!strcmp(attr, name)) |
| return attr; |
| } |
| return NULL; |
| } |
| |
| ssize_t iio_channel_attr_read(const struct iio_channel *chn, |
| const char *attr, char *dst, size_t len) |
| { |
| if (chn->dev->ctx->ops->read_channel_attr) |
| return chn->dev->ctx->ops->read_channel_attr(chn, |
| attr, dst, len); |
| else |
| return -ENOSYS; |
| } |
| |
| ssize_t iio_channel_attr_write_raw(const struct iio_channel *chn, |
| const char *attr, const void *src, size_t len) |
| { |
| if (chn->dev->ctx->ops->write_channel_attr) |
| return chn->dev->ctx->ops->write_channel_attr(chn, |
| attr, src, len); |
| else |
| return -ENOSYS; |
| } |
| |
| ssize_t iio_channel_attr_write(const struct iio_channel *chn, |
| const char *attr, const char *src) |
| { |
| return iio_channel_attr_write_raw(chn, attr, src, strlen(src) + 1); |
| } |
| |
| void iio_channel_set_data(struct iio_channel *chn, void *data) |
| { |
| chn->userdata = data; |
| } |
| |
| void * iio_channel_get_data(const struct iio_channel *chn) |
| { |
| return chn->userdata; |
| } |
| |
| long iio_channel_get_index(const struct iio_channel *chn) |
| { |
| return chn->index; |
| } |
| |
| const struct iio_data_format * iio_channel_get_data_format( |
| const struct iio_channel *chn) |
| { |
| return &chn->format; |
| } |
| |
| bool iio_channel_is_enabled(const struct iio_channel *chn) |
| { |
| return chn->index >= 0 && chn->dev->mask && |
| TEST_BIT(chn->dev->mask, chn->number); |
| } |
| |
| void iio_channel_enable(struct iio_channel *chn) |
| { |
| if (chn->is_scan_element && chn->index >= 0 && chn->dev->mask) |
| SET_BIT(chn->dev->mask, chn->number); |
| } |
| |
| void iio_channel_disable(struct iio_channel *chn) |
| { |
| if (chn->index >= 0 && chn->dev->mask) |
| CLEAR_BIT(chn->dev->mask, chn->number); |
| } |
| |
| void free_channel(struct iio_channel *chn) |
| { |
| size_t i; |
| for (i = 0; i < chn->nb_attrs; i++) { |
| free(chn->attrs[i].name); |
| free(chn->attrs[i].filename); |
| } |
| if (chn->nb_attrs) |
| free(chn->attrs); |
| if (chn->name) |
| free(chn->name); |
| if (chn->id) |
| free(chn->id); |
| free(chn); |
| } |
| |
| static void byte_swap(uint8_t *dst, const uint8_t *src, size_t len) |
| { |
| size_t i; |
| for (i = 0; i < len; i++) |
| dst[i] = src[len - i - 1]; |
| } |
| |
| static void shift_bits(uint8_t *dst, size_t shift, size_t len, bool left) |
| { |
| size_t i, shift_bytes = shift / 8; |
| shift %= 8; |
| |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| if (!left) |
| #else |
| if (left) |
| #endif |
| { |
| if (shift_bytes) { |
| memmove(dst, dst + shift_bytes, len - shift_bytes); |
| memset(dst + len - shift_bytes, 0, shift_bytes); |
| } |
| if (shift) { |
| for (i = 0; i < len; i++) { |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| dst[i] >>= shift; |
| if (i < len - 1) |
| dst[i] |= dst[i + 1] << (8 - shift); |
| #else |
| dst[i] <<= shift; |
| if (i < len - 1) |
| dst[i] |= dst[i + 1] >> (8 - shift); |
| #endif |
| } |
| } |
| } else { |
| if (shift_bytes) { |
| memmove(dst + shift_bytes, dst, len - shift_bytes); |
| memset(dst, 0, shift_bytes); |
| } |
| if (shift) { |
| for (i = len; i > 0; i--) { |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| dst[i - 1] <<= shift; |
| if (i > 1) |
| dst[i - 1] |= dst[i - 2] >> (8 - shift); |
| #else |
| dst[i - 1] >>= shift; |
| if (i > 1) |
| dst[i - 1] |= dst[i - 2] << (8 - shift); |
| #endif |
| } |
| } |
| } |
| } |
| |
| static void sign_extend(uint8_t *dst, size_t bits, size_t len) |
| { |
| size_t upper_bytes = ((len * 8 - bits) / 8); |
| uint8_t msb, msb_bit = 1 << ((bits - 1) % 8); |
| |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| msb = dst[len - 1 - upper_bytes] & msb_bit; |
| if (upper_bytes) |
| memset(dst + len - upper_bytes, msb ? 0xff : 0x00, upper_bytes); |
| if (msb) |
| dst[len - 1 - upper_bytes] |= ~(msb_bit - 1); |
| else |
| dst[len - 1 - upper_bytes] &= (msb_bit - 1); |
| #else |
| /* XXX: untested */ |
| msb = dst[upper_bytes] & msb_bit; |
| if (upper_bytes) |
| memset(dst, msb ? 0xff : 0x00, upper_bytes); |
| if (msb) |
| dst[upper_bytes] |= ~(msb_bit - 1); |
| #endif |
| } |
| |
| static void mask_upper_bits(uint8_t *dst, size_t bits, size_t len) |
| { |
| size_t i; |
| |
| /* Clear upper bits */ |
| if (bits % 8) |
| dst[bits / 8] &= (1 << (bits % 8)) - 1; |
| |
| /* Clear upper bytes */ |
| for (i = (bits + 7) / 8; i < len; i++) |
| dst[i] = 0; |
| } |
| |
| |
| void iio_channel_convert(const struct iio_channel *chn, |
| void *dst, const void *src) |
| { |
| uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst; |
| unsigned int len = chn->format.length / 8; |
| ptrdiff_t end = len * chn->format.repeat; |
| uintptr_t end_ptr = src_ptr + end; |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| bool swap = chn->format.is_be; |
| #else |
| bool swap = !chn->format.is_be; |
| #endif |
| |
| for (src_ptr = (uintptr_t) src; src_ptr < end_ptr; |
| src_ptr += len, dst_ptr += len) { |
| if (len == 1 || !swap) |
| memcpy((void *) dst_ptr, (const void *) src_ptr, len); |
| else |
| byte_swap((void *) dst_ptr, (const void *) src_ptr, |
| len); |
| |
| if (chn->format.shift) |
| shift_bits((void *) dst_ptr, chn->format.shift, len, |
| false); |
| |
| if (!chn->format.is_fully_defined) { |
| if (chn->format.is_signed) |
| sign_extend((void *) dst_ptr, |
| chn->format.bits, len); |
| else |
| mask_upper_bits((void *) dst_ptr, |
| chn->format.bits, len); |
| } |
| } |
| } |
| |
| void iio_channel_convert_inverse(const struct iio_channel *chn, |
| void *dst, const void *src) |
| { |
| uintptr_t src_ptr = (uintptr_t) src, dst_ptr = (uintptr_t) dst; |
| unsigned int len = chn->format.length / 8; |
| ptrdiff_t end = len * chn->format.repeat; |
| uintptr_t end_ptr = dst_ptr + end; |
| #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| bool swap = chn->format.is_be; |
| #else |
| bool swap = !chn->format.is_be; |
| #endif |
| uint8_t buf[1024]; |
| |
| /* Somehow I doubt we will have samples of 8192 bits each. */ |
| if (len > sizeof(buf)) |
| return; |
| |
| for (dst_ptr = (uintptr_t) dst; dst_ptr < end_ptr; |
| src_ptr += len, dst_ptr += len) { |
| memcpy(buf, (const void *) src_ptr, len); |
| mask_upper_bits(buf, chn->format.bits, len); |
| |
| if (chn->format.shift) |
| shift_bits(buf, chn->format.shift, len, true); |
| |
| if (len == 1 || !swap) |
| memcpy((void *) dst_ptr, buf, len); |
| else |
| byte_swap((void *) dst_ptr, buf, len); |
| } |
| } |
| |
| size_t iio_channel_read_raw(const struct iio_channel *chn, |
| struct iio_buffer *buf, void *dst, size_t len) |
| { |
| uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len; |
| unsigned int length = chn->format.length / 8 * chn->format.repeat; |
| uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); |
| ptrdiff_t buf_step = iio_buffer_step(buf); |
| |
| for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn); |
| src_ptr < buf_end && dst_ptr + length <= end; |
| src_ptr += buf_step, dst_ptr += length) |
| memcpy((void *) dst_ptr, (const void *) src_ptr, length); |
| return dst_ptr - (uintptr_t) dst; |
| } |
| |
| size_t iio_channel_read(const struct iio_channel *chn, |
| struct iio_buffer *buf, void *dst, size_t len) |
| { |
| uintptr_t src_ptr, dst_ptr = (uintptr_t) dst, end = dst_ptr + len; |
| unsigned int length = chn->format.length / 8 * chn->format.repeat; |
| uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); |
| ptrdiff_t buf_step = iio_buffer_step(buf); |
| |
| for (src_ptr = (uintptr_t) iio_buffer_first(buf, chn); |
| src_ptr < buf_end && dst_ptr + length <= end; |
| src_ptr += buf_step, dst_ptr += length) |
| iio_channel_convert(chn, |
| (void *) dst_ptr, (const void *) src_ptr); |
| return dst_ptr - (uintptr_t) dst; |
| } |
| |
| size_t iio_channel_write_raw(const struct iio_channel *chn, |
| struct iio_buffer *buf, const void *src, size_t len) |
| { |
| uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len; |
| unsigned int length = chn->format.length / 8 * chn->format.repeat; |
| uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); |
| ptrdiff_t buf_step = iio_buffer_step(buf); |
| |
| for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn); |
| dst_ptr < buf_end && src_ptr + length <= end; |
| dst_ptr += buf_step, src_ptr += length) |
| memcpy((void *) dst_ptr, (const void *) src_ptr, length); |
| return src_ptr - (uintptr_t) src; |
| } |
| |
| size_t iio_channel_write(const struct iio_channel *chn, |
| struct iio_buffer *buf, const void *src, size_t len) |
| { |
| uintptr_t dst_ptr, src_ptr = (uintptr_t) src, end = src_ptr + len; |
| unsigned int length = chn->format.length / 8 * chn->format.repeat; |
| uintptr_t buf_end = (uintptr_t) iio_buffer_end(buf); |
| ptrdiff_t buf_step = iio_buffer_step(buf); |
| |
| for (dst_ptr = (uintptr_t) iio_buffer_first(buf, chn); |
| dst_ptr < buf_end && src_ptr + length <= end; |
| dst_ptr += buf_step, src_ptr += length) |
| iio_channel_convert_inverse(chn, |
| (void *) dst_ptr, (const void *) src_ptr); |
| return src_ptr - (uintptr_t) src; |
| } |
| |
| int iio_channel_attr_read_longlong(const struct iio_channel *chn, |
| const char *attr, long long *val) |
| { |
| char *end, buf[1024]; |
| long long value; |
| ssize_t ret = iio_channel_attr_read(chn, attr, buf, sizeof(buf)); |
| if (ret < 0) |
| return (int) ret; |
| |
| value = strtoll(buf, &end, 0); |
| if (end == buf) |
| return -EINVAL; |
| *val = value; |
| return 0; |
| } |
| |
| int iio_channel_attr_read_bool(const struct iio_channel *chn, |
| const char *attr, bool *val) |
| { |
| long long value; |
| int ret = iio_channel_attr_read_longlong(chn, attr, &value); |
| if (ret < 0) |
| return ret; |
| |
| *val = !!value; |
| return 0; |
| } |
| |
| int iio_channel_attr_read_double(const struct iio_channel *chn, |
| const char *attr, double *val) |
| { |
| char buf[1024]; |
| ssize_t ret = iio_channel_attr_read(chn, attr, buf, sizeof(buf)); |
| if (ret < 0) |
| return (int) ret; |
| else |
| return read_double(buf, val); |
| } |
| |
| int iio_channel_attr_write_longlong(const struct iio_channel *chn, |
| const char *attr, long long val) |
| { |
| ssize_t ret; |
| char buf[1024]; |
| iio_snprintf(buf, sizeof(buf), "%lld", val); |
| ret = iio_channel_attr_write(chn, attr, buf); |
| return ret < 0 ? ret : 0; |
| } |
| |
| int iio_channel_attr_write_double(const struct iio_channel *chn, |
| const char *attr, double val) |
| { |
| ssize_t ret; |
| char buf[1024]; |
| |
| ret = (ssize_t) write_double(buf, sizeof(buf), val); |
| if (!ret) |
| ret = iio_channel_attr_write(chn, attr, buf); |
| return ret < 0 ? ret : 0; |
| } |
| |
| int iio_channel_attr_write_bool(const struct iio_channel *chn, |
| const char *attr, bool val) |
| { |
| ssize_t ret; |
| if (val) |
| ret = iio_channel_attr_write_raw(chn, attr, "1", 2); |
| else |
| ret = iio_channel_attr_write_raw(chn, attr, "0", 2); |
| return ret < 0 ? ret : 0; |
| } |
| |
| const char * iio_channel_attr_get_filename( |
| const struct iio_channel *chn, const char *attr) |
| { |
| unsigned int i; |
| for (i = 0; i < chn->nb_attrs; i++) { |
| if (!strcmp(chn->attrs[i].name, attr)) |
| return chn->attrs[i].filename; |
| } |
| return NULL; |
| } |
| |
| int iio_channel_attr_read_all(struct iio_channel *chn, |
| int (*cb)(struct iio_channel *chn, |
| const char *attr, const char *val, size_t len, void *d), |
| void *data) |
| { |
| int ret, buf_size; |
| char *buf, *ptr; |
| unsigned int i; |
| |
| /* We need a big buffer here; 1 MiB should be enough */ |
| buf = malloc(0x100000); |
| if (!buf) |
| return -ENOMEM; |
| |
| ret = (int) iio_channel_attr_read(chn, NULL, buf, 0x100000); |
| if (ret < 0) |
| goto err_free_buf; |
| |
| ptr = buf; |
| buf_size = ret; |
| |
| for (i = 0; i < iio_channel_get_attrs_count(chn); i++) { |
| const char *attr = iio_channel_get_attr(chn, i); |
| int32_t len; |
| |
| if (buf_size < 4) { |
| ret = -EPROTO; |
| break; |
| } |
| |
| len = (int32_t) iio_be32toh(*(uint32_t *) ptr); |
| ptr += 4; |
| buf_size -= 4; |
| |
| if (len > 0 && buf_size < len) { |
| ret = -EPROTO; |
| break; |
| } |
| |
| if (len > 0) { |
| ret = cb(chn, attr, ptr, (size_t) len, data); |
| if (ret < 0) |
| goto err_free_buf; |
| |
| if (len & 0x3) |
| len = ((len >> 2) + 1) << 2; |
| ptr += len; |
| if (len >= buf_size) |
| buf_size = 0; |
| else |
| buf_size -= len; |
| } |
| } |
| |
| err_free_buf: |
| free(buf); |
| return ret < 0 ? ret : 0; |
| } |
| |
| int iio_channel_attr_write_all(struct iio_channel *chn, |
| ssize_t (*cb)(struct iio_channel *chn, |
| const char *attr, void *buf, size_t len, void *d), |
| void *data) |
| { |
| char *buf, *ptr; |
| unsigned int i; |
| size_t len = 0x100000; |
| int ret; |
| |
| /* We need a big buffer here; 1 MiB should be enough */ |
| buf = malloc(len); |
| if (!buf) |
| return -ENOMEM; |
| |
| ptr = buf; |
| |
| for (i = 0; i < iio_channel_get_attrs_count(chn); i++) { |
| const char *attr = iio_channel_get_attr(chn, i); |
| |
| ret = (int) cb(chn, attr, ptr + 4, len - 4, data); |
| if (ret < 0) |
| goto err_free_buf; |
| |
| *(int32_t *) ptr = (int32_t) iio_htobe32((uint32_t) ret); |
| ptr += 4; |
| len -= 4; |
| |
| if (ret > 0) { |
| if (ret & 0x3) |
| ret = ((ret >> 2) + 1) << 2; |
| ptr += ret; |
| len -= ret; |
| } |
| } |
| |
| ret = (int) iio_channel_attr_write_raw(chn, NULL, buf, ptr - buf); |
| |
| err_free_buf: |
| free(buf); |
| return ret < 0 ? ret : 0; |
| } |
| |
| const struct iio_device * iio_channel_get_device(const struct iio_channel *chn) |
| { |
| return chn->dev; |
| } |