diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index f0d1072e..076ae039 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -65,6 +65,10 @@ if(ENABLE_DOC) endif() endif() +if(LIBIIO_V1) + set(CMAKE_SWIG_FLAGS -DLIBIIO_V1) +endif() + SET_SOURCE_FILES_PROPERTIES(../${PROJECT_NAME}.i PROPERTIES CPLUSPLUS ON) set(CMAKE_SWIG_OUTDIR ${CMAKE_BINARY_DIR}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fbf62cc..1064e4cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,12 +29,12 @@ endif() SET(CMAKE_EXE_LINKER_FLAGS "/FORCE") -FILE(GLOB_RECURSE SRC_LIST *.cpp *_impl.hpp */*_impl.hpp) +FILE(GLOB_RECURSE SRC_LIST *.hpp *.cpp *_impl.hpp */*_impl.hpp) IF(DEFINED LIBIIO_V1 AND NOT DEFINED CACHE{LIBIIO_V1}) # TBD separate a diff if to check if cached and display an error msg - LIST(FILTER SRC_LIST EXCLUDE REGEX ".*_v0.cpp") + LIST(FILTER SRC_LIST EXCLUDE REGEX ".*_v0.cpp|.*_v0.hpp") ELSE() - LIST(FILTER SRC_LIST EXCLUDE REGEX ".*_v1.cpp") + LIST(FILTER SRC_LIST EXCLUDE REGEX ".*_v1.cpp|.*_v1.hpp") ENDIF() FILE(GLOB_RECURSE HEADERS_LIST ${CMAKE_SOURCE_DIR}/include/libm2k/*.hpp) diff --git a/src/analog/m2kanalogin_impl.cpp b/src/analog/m2kanalogin_impl.cpp index e9bcc48c..6e624caf 100644 --- a/src/analog/m2kanalogin_impl.cpp +++ b/src/analog/m2kanalogin_impl.cpp @@ -76,8 +76,10 @@ M2kAnalogInImpl::M2kAnalogInImpl(iio_context * ctx, std::string adc_dev, bool sy m_trigger->setCalibParameters(i, getScalingFactor(i), m_adc_hw_vert_offset.at(i)); } +#ifndef LIBIIO_V1 // data_available attribute exists only in firmware versions newer than 0.23 m_data_available = m_m2k_adc->hasBufferAttribute("data_available"); +#endif if (sync) { syncDevice(); @@ -156,6 +158,9 @@ void M2kAnalogInImpl::syncDevice() void M2kAnalogInImpl::loadNbKernelBuffers() { +#ifdef LIBIIO_V1 + m_nb_kernel_buffers = 4; +#else if (m_data_available) { const unsigned int buffersize = 16; handleChannelsEnableState(true); @@ -168,6 +173,7 @@ void M2kAnalogInImpl::loadNbKernelBuffers() } else { m_nb_kernel_buffers = 4; } +#endif } void M2kAnalogInImpl::setAdcCalibGain(ANALOG_IN_CHANNEL channel, double gain) @@ -962,4 +968,4 @@ void M2kAnalogInImpl::setCalibrateHDL(std::string calibrate_val) std::string M2kAnalogInImpl::getCalibrateHDL() { return m_m2k_adc->getStringValue("calibrate"); -} \ No newline at end of file +} diff --git a/src/analog/m2kanalogout_impl.cpp b/src/analog/m2kanalogout_impl.cpp index 0c485697..2bb2769d 100644 --- a/src/analog/m2kanalogout_impl.cpp +++ b/src/analog/m2kanalogout_impl.cpp @@ -76,9 +76,10 @@ M2kAnalogOutImpl::M2kAnalogOutImpl(iio_context *ctx, std::vector da // dma_start_sync attribute is only available in firmware versions newer than 0.24 m_dma_start_sync_available = getDacDevice(0)->hasGlobalAttribute("dma_sync_start"); +#ifndef LIBIIO_V1 // data_available attribute exists only in firmware versions newer than 0.23 m_dma_data_available = getDacDevice(0)->hasBufferAttribute("data_available"); - +#endif m_raw_enable_available.push_back(getDacDevice(0)->getChannel(0, true)->hasAttribute("raw_enable")); m_raw_enable_available.push_back(getDacDevice(1)->getChannel(0, true)->hasAttribute("raw_enable")); m_raw_available.push_back(getDacDevice(0)->getChannel(0, true)->hasAttribute("raw")); @@ -124,6 +125,11 @@ void M2kAnalogOutImpl::syncDevice() void M2kAnalogOutImpl::loadNbKernelBuffers() { +#ifdef LIBIIO_V1 + for (unsigned int chn = 0; chn < m_dac_devices.size(); chn++) { + m_nb_kernel_buffers[chn] = 4; + } +#else if (m_dma_data_available) { const unsigned int buffersize = 16; for (unsigned int chn = 0; chn < m_dac_devices.size(); chn++) { @@ -141,6 +147,7 @@ void M2kAnalogOutImpl::loadNbKernelBuffers() m_nb_kernel_buffers[chn] = 4; } } +#endif } unsigned short M2kAnalogOutImpl::setVoltage(unsigned int chn_idx, double volts) diff --git a/src/context_impl_v1.cpp b/src/context_impl_v1.cpp new file mode 100644 index 00000000..2cb9a51e --- /dev/null +++ b/src/context_impl_v1.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "context_impl.hpp" +#include "analog/dmm_impl.hpp" +#include "utils/channel.hpp" +#include +#include +#include +#include +#include + +#ifndef LIBIIO_V1 +#include +#else +#include +#endif + +using namespace libm2k::analog; +using namespace libm2k::digital; +using namespace libm2k::context; +using namespace libm2k::utils; + +ContextImpl::ContextImpl(std::string uri, struct iio_context *ctx, std::string name, bool sync) +{ + UNUSED(name); + m_context = ctx; + m_uri = uri; + m_sync = sync; + m_ownsContext = false; + + /* Initialize the DMM list */ + scanAllDMM(); + + initializeContextAttributes(); +} + +ContextImpl::~ContextImpl() +{ + for (auto d : m_instancesDMM) { + delete d; + } + m_instancesDMM.clear(); + + if (m_context && m_ownsContext) { + iio_context_destroy(m_context); + } +} + +void ContextImpl::reset() +{ +} + +void ContextImpl::deinitialize() +{ +} + + +bool ContextImpl::iioDevHasAttribute(iio_device* dev, std::string const& attr) +{ + unsigned int nb_attr = iio_device_get_attrs_count(dev); + for (unsigned int i = 0; i < nb_attr; i++) { + const struct iio_attr *attr_i = iio_device_get_attr(dev, i); + if (attr_i == NULL) { + continue; + } + const char *attr_name = iio_attr_get_name(attr_i); + std::size_t found = std::string(attr_name).find(attr); + if (found != std::string::npos) { + return true; + } + } + return false; +} + +bool ContextImpl::iioDevBufferHasAttribute(iio_device *dev, const std::string &attr) +{ + // TBD buffers need to be created in order to check their attributes +// const char *attribute = iio_device_find_buffer_attr(dev, attr.c_str()); +// return attribute != nullptr; + return true; +} + +bool ContextImpl::iioChannelHasAttribute(iio_channel* chn, std::string const& attr) +{ + unsigned int nb_attr = iio_channel_get_attrs_count(chn); + for (unsigned int i = 0; i < nb_attr; i++) { + const struct iio_attr *attr_i = iio_channel_get_attr(chn, i); + if (attr_i == NULL) { + continue; + } + const char *attr_name = iio_attr_get_name(attr_i); + std::size_t found = std::string(attr_name).find(attr); + if (found != std::string::npos) { + return true; + } + } + return false; +} + +DEVICE_DIRECTION ContextImpl::getIioDeviceDirection(std::string dev_name) +{ + DEVICE_DIRECTION dir = NO_DIRECTION; + auto dev = iio_context_find_device(m_context, dev_name.c_str()); + if (!dev) { + THROW_M2K_EXCEPTION("No device found with name: " + dev_name, libm2k::EXC_INVALID_PARAMETER); + } + + auto chn_count = iio_device_get_channels_count(dev); + for (unsigned int i = 0; i < chn_count; i++) { + auto chn = iio_device_get_channel(dev, i); + if (iio_channel_is_output(chn)) { + if (dir == INPUT) { + dir = BOTH; + } else if (dir != BOTH){ + dir = OUTPUT; + } + } else { + if (dir == OUTPUT) { + dir = BOTH; + } else if (dir != BOTH){ + dir = INPUT; + } + } + } + return dir; +} + + +DEVICE_TYPE ContextImpl::getIioDeviceType(std::string dev_name) +{ + auto dev = iio_context_find_device(m_context, dev_name.c_str()); + if (!dev) { + THROW_M2K_EXCEPTION("No device found with name: " + dev_name, libm2k::EXC_INVALID_PARAMETER); + } + + auto chn = iio_device_get_channel(dev, 0); + if (!chn) { + return NO_DEV; + } + + const struct iio_data_format* data_format = iio_channel_get_data_format(chn); + if (data_format->bits == 1) { + return DIGITAL_DEV; + } else { + return ANALOG_DEV; + } +} + +std::vector> ContextImpl::getIioDevByChannelAttrs(std::vector attr_list) +{ + iio_device* dev = nullptr; + iio_channel* chn = nullptr; + std::vector> dev_chn_list; + unsigned int nb_chn = 0; + unsigned int nb_dev = iio_context_get_devices_count(m_context); + + for (unsigned int i_dev = 0; i_dev < nb_dev; i_dev++) { + bool dev_match = true; + dev = iio_context_get_device(m_context, i_dev); + nb_chn = iio_device_get_channels_count(dev); + + for (unsigned int i_chn = 0; i_chn < nb_chn; i_chn++) { + bool chn_match = true; + chn = iio_device_get_channel(dev, i_chn); + + /* Check if the current channel has all the required attributes */ + for (unsigned int i_attr = 0; i_attr < attr_list.size(); i_attr++) { + if (!iioChannelHasAttribute(chn, attr_list.at(i_attr))) { + chn_match = false; + break; + } + } + + if (chn_match) { + auto d_name = iio_device_get_name(dev); + auto c_name = iio_channel_get_id(chn); + if (c_name && d_name) { + dev_chn_list.push_back(make_pair(std::string(d_name), + std::string(c_name))); + } + } + } + + /* Check if the device has all the required attributes as global attributes */ + for(unsigned int i_attr = 0; i_attr < attr_list.size(); i_attr++) { + if (!iioDevHasAttribute(dev, attr_list.at(i_attr))) { + dev_match = false; + break; + } + } + + if (dev_match) { + dev_chn_list.push_back(make_pair(std::string(iio_device_get_name(dev)), "")); + } + } + return dev_chn_list; +} + +std::vector> ContextImpl::getHwmonDevices(){ + iio_device* dev = nullptr; + iio_channel* chn = nullptr; + std::vector> dev_chn_list; + unsigned int nb_chn = 0; + unsigned int nb_dev = iio_context_get_devices_count(m_context); + + for (unsigned int i_dev = 0; i_dev < nb_dev; i_dev++) { + dev = iio_context_get_device(m_context, i_dev); + nb_chn = iio_device_get_channels_count(dev); + for (unsigned int i_chn = 0; i_chn < nb_chn; i_chn++) { + chn = iio_device_get_channel(dev, i_chn); + + /* Check if the current device has any valid channels*/ + if (iio_device_is_hwmon(dev) && iioChannelHasAttribute(chn, "input")){ + auto d_name = iio_device_get_name(dev); + auto c_name = std::string(iio_channel_get_id(chn)); + if (!c_name.empty() && d_name) { + dev_chn_list.push_back(make_pair(std::string(d_name),c_name)); + } + } + } + } + return dev_chn_list; +} + +bool ContextImpl::isIioDeviceBufferCapable(std::string dev_name) +{ + struct iio_device *dev = iio_context_find_device(m_context, dev_name.c_str()); + if (dev == NULL) { + return false; + } + unsigned int chn_nb = iio_device_get_channels_count(dev); + for (unsigned int i = 0; i < chn_nb; i++) { + struct iio_channel *chn = iio_device_get_channel(dev, i); + if (iio_channel_is_scan_element(chn)) { + return true; + } + } + return false; +} + +std::unordered_set ContextImpl::getAllDevices() const +{ + return Utils::getAllDevices(m_context); +} + +void ContextImpl::scanAllDMM() +{ + auto dev_list = getIioDevByChannelAttrs({"raw", "scale"}); + // check if there are any dmm hwmon devices + auto dev_list_by_ch = getHwmonDevices(); + dev_list.insert(dev_list.end(), dev_list_by_ch.begin(), dev_list_by_ch.end()); + + for (auto dev : dev_list) { + if (getIioDeviceDirection(dev.first) != OUTPUT) { + if (!getDMM(dev.first)) { + m_instancesDMM.push_back(new DMMImpl(m_context, dev.first, m_sync)); + } + } + } +} + +void ContextImpl::logAllAttributes() const +{ + // TBD here as well +#ifdef LIBM2K_ENABLE_LOG + LIBM2K_LOG(INFO, "[BEGIN] LOG ALL"); + const char *name, *value; + unsigned int attr_no = iio_context_get_attrs_count(m_context); + for (unsigned int i = 0; i < attr_no; i++) { + std::pair pair; + const struct iio_attr *attr = iio_context_get_attr(m_context, i); + if (!attr) { + THROW_M2K_EXCEPTION("Device: Can't get context attribute " + std::to_string(i), + libm2k::EXC_RUNTIME_ERROR); + } + name = iio_attr_get_name(attr); + value = iio_attr_get_static_value(attr); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({"context", name, LIBM2K_ATTRIBUTE_READ}, value)); + } + + auto devices = getAllDevices(); + for (auto &dev_name : devices) { + DeviceGeneric dev(m_context, dev_name); + auto nb_out_channels = dev.getNbChannels(true); + auto nb_in_channels = dev.getNbChannels(false); + for (unsigned int i = 0; i < nb_out_channels; i++) { + auto channel = dev.getChannel(i, true); + for (unsigned int j = 0; j < channel->getNbAttributes(); j++) { + auto attr_name = channel->getAttributeName(j); + auto attr_val = channel->getStringValue(attr_name); + } + } + for (unsigned int i = 0; i < nb_in_channels; i++) { + auto channel = dev.getChannel(i, false); + for (unsigned int j = 0; j < channel->getNbAttributes(); j++) { + auto attr_name = channel->getAttributeName(j); + auto attr_val = channel->getStringValue(attr_name); + } + } + auto nb_dev_attrs = dev.getNbAttributes(); + for(unsigned int i = 0; i < nb_dev_attrs; i++) { + auto attr_name = dev.getAttributeName(i); + auto attr_val = dev.getStringValue(attr_name); + } + auto nb_dev_buffer_attrs = dev.getNbBufferAttributes(); + for(unsigned int i = 0; i < nb_dev_buffer_attrs; i++) { + auto attr_name = dev.getBufferAttributeName(i); + auto attr_val = dev.getBufferStringValue(attr_name); + } + } + LIBM2K_LOG(INFO, "[END] LOG ALL"); +#endif +} + +DMM* ContextImpl::getDMM(std::string dev_name) +{ + for (DMM* d : m_instancesDMM) { + if (d->getName() == dev_name) { + return d; + } + } + return nullptr; +} + + +DMM* ContextImpl::getDMM(unsigned int index) +{ + if (index < m_instancesDMM.size()) { + return m_instancesDMM.at(index); + } else { + return nullptr; + } +} + +std::vector ContextImpl::getAllDmm() +{ + return m_instancesDMM; +} + +unsigned int ContextImpl::getDmmCount() +{ + return m_instancesDMM.size(); +} + + +std::vector ContextImpl::getAvailableContextAttributes() +{ + std::vector available_attrs = {}; + for (auto a : m_context_attributes) { + available_attrs.push_back(a.first); + } + return available_attrs; +} + +std::string ContextImpl::getContextAttributeValue(std::string attr) +{ + std::string val; + val = m_context_attributes.at(attr); + return val; +} + +std::string ContextImpl::getContextDescription() +{ + if (!m_context) { + return ""; + } + std::string descr = std::string(iio_context_get_description(m_context)); + return descr; +} + +std::string ContextImpl::getSerialNumber() +{ + return getContextAttributeValue("hw_serial"); +} + +M2k* ContextImpl::toM2k() +{ + libm2k::context::M2k* dev = dynamic_cast(this); + if(dev) { + return dev; + } else { + return nullptr; + } +} + +Generic *ContextImpl::toGeneric() +{ + libm2k::context::Generic* dev = dynamic_cast(this); + if(dev) { + return dev; + } else { + return nullptr; + } +} + +std::string ContextImpl::getUri() +{ + return m_uri; +} + +std::string ContextImpl::getFirmwareVersion() +{ + return Utils::getFirmwareVersion(m_context); +} + +void ContextImpl::initializeContextAttributes() +{ + const char *name; + const char *value; + unsigned int attr_no = iio_context_get_attrs_count(m_context); + for (unsigned int i = 0; i < attr_no; i++) { + std::pair pair; + const struct iio_attr *attr = iio_context_get_attr(m_context, i); +// int ret = iio_context_get_attr(m_context, i, &name, &value); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Device: Can't get context attribute " + std::to_string(i), libm2k::EXC_RUNTIME_ERROR); + } + pair.first = std::string(iio_attr_get_name(attr)); + pair.second = std::string(iio_attr_get_static_value(attr)); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({"context", pair.first.c_str(), LIBM2K_ATTRIBUTE_READ}, pair.second.c_str())); + m_context_attributes.insert(pair); + } +} + +const struct libm2k::IIO_CONTEXT_VERSION ContextImpl::getIioContextVersion() +{ + libm2k::IIO_CONTEXT_VERSION iioContextVersion = {}; + iioContextVersion.major = iio_context_get_version_major(m_context); + iioContextVersion.minor = iio_context_get_version_minor(m_context); + return iioContextVersion; +} + +struct iio_context *ContextImpl::getIioContext() +{ + return m_context; +} + +void ContextImpl::setTimeout(unsigned int timeout) +{ + iio_context_set_timeout(m_context, timeout); +} + +void ContextImpl::setContextOwnership(bool ownsContext) +{ + m_ownsContext = ownsContext; +} diff --git a/src/contextbuilder_v1.cpp b/src/contextbuilder_v1.cpp new file mode 100644 index 00000000..72ac7132 --- /dev/null +++ b/src/contextbuilder_v1.cpp @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "m2k_impl.hpp" +#include "generic_impl.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LIBIIO_V1 +#include +#else +#include +#endif + + +using namespace libm2k::context; +using namespace libm2k::utils; + +std::vector ContextBuilder::s_connectedDevices = {}; +std::map> ContextBuilder::m_dev_map = { + {ContextTypes::CtxFMCOMMS, {"cf-ad9361-lpc", "cf-ad9361-dds-core-lpc", "ad9361-phy"}}, + {ContextTypes::CtxM2K, {"m2k-adc", "m2k-dac-a", + "m2k-dac-b", "m2k-logic-analyzer-rx", + "m2k-logic-analyzer-tx", "m2k-logic-analyzer"}}, +}; + +std::map ContextBuilder::m_dev_name_map = { + {ContextTypes::CtxFMCOMMS, "FMMCOMMS"}, + {ContextTypes::CtxM2K, "M2K"}, + {Other, "Generic"} +}; + +bool ContextBuilder::m_disable_logging = true; +std::map ContextBuilder::reference_count = {}; + +ContextBuilder::ContextBuilder() +{ +} + +ContextBuilder::~ContextBuilder() +{ +} + +std::vector ContextBuilder::getContextsInfo() +{ + struct iio_scan *scan_ctx; + int err; + size_t results; + unsigned int nb_contexts; + std::regex re(":| \\(Analog Devices Inc. |\\), serial="); + const sregex_token_iterator end; + std::vector contexts_info; + + scan_ctx = iio_scan(NULL, "usb"); + err = iio_err(scan_ctx); + if (err) { + std::cout << "Unable to create scan context!" << std::endl; + return contexts_info; + } + + results = iio_scan_get_results_count(scan_ctx); + + if (results == 0) { + std::cout << "Unable to scan!" << std::endl; + goto out_destroy_context; + } + + nb_contexts = static_cast(results); + + for (unsigned int i = 0; i < nb_contexts; i++) + { + std::string description = std::string(iio_scan_get_description(scan_ctx, i)); + std::sregex_token_iterator ptr(description.begin(), description.end(), re, -1); + auto *ctx_info = new struct libm2k::CONTEXT_INFO(); + ctx_info->uri = std::string(iio_scan_get_uri(scan_ctx, i)); + ctx_info->manufacturer = "Analog Devices Inc."; + if (ptr != end) { + ctx_info->id_vendor = *ptr++; + } + if (ptr != end) { + ctx_info->id_product = *ptr++; + } + if (ptr != end) { + ctx_info->product = *ptr++; + } + if (ptr != end) { + ctx_info->serial = *ptr++; + } + contexts_info.push_back(ctx_info); + } + +out_destroy_context: + iio_scan_destroy(scan_ctx); + return contexts_info; +} + +std::vector ContextBuilder::getAllContexts() +{ + std::vector uris; + + auto contexts_info = getContextsInfo(); + uris.reserve(contexts_info.size()); + + for (auto ctx_info: contexts_info) { + uris.push_back(ctx_info->uri); + delete ctx_info; + } + return uris; +} + +Context* ContextBuilder::buildContext(ContextTypes type, std::string uri, + struct iio_context* ctx, bool sync, bool ownsContext) // enum Device Name +{ + std::string name = m_dev_name_map.at(type); + switch (type) { + case CtxM2K: + { + auto m2k = new M2kImpl(uri, ctx, name, sync); + m2k->setContextOwnership(ownsContext); + return m2k; + } + case Other: + default: + { + auto generic = new GenericImpl(uri, ctx, name, sync); + generic->setContextOwnership(ownsContext); + return generic; + } + } +} + +void ContextBuilder::incrementReferenceCount(std::string uri) +{ + if (reference_count.find(uri) != reference_count.end()) { + reference_count[uri]++; + } else { + reference_count.emplace(uri, 1); + } +} + +void ContextBuilder::decrementReferenceCount(std::string uri) +{ + if (reference_count.find(uri) != reference_count.end()) { + reference_count[uri]--; + } +} + +bool ContextBuilder::checkLastReference(std::string uri) +{ + if (reference_count.find(uri) != reference_count.end()) { + return (reference_count[uri] == 0); + } + return false; +} + +Context* ContextBuilder::searchInConnectedDevices(std::string uri) +{ + for (auto dev : s_connectedDevices) { + if (dev->getUri() == uri) { + return dev; + } + } + return nullptr; +} + +Context* ContextBuilder::contextOpen(const char *uri) +{ + int err; + Context* dev = nullptr; + if (m_disable_logging) { + enableLogging(false); + } + LIBM2K_LOG(INFO, "libm2k version: " + getVersion()); + // use saved device is possible + dev = searchInConnectedDevices(uri); + if (dev) { + incrementReferenceCount(uri); + return dev; + } + // create and save device during first call + struct iio_context* ctx = iio_create_context(NULL, uri); + err = iio_err(ctx); + if (err) { + return nullptr; + } + unsigned int ctx_major, ctx_minor; + ctx_major = iio_context_get_version_major(ctx); + ctx_minor = iio_context_get_version_minor(ctx); +// iio_context_get_version(ctx, &ctx_major, &ctx_minor, ctx_git_tag); + LIBM2K_LOG(INFO, "libiio version: " + to_string(ctx_major) + "." + + to_string(ctx_minor)); + + const struct iio_attr *attr_libusb = iio_context_find_attr(ctx, "usb,libusb"); + if (attr_libusb != NULL) { + const char *libusb_version = iio_attr_get_static_value(attr_libusb); + LIBM2K_LOG(INFO, "libusb version: " + std::string(libusb_version)); + } + + const struct iio_attr *attr_fw_version = iio_context_find_attr(ctx, "fw_version"); + if (attr_fw_version != NULL) { + const char *hw_fw_version = iio_attr_get_static_value(attr_fw_version); + LIBM2K_LOG(INFO, "Firmware version: " + std::string(hw_fw_version)); + } + + ContextTypes dev_type = ContextBuilder::identifyContext(ctx); + + dev = buildContext(dev_type, std::string(uri), ctx, true, true); + s_connectedDevices.push_back(dev); + incrementReferenceCount(uri); + + return dev; +} + +Context* ContextBuilder::contextOpen(struct iio_context* ctx, const char* uri) +{ + Context* dev = nullptr; + if (m_disable_logging) { + enableLogging(false); + } + LIBM2K_LOG(INFO, "libm2k version: " + getVersion()); + + unsigned int ctx_major, ctx_minor; + ctx_major = iio_context_get_version_major(ctx); + ctx_minor = iio_context_get_version_minor(ctx); +// iio_context_get_version(ctx, &ctx_major, &ctx_minor, ctx_git_tag); + LIBM2K_LOG(INFO, "libiio version: " + to_string(ctx_major) + "." + + to_string(ctx_minor)); + + const struct iio_attr *attr_libusb = iio_context_find_attr(ctx, "usb,libusb"); + if (attr_libusb != NULL) { + const char *libusb_version = iio_attr_get_static_value(attr_libusb); + LIBM2K_LOG(INFO, "libusb version: " + std::string(libusb_version)); + } + + const struct iio_attr *attr_fw_version = iio_context_find_attr(ctx, "fw_version"); + if (attr_fw_version != NULL) { + const char *hw_fw_version = iio_attr_get_static_value(attr_fw_version); + LIBM2K_LOG(INFO, "Firmware version: " + std::string(hw_fw_version)); + } + + // use saved device is possible + dev = searchInConnectedDevices(uri); + if (dev) { + incrementReferenceCount(uri); + return dev; + } + + if (!ctx) { + return nullptr; + } + // create and save device during first call + ContextTypes dev_type = ContextBuilder::identifyContext(ctx); + + dev = buildContext(dev_type, std::string(uri), ctx, true); + s_connectedDevices.push_back(dev); + incrementReferenceCount(uri); + + return dev; +} + +/* Connect to the first usb device that was found +TODO: try to use the "local" context, +before trying the "usb" one. */ +Context* ContextBuilder::contextOpen() +{ + auto lst = getAllContexts(); + if (lst.size() <= 0) { + return nullptr; + } + return contextOpen(lst.at(0).c_str()); +} + +M2k *ContextBuilder::m2kOpen(struct iio_context* ctx, const char *uri) +{ + auto dev = contextOpen(ctx, uri); + if (!dev) { + return nullptr; + } + + auto m2k = dev->toM2k(); + if (m2k) { + return m2k; + } + contextClose(dev); + return nullptr; +} + +M2k *ContextBuilder::m2kOpen(const char *uri) +{ + auto dev = contextOpen(uri); + if (!dev) { + return nullptr; + } + + auto m2k = dev->toM2k(); + if (m2k) { + return m2k; + } + contextClose(dev); + return nullptr; +} + +M2k *ContextBuilder::m2kOpen() +{ + auto dev = contextOpen(); + if (!dev) { + return nullptr; + } + + auto m2k = dev->toM2k(); + if (m2k) { + return m2k; + } + contextClose(dev); + return nullptr; +} + +void ContextBuilder::contextClose(Context* device, bool deinit) +{ + auto uri = device->getUri(); + if (searchInConnectedDevices(uri) == nullptr) { + return; + } + decrementReferenceCount(uri); + bool isLastReference = checkLastReference(uri); + if (!isLastReference) { + return; + } + + reference_count.erase(uri); + s_connectedDevices.erase(std::remove(s_connectedDevices.begin(), + s_connectedDevices.end(), + device), s_connectedDevices.end()); + try { + if (deinit) { + device->deinitialize(); + } + } catch (std::exception &e ){ + delete device; + THROW_M2K_EXCEPTION("Context deinit: " + std::string(e.what()), libm2k::EXC_RUNTIME_ERROR); + } + delete device; +} + +void ContextBuilder::contextCloseAll() +{ + while (s_connectedDevices.size() > 0) { + contextClose(s_connectedDevices.at(0)); + } +} + +std::string ContextBuilder::getVersion() +{ + if (std::string(PROJECT_VERSION_GIT).empty()) + return "v" + std::string(PROJECT_VERSION); + else + return "v" + std::string(PROJECT_VERSION) + + "-g" + std::string(PROJECT_VERSION_GIT); +} + +void ContextBuilder::enableLogging(bool enable) +{ +#ifdef LIBM2K_ENABLE_LOG + if (enable) { + FLAGS_minloglevel = 0; + m_disable_logging = false; + } else { + FLAGS_minloglevel = 3; + m_disable_logging = true; + } +#else + if (enable) { + std::cout << "libm2k built without logging support\n"; + } +#endif +} + +ContextTypes ContextBuilder::identifyContext(iio_context *ctx) +{ + ContextTypes type = Other; + for (auto dev : m_dev_map) { + bool found = Utils::devicesFoundInContext(ctx, dev.second); + if (found) { + return dev.first; + } + } + return type; +} + +Context *libm2k::context::contextOpen() +{ + return ContextBuilder::contextOpen(); +} + +Context *libm2k::context::contextOpen(const char *uri) +{ + return ContextBuilder::contextOpen(uri); +} + +Context *libm2k::context::contextOpen(struct iio_context *ctx, const char *uri) +{ + return ContextBuilder::contextOpen(ctx, uri); +} + +M2k *libm2k::context::m2kOpen(const char *uri) +{ + return ContextBuilder::m2kOpen(uri); +} + +M2k *libm2k::context::m2kOpen(struct iio_context *ctx, const char *uri) +{ + return ContextBuilder::m2kOpen(ctx, uri); +} + +M2k *libm2k::context::m2kOpen() +{ + return ContextBuilder::m2kOpen(); +} + +std::vector libm2k::context::getContextsInfo() +{ + return ContextBuilder::getContextsInfo(); +} + +std::vector libm2k::context::getAllContexts() +{ + return ContextBuilder::getAllContexts(); +} + +void libm2k::context::contextClose(Context *ctx, bool deinit) +{ + ContextBuilder::contextClose(ctx, deinit); +} + +void libm2k::context::contextCloseAll() +{ + ContextBuilder::contextCloseAll(); +} + +std::string libm2k::context::getVersion() +{ + return ContextBuilder::getVersion(); +} + +void libm2k::context::enableLogging(bool enable) +{ + return ContextBuilder::enableLogging(enable); +} diff --git a/src/utils/buffer.hpp b/src/utils/buffer.hpp index 0990f20c..cf234d4f 100644 --- a/src/utils/buffer.hpp +++ b/src/utils/buffer.hpp @@ -23,75 +23,10 @@ #define BUFFER_HPP #ifndef LIBIIO_V1 -#include +#include "buffer_v0.hpp" #else -#include +#include "buffer_v1.hpp" #endif -#include -#include -#include -#include -#include - -namespace libm2k { -namespace utils { -class Channel; - -class Buffer -{ -public: - Buffer(struct iio_device *dev); - ~Buffer(); - - void initializeBuffer(unsigned int size, bool cyclic, bool output, bool enableFlag = false); - void push(std::vector const &data, unsigned int channel = 0, - bool cyclic = true, bool multiplex = false, bool enableFlag = false); - void push(std::vector const &data, unsigned int channel = 0, - bool cyclic = true, bool multiplex = false, bool enableFlag = false); - void push(unsigned short *data, unsigned int channel, unsigned int nb_samples, - bool cyclic = true, bool multiplex = false, bool enableFlag = false); - void push(std::vector const &data, unsigned int channel = 0, bool cyclic = true, bool enableFlag = false); - void push(std::vector> const &data); - - void push(double *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); - void push(short *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); - - void setChannels(std::vector channels); - std::vector getSamples(unsigned int nb_samples); - const unsigned short* getSamplesP(unsigned int nb_samples); - - std::vector> getSamples(unsigned int nb_samples, - const std::function &process); - const double *getSamplesInterleaved(unsigned int nb_samples, - const std::function &process); - const short *getSamplesRawInterleaved(unsigned int nb_samples); - void* getSamplesRawInterleavedVoid(unsigned int nb_samples); - - void getSamples(std::vector> &data, unsigned int nb_samples, - const std::function &process); - void getSamples(std::vector &data, unsigned int nb_samples); - - void stop(); - void setCyclic(bool enable); - void cancelBuffer(); - void flushBuffer(); - unsigned int getNbSamples() const; - - struct iio_buffer* getBuffer(); -private: - struct iio_device* m_dev; - struct iio_buffer* m_buffer; - const char *m_dev_name; - unsigned int m_last_nb_samples; - bool m_cyclic; - std::vector m_channel_list; - std::vector> m_data; - std::vector m_data_short; - - void destroy(); -}; -} -} #endif //BUFFER_HPP diff --git a/src/utils/buffer_v0.cpp b/src/utils/buffer_v0.cpp index fa2f758c..62ffb797 100644 --- a/src/utils/buffer_v0.cpp +++ b/src/utils/buffer_v0.cpp @@ -19,7 +19,7 @@ * */ -#include "buffer.hpp" +#include "buffer_v0.hpp" #include "channel.hpp" #include #include diff --git a/src/utils/buffer_v0.hpp b/src/utils/buffer_v0.hpp new file mode 100644 index 00000000..c7c1503a --- /dev/null +++ b/src/utils/buffer_v0.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef BUFFER_V0_HPP +#define BUFFER_V0_HPP + +#include +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel; + +class Buffer +{ +public: + Buffer(struct iio_device *dev); + ~Buffer(); + + void initializeBuffer(unsigned int size, bool cyclic, bool output, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(unsigned short *data, unsigned int channel, unsigned int nb_samples, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, bool cyclic = true, bool enableFlag = false); + void push(std::vector> const &data); + + void push(double *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); + void push(short *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); + + void setChannels(std::vector channels); + std::vector getSamples(unsigned int nb_samples); + const unsigned short* getSamplesP(unsigned int nb_samples); + + std::vector> getSamples(unsigned int nb_samples, + const std::function &process); + const double *getSamplesInterleaved(unsigned int nb_samples, + const std::function &process); + const short *getSamplesRawInterleaved(unsigned int nb_samples); + void* getSamplesRawInterleavedVoid(unsigned int nb_samples); + + void getSamples(std::vector> &data, unsigned int nb_samples, + const std::function &process); + void getSamples(std::vector &data, unsigned int nb_samples); + + void stop(); + void setCyclic(bool enable); + void cancelBuffer(); + void flushBuffer(); + unsigned int getNbSamples() const; + + struct iio_buffer* getBuffer(); +private: + struct iio_device* m_dev; + struct iio_buffer* m_buffer; + const char *m_dev_name; + unsigned int m_last_nb_samples; + bool m_cyclic; + std::vector m_channel_list; + std::vector> m_data; + std::vector m_data_short; + + void destroy(); +}; +} +} + +#endif //BUFFER_V0_HPP diff --git a/src/utils/buffer_v1.cpp b/src/utils/buffer_v1.cpp new file mode 100644 index 00000000..9d949e76 --- /dev/null +++ b/src/utils/buffer_v1.cpp @@ -0,0 +1,780 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "buffer_v1.hpp" +#include "channel_v1.hpp" +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace libm2k::utils; + +Buffer::Buffer(struct iio_device *dev, struct iio_channels_mask *mask) { + m_dev = dev; + m_mask = mask; + + if (!m_dev) { + m_dev = nullptr; + THROW_M2K_EXCEPTION("Buffer: Device not found, so no buffer can be created", libm2k::EXC_INVALID_PARAMETER); + } + m_dev_name = iio_device_get_name(m_dev); + m_buffer = nullptr; + m_block = nullptr; + m_block_enqueued = false; + m_stream = nullptr; + m_last_nb_samples = 0; + m_nb_kernel_buffers = 4; +} + +Buffer::~Buffer() { + stop(); + destroy(); + destroyBuffer(); + m_data.clear(); + m_data_short.clear(); +} + +void Buffer::initializeBuffer(unsigned int size, bool cyclic, bool output, bool enableFlag) +{ + /* In non-cyclic mode pushing samples will fill the internal buffers, creating the possibility of continuous + * data transferring; the buffer must be destroy when its size is changed + * + * In cyclic mode the very first buffer pushed will be repeated; in order to push any other buffer the + * old buffer must be destroyed and a new one must be created*/ + if (!m_buffer) { + // TBD who should own this channel mask? + if (m_mask) { + m_buffer = iio_device_create_buffer(m_dev, 0, m_mask); + int buf_err = iio_err(m_buffer); + if (buf_err) { + m_buffer = nullptr; + if (buf_err == -ETIMEDOUT) { + THROW_M2K_EXCEPTION("Buffer: Cannot create the buffer", libm2k::EXC_TIMEOUT, -ETIMEDOUT); + } + THROW_M2K_EXCEPTION("Buffer: Cannot create the buffer", libm2k::EXC_RUNTIME_ERROR, buf_err); + } + } else { + LIBM2K_LOG(INFO, "[FAIL] Buffer NOT created, no mask provided."); + } + } + if (size != m_last_nb_samples || cyclic) { + if (enableFlag && cyclic) { + for(auto channel : m_channel_list) { + if (channel->hasAttribute("raw_enable")) { + channel->setStringValue("raw_enable", "enabled"); + } + } + } + destroy(); + + m_last_nb_samples = size; + if (cyclic && output) { + ssize_t sample_size = iio_device_get_sample_size(m_dev, m_mask); + m_block = iio_buffer_create_block(m_buffer, sample_size * m_last_nb_samples); + int ret = iio_err(m_block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot create block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + } else { + m_stream = iio_buffer_create_stream(m_buffer, m_nb_kernel_buffers, size); + int ret = iio_err(m_stream); + if (ret) { + destroy(); + + // TBD destroy channel mask + // timeout error code + THROW_M2K_EXCEPTION("Buffer: Cannot create stream", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + } + + if (enableFlag) { + for(auto channel : m_channel_list) { + if (channel->hasAttribute("raw_enable")) { + channel->setStringValue("raw_enable", "disabled"); + } + } + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, std::string((output ? "TX" : "RX")) + " buffer created (" + std::to_string(size) + + " samples)")); + LIBM2K_LOG_IF(WARNING, size % 4 != 0 || size < 16, + libm2k::buildLoggingMessage({m_dev_name}, "Incorrect number of samples")); + + LIBM2K_LOG_IF(WARNING, output && cyclic && size < 1024, + libm2k::buildLoggingMessage({m_dev_name}, "Cyclic buffer too small. The length of the buffer should be greater than 1024")); + } +} + +void Buffer::setChannels(std::vector channels) +{ + m_channel_list = channels; +} + +void Buffer::push(unsigned short *data, unsigned int channel, unsigned int nb_samples, + bool cyclic, bool multiplex, bool enableFlag) +{ + if (Utils::getIioDeviceDirection(m_dev) != OUTPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (nb_samples == 0) { + return; + } + + initializeBuffer(nb_samples, cyclic, true, enableFlag); + + if (channel < m_channel_list.size() ) { + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + + if (!multiplex) { + m_channel_list.at(channel)->write(block, data, nb_samples); + } else { + short *p_dat; + int i = 0; + + for (p_dat = (short *)iio_block_start(block); (p_dat < iio_block_end(block)); + (unsigned short*)p_dat++, i++) { + *p_dat = data[i]; + } + + } + + /** + ssize_t ret = iio_buffer_push(m_buffer); + if (ret < 0) { + destroy(); + // timeout error code + if (ret == -ETIMEDOUT) { + THROW_M2K_EXCEPTION("Buffer: Push timeout occurred", libm2k::EXC_TIMEOUT, ret); + } + THROW_M2K_EXCEPTION("Buffer: Cannot push TX buffer", libm2k::EXC_RUNTIME_ERROR, ret); + } + **/ + + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + } +} + +//push on a certain channel +void Buffer::push(std::vector const &data, unsigned int channel, + bool cyclic, bool multiplex, bool enableFlag) +{ + size_t size = data.size(); + if (Utils::getIioDeviceDirection(m_dev) != OUTPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (data.size() == 0) { + return; + } + + initializeBuffer(size, cyclic, true, enableFlag); + const struct iio_block *block; + + if (channel < m_channel_list.size() ) { + const struct iio_channels_mask *hw_mask = iio_buffer_get_channels_mask(m_buffer); + ssize_t sample_size = iio_device_get_sample_size(m_dev, hw_mask); + if (cyclic || m_block) { + block = m_block; + } else { + block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + } + if (!multiplex) { + m_channel_list.at(channel)->write(block, (void*)data.data(), sample_size, size); + } else { + short *p_dat; + int i = 0; + + for (p_dat = (short *)iio_block_start(block); (p_dat < iio_block_end(block)); + (short*)p_dat++, i++) { + *p_dat = data[i]; + } + + } + if (cyclic || m_block) { + int err = iio_block_enqueue(m_block, 0, cyclic); + if (err) { + THROW_M2K_EXCEPTION("Buffer: Unable to enqueue block.", libm2k::EXC_INVALID_PARAMETER, err); + } + m_block_enqueued = true; + err = iio_buffer_enable(m_buffer); + if (err) { + THROW_M2K_EXCEPTION("Buffer: Unable to enable buffer.", libm2k::EXC_INVALID_PARAMETER, err); + } + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Buffer::push(std::vector const &data, unsigned int channel, + bool cyclic, bool multiplex, bool enableFlag) +{ + size_t size = data.size(); + if (Utils::getIioDeviceDirection(m_dev) != OUTPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (data.size() == 0) { + return; + } + + initializeBuffer(size, cyclic, true, enableFlag); + const struct iio_block *block; + + if (channel < m_channel_list.size() ) { + + const struct iio_channels_mask *hw_mask = iio_buffer_get_channels_mask(m_buffer); + ssize_t sample_size = iio_device_get_sample_size(m_dev, hw_mask); + if (cyclic || m_block) { + block = m_block; + } else { + block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + } + if (!multiplex) { + m_channel_list.at(channel)->write(block, (void*)data.data(), sample_size, size); + } else { + void *p_dat; + int i = 0; + for (p_dat = iio_block_start(block); (p_dat < iio_block_end(block)); + p_dat = (void *)((intptr_t) p_dat + sample_size), i++) { +// *p_dat = data[i]; + memcpy(p_dat, data.data()+i, sample_size); + } + + } + if (cyclic || m_block) { + int err = iio_block_enqueue(m_block, 0, cyclic); + if (err) { + THROW_M2K_EXCEPTION("Buffer: Unable to enqueue block.", libm2k::EXC_INVALID_PARAMETER, err); + } + m_block_enqueued = true; + err = iio_buffer_enable(m_buffer); + if (err) { + THROW_M2K_EXCEPTION("Buffer: Unable to enable buffer.", libm2k::EXC_INVALID_PARAMETER, err); + } + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + + } +} + +void Buffer::push(std::vector const &data, unsigned int channel, bool cyclic, bool enableFlag) +{ + size_t size = data.size(); + if (Utils::getIioDeviceDirection(m_dev) == INPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (data.size() == 0) { + return; + } + + initializeBuffer(size, cyclic, true, enableFlag); + + if (channel < m_channel_list.size() ) { + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + + m_channel_list.at(channel)->write(block, data); +// ssize_t ret = iio_buffer_push(m_buffer); +// if (ret < 0) { +// destroy(); +// // timeout error code +// if (ret == -ETIMEDOUT) { +// THROW_M2K_EXCEPTION("Buffer: Push timeout occurred", libm2k::EXC_TIMEOUT, ret); +// } +// THROW_M2K_EXCEPTION("Buffer: Cannot push TX buffer", libm2k::EXC_RUNTIME_ERROR, ret); +// } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Buffer::push(std::vector> const &data) +{ + size_t data_ch_nb = data.size(); + + if (data_ch_nb > m_channel_list.size()) { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_OUT_OF_RANGE); + } + + for (unsigned int i = 0; i < data_ch_nb; i++) { + push(data.at(i), i); + } +} + +void Buffer::push(double *data, unsigned int channel, unsigned int nb_samples, bool cyclic, bool enableFlag) +{ + if (Utils::getIioDeviceDirection(m_dev) == INPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (nb_samples == 0) { + return; + } + + initializeBuffer(nb_samples, cyclic, true, enableFlag); + + if (channel < m_channel_list.size() ) { + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + m_channel_list.at(channel)->write(block, data, nb_samples); +// ssize_t ret = iio_buffer_push(m_buffer); +// if (ret < 0) { +// destroy(); +// // timeout error code +// if (ret == -ETIMEDOUT) { +// THROW_M2K_EXCEPTION("Buffer: Push timeout occurred", libm2k::EXC_TIMEOUT, ret); +// } +// THROW_M2K_EXCEPTION("Buffer: Cannot push TX buffer", libm2k::EXC_RUNTIME_ERROR, ret); +// } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Buffer::push(short *data, unsigned int channel, unsigned int nb_samples, bool cyclic, bool enableFlag) +{ + if (Utils::getIioDeviceDirection(m_dev) == INPUT) { + THROW_M2K_EXCEPTION("Device not output buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + } + + /* If the data vector is empty, then it means we want + * to remove what was pushed earlier to the device, so + * we destroy the buffer */ + if (nb_samples == 0) { + return; + } + + initializeBuffer(nb_samples, cyclic, true, enableFlag); + + if (channel < m_channel_list.size() ) { + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + + m_channel_list.at(channel)->write(block, data, nb_samples); +// ssize_t ret = iio_buffer_push(m_buffer); +// if (ret < 0) { +// destroy(); +// // timeout error code +// if (ret == -ETIMEDOUT) { +// THROW_M2K_EXCEPTION("Buffer: Push timeout occurred", libm2k::EXC_TIMEOUT, ret); +// } +// THROW_M2K_EXCEPTION("Buffer: Cannot push TX buffer", libm2k::EXC_RUNTIME_ERROR, ret); +// } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer pushed")); + } else { + THROW_M2K_EXCEPTION("Buffer: Please setup channels before pushing data", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Buffer::getSamples(std::vector &data, unsigned int nb_samples) +{ + if (Utils::getIioDeviceDirection(m_dev) == OUTPUT) { + THROW_M2K_EXCEPTION("Device not input-buffer capable, so no buffer was created", libm2k::EXC_RUNTIME_ERROR); + } + + data.clear(); + + initializeBuffer(nb_samples, false, false); + + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return; + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer refilled")); + + unsigned short* d_ptr = (unsigned short*)iio_block_start(block); + for (unsigned int i = 0; i < nb_samples; i++) { + data.push_back(d_ptr[i]); + } +} + +void Buffer::setKernelBuffersCount(unsigned int count) +{ + m_nb_kernel_buffers = count; +} + +std::vector Buffer::getSamples(unsigned int nb_samples) +{ + m_data_short.clear(); + getSamples(m_data_short, nb_samples); + return m_data_short; +} + +const unsigned short* Buffer::getSamplesP(unsigned int nb_samples) +{ + if (Utils::getIioDeviceDirection(m_dev) == OUTPUT) { + THROW_M2K_EXCEPTION("Device not input-buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + return nullptr; + } + + initializeBuffer(nb_samples, false, false); + + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return nullptr; + } + + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer refilled")); + + const unsigned short* data = (const unsigned short*)iio_block_start(block); + return data; +} + + +void Buffer::getSamples(std::vector> &data, unsigned int nb_samples, + const std::function &process) +{ + short* data_p = (short *) getSamplesRawInterleaved(nb_samples); + + std::vector channels_enabled; + data.clear(); + + for (auto chn : m_channel_list) { + bool en = chn->isEnabled(m_mask); + channels_enabled.push_back(en); + std::vector ch_data {}; + ch_data.reserve(nb_samples); + data.push_back(ch_data); + } + + unsigned int i; + unsigned int nb_channels = m_channel_list.size(); + + + for (i = 0; i < nb_samples; i++) { + for (unsigned int ch = 0; ch < nb_channels; ch++) { + if (channels_enabled[ch]) { + data[ch].push_back(process(data_p[i * nb_channels + ch], ch)); + } + } + } +} + +std::vector> Buffer::getSamples(unsigned int nb_samples, + const std::function &process) +{ + m_data.clear(); + getSamples(m_data, nb_samples, process); + return m_data; +} + +const short* Buffer::getSamplesRawInterleaved(unsigned int nb_samples) +{ + return static_cast(getSamplesRawInterleavedVoid(nb_samples)); +} + +void* Buffer::getSamplesRawInterleavedVoid(unsigned int nb_samples) +{ + bool anyChannelEnabled = false; + if (Utils::getIioDeviceDirection(m_dev) != INPUT) { + THROW_M2K_EXCEPTION("Device not input-buffer capable, so no buffer was created", libm2k::EXC_INVALID_PARAMETER); + return nullptr; + } + + for (auto chn : m_channel_list) { + bool en = chn->isEnabled(m_mask); + anyChannelEnabled = en ? true : anyChannelEnabled; + } + + if (!anyChannelEnabled) { + THROW_M2K_EXCEPTION("Buffer: No channel enabled for RX buffer", libm2k::EXC_INVALID_PARAMETER); + return nullptr; + } + + initializeBuffer(nb_samples, false, false); + + if (!m_stream) { + return nullptr; + } + const struct iio_block *block = iio_stream_get_next_block(m_stream); + int ret = iio_err(block); + if (ret) { + destroy(); + THROW_M2K_EXCEPTION("Buffer: Cannot get next block", libm2k::EXC_RUNTIME_ERROR, ret); + return nullptr; + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer refilled")); + + return m_channel_list.at(0)->getFirstVoid(block); +} + +const double* Buffer::getSamplesInterleaved(unsigned int nb_samples, + const std::function &process) +{ + const short* data_p = getSamplesRawInterleaved(nb_samples); + + std::vector channels_enabled; + + for (auto chn : m_channel_list) { + bool en = chn->isEnabled(m_mask); + channels_enabled.push_back(en); + } + + unsigned int nb_channels = m_channel_list.size(); + double *data_p_d = new double[nb_samples * channels_enabled.size()]; + unsigned int i; + unsigned int i_d = 0; + + for (i = 0; i < nb_samples; i++) { + for (unsigned int ch = 0; ch < nb_channels; ch++) { + if (channels_enabled.at(ch)) { + data_p_d[i_d] = process(data_p[i * nb_channels + ch], ch); + i_d++; + } + } + } + + return (const double *)data_p_d; +} + +void Buffer::stop() +{ + if (Utils::getIioDeviceDirection(m_dev) != OUTPUT) { + return; + } + + if (m_buffer) { + // TBD watchout for cancelling buffers + iio_buffer_cancel(m_buffer); + } + + destroy(); + destroyBuffer(); +} + +void Buffer::destroyBuffer() +{ + if (m_buffer) { + iio_buffer_destroy(m_buffer); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer destroyed")); + m_buffer = nullptr; + m_last_nb_samples = 0; + } +} + + +void Buffer::destroy() +{ + if (m_block) { + if (m_block_enqueued) { + iio_block_dequeue(m_block, false); + m_block_enqueued = false; + } + iio_block_destroy(m_block); + m_block = nullptr; + int err = iio_buffer_disable(m_buffer); + } + + if (m_stream) { + iio_stream_destroy(m_stream); +// iio_buffer_disable(m_buffer); + } + + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Stream destroyed")); + m_stream = nullptr; + m_last_nb_samples = 0; +} + +void Buffer::cancelBuffer() +{ + if (m_buffer) { + iio_buffer_cancel(m_buffer); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name}, "Buffer canceled")); + } +} + +void Buffer::flushBuffer() +{ + destroy(); + m_last_nb_samples = 0; +} + +void Buffer::setCyclic(bool enable) +{ + m_cyclic = enable; +} + +unsigned int Buffer::getNbSamples() const +{ + return m_last_nb_samples; +} + +unsigned int Buffer::getNbAttributes() +{ + if (!m_buffer) + return 0; + return iio_buffer_get_attrs_count(m_buffer); +} + +std::string Buffer::getAttributeName(unsigned int idx) +{ + const char *attr_name = ""; + const struct iio_attr* attr = iio_buffer_get_attr(m_buffer, idx); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Buffer: invalid attribute requested.", libm2k::EXC_INVALID_PARAMETER); + } + attr_name = iio_attr_get_name(attr); + return attr_name; +} + +int Buffer::getLongValue(std::string attr_name) +{ + long long value = 0; + const struct iio_attr* attr = iio_buffer_find_attr(m_buffer, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Buffer: invalid attribute requested: " + attr_name, + libm2k::EXC_INVALID_PARAMETER); + } + int ret = iio_attr_read_longlong(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Buffer: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::to_string(value))); + return static_cast(value); +} + +void Buffer::setLongValue(int value, std::string attr_name) +{ + const struct iio_attr* attr = iio_buffer_find_attr(m_buffer, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Buffer: invalid attribute requested: " + attr_name, + libm2k::EXC_INVALID_PARAMETER); + } + int ret = iio_attr_write_longlong(attr, value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Buffer: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, std::to_string(value))); +} + +std::string Buffer::getStringValue(std::string attr_name) +{ + char value[1024]; + const struct iio_attr* attr = iio_buffer_find_attr(m_buffer, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Buffer: invalid attribute requested: " + attr_name, + libm2k::EXC_INVALID_PARAMETER); + } + int ret = iio_attr_read_raw(attr, value, sizeof(value)); + if (ret < 0) { + THROW_M2K_EXCEPTION("Buffer: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::string(value))); + return std::string(value); +} + +void Buffer::setStringValue(std::string attr_name, std::string value) +{ + const struct iio_attr* attr = iio_buffer_find_attr(m_buffer, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Buffer: invalid attribute requested: " + attr_name, + libm2k::EXC_INVALID_PARAMETER); + } + int ret = iio_attr_write_raw(attr, value.c_str(), value.size()); + if (ret < 0) { + THROW_M2K_EXCEPTION("Buffer: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::string(value))); +} + +struct iio_buffer* Buffer::getBuffer() +{ + return m_buffer; +} + +bool Buffer::hasBufferAttribute(std::string attr_name) +{ + const struct iio_attr* attr = iio_buffer_find_attr(m_buffer, attr_name.c_str()); + return !!attr; + // TBD ? +} diff --git a/src/utils/buffer_v1.hpp b/src/utils/buffer_v1.hpp new file mode 100644 index 00000000..6f41f3d7 --- /dev/null +++ b/src/utils/buffer_v1.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef BUFFER_V1_HPP +#define BUFFER_V1_HPP +#include + +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel; + +class Buffer +{ +public: + Buffer(struct iio_device *dev, struct iio_channels_mask *mask); + ~Buffer(); + + void initializeBuffer(unsigned int size, bool cyclic, bool output, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(unsigned short *data, unsigned int channel, unsigned int nb_samples, + bool cyclic = true, bool multiplex = false, bool enableFlag = false); + void push(std::vector const &data, unsigned int channel = 0, bool cyclic = true, bool enableFlag = false); + void push(std::vector> const &data); + + void push(double *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); + void push(short *data, unsigned int channel, unsigned int nb_samples, bool cyclic = true, bool enableFlag = false); + + void setChannels(std::vector channels); + std::vector getSamples(unsigned int nb_samples); + const unsigned short* getSamplesP(unsigned int nb_samples); + + std::vector> getSamples(unsigned int nb_samples, + const std::function &process); + const double *getSamplesInterleaved(unsigned int nb_samples, + const std::function &process); + const short *getSamplesRawInterleaved(unsigned int nb_samples); + void* getSamplesRawInterleavedVoid(unsigned int nb_samples); + + void getSamples(std::vector> &data, unsigned int nb_samples, + const std::function &process); + void getSamples(std::vector &data, unsigned int nb_samples); + + void setKernelBuffersCount(unsigned int count); + + void stop(); + void setCyclic(bool enable); + void cancelBuffer(); + void flushBuffer(); + unsigned int getNbSamples() const; + + unsigned int getNbAttributes(); + std::string getAttributeName(unsigned int idx); + void setLongValue(int value, std::string attr_name); + int getLongValue(std::string attr_name); + struct iio_buffer* getBuffer(); + bool hasBufferAttribute(std::string attr_name); + void setStringValue(std::string attr_name, std::string value); + std::string getStringValue(std::string attr_name); +private: + struct iio_device* m_dev; + struct iio_buffer* m_buffer; + struct iio_channels_mask *m_mask; + struct iio_stream *m_stream; + struct iio_block *m_block; + bool m_block_enqueued; + const char *m_dev_name; + unsigned int m_last_nb_samples; + unsigned int m_nb_kernel_buffers; + bool m_cyclic; + std::vector m_channel_list; + std::vector> m_data; + std::vector m_data_short; + + void destroy(); + void destroyBuffer(); +}; +} +} + +#endif //BUFFER_V1_HPP diff --git a/src/utils/channel.hpp b/src/utils/channel.hpp index b8e6bfdc..fb6d979a 100644 --- a/src/utils/channel.hpp +++ b/src/utils/channel.hpp @@ -23,72 +23,9 @@ #define CHANNEL_HPP #ifndef LIBIIO_V1 -#include +#include "channel_v0.hpp" #else -#include +#include "channel_v1.hpp" #endif -#include -#include -#include -#include - -namespace libm2k { -namespace utils { -class Channel -{ -public: - Channel(struct iio_device* device, unsigned int channel = 0); - Channel(struct iio_device* device, std::string channel_name, bool output); - virtual ~Channel(); - - std::string getName(); - std::string getId(); - unsigned int getIndex(); - bool isOutput(); - bool isEnabled(); - unsigned int getNbAttributes(); - std::string getAttributeName(unsigned int idx); - bool hasAttribute(std::string attr); - void write(struct iio_buffer* buffer, std::vector const &data); - void write(struct iio_buffer* buffer, std::vector const &data); - void write(struct iio_buffer* buffer, std::vector const &data); - void write(struct iio_buffer* buffer, double *data, unsigned int nb_samples); - void write(struct iio_buffer* buffer, short *data, unsigned int nb_samples); - void write(struct iio_buffer* buffer, unsigned short *data, unsigned int nb_samples); - - void convert(int16_t *avg, int16_t *src); - void convert(double *avg, int16_t *src); - - void setDoubleValue(std::string attr, double val); - double getDoubleValue(std::string attr); - - void setStringValue(std::string attr, std::string val); - std::string getStringValue(std::string attr); - - void setLongValue(std::string attr, long long val); - long long getLongValue(std::string attr); - - void setBoolValue(std::string attr, bool val); - bool getBoolValue(std::string attr); - - std::vector getAvailableAttributeValues(const std::string &attr); - - void enableChannel(bool enable); - uintptr_t getFirst(struct iio_buffer* buffer); - void *getFirstVoid(iio_buffer *buffer); - - bool isValid(); - struct iio_channel* getChannel(); - iio_device* getDevice(); -private: - struct iio_device *m_device; - struct iio_channel *m_channel; - - const char *m_dev_name; - const char *m_channel_id; - -}; -} -} #endif //CHANNEL_HPP diff --git a/src/utils/channel_v0.hpp b/src/utils/channel_v0.hpp new file mode 100644 index 00000000..279c8152 --- /dev/null +++ b/src/utils/channel_v0.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef CHANNEL_V0_HPP +#define CHANNEL_V0_HPP + +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel +{ +public: + Channel(struct iio_device* device, unsigned int channel = 0); + Channel(struct iio_device* device, std::string channel_name, bool output); + virtual ~Channel(); + + std::string getName(); + std::string getId(); + unsigned int getIndex(); + bool isOutput(); + bool isEnabled(); + unsigned int getNbAttributes(); + std::string getAttributeName(unsigned int idx); + bool hasAttribute(std::string attr); + void write(struct iio_buffer* buffer, std::vector const &data); + void write(struct iio_buffer* buffer, std::vector const &data); + void write(struct iio_buffer* buffer, std::vector const &data); + void write(struct iio_buffer* buffer, double *data, unsigned int nb_samples); + void write(struct iio_buffer* buffer, short *data, unsigned int nb_samples); + void write(struct iio_buffer* buffer, unsigned short *data, unsigned int nb_samples); + + void convert(int16_t *avg, int16_t *src); + void convert(double *avg, int16_t *src); + + void setDoubleValue(std::string attr, double val); + double getDoubleValue(std::string attr); + + void setStringValue(std::string attr, std::string val); + std::string getStringValue(std::string attr); + + void setLongValue(std::string attr, long long val); + long long getLongValue(std::string attr); + + void setBoolValue(std::string attr, bool val); + bool getBoolValue(std::string attr); + + std::vector getAvailableAttributeValues(const std::string &attr); + + void enableChannel(bool enable); + uintptr_t getFirst(struct iio_buffer* buffer); + void *getFirstVoid(iio_buffer *buffer); + + bool isValid(); + struct iio_channel* getChannel(); + iio_device* getDevice(); +private: + struct iio_device *m_device; + struct iio_channel *m_channel; + + const char *m_dev_name; + const char *m_channel_id; + +}; +} +} +#endif //CHANNEL_V0_HPP diff --git a/src/utils/channel_v1.cpp b/src/utils/channel_v1.cpp new file mode 100644 index 00000000..db4fea6b --- /dev/null +++ b/src/utils/channel_v1.cpp @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "channel_v1.hpp" +#include +#include +#include +#include +#include + +using namespace libm2k; +using namespace libm2k::utils; + +Channel::Channel(iio_device *device, unsigned int channel) { + m_device = device; + if (m_device) { + m_channel = iio_device_get_channel(m_device, channel); + } + m_dev_name = iio_device_get_name(m_device); + m_channel_id = iio_channel_get_id(m_channel); + + if (!m_channel) { + m_channel = nullptr; + } +} + +Channel::Channel(iio_device *device, std::string channel_name, bool output) +{ + m_device = device; + if (m_device) { + m_channel = iio_device_find_channel(device, channel_name.c_str(), output); + } + + if (!m_channel) { + m_channel = nullptr; + + } + +} + +Channel::~Channel() { +} + +std::string Channel::getName() +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + std::string name = ""; + auto n = iio_channel_get_name(m_channel); + if (n) { + name = std::string(n); + } + return name; +} + +std::string Channel::getId() +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + return iio_channel_get_id(m_channel); +} + +unsigned int Channel::getIndex() +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + long ret; + ret = iio_channel_get_index(m_channel); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot get the index of channel", libm2k::EXC_INVALID_PARAMETER, ret); + } + return ret; +} + +bool Channel::isOutput() +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + return iio_channel_is_output(m_channel); +} + +bool Channel::isEnabled(struct iio_channels_mask *mask) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + return iio_channel_is_enabled(m_channel, mask); +} + +unsigned int Channel::getNbAttributes() +{ + return iio_channel_get_attrs_count(m_channel); +} + +std::string Channel::getAttributeName(unsigned int idx) +{ + const struct iio_attr *attr = iio_channel_get_attr(m_channel, idx); + if (attr != NULL) { + return iio_attr_get_name(attr); + } + return ""; +} + +bool Channel::hasAttribute(std::string attr) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + if (iio_channel_find_attr(m_channel, attr.c_str()) != NULL) { + return true; + } + return false; +} + +void Channel::enableChannel(bool enable, struct iio_channels_mask *mask) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + if (enable) { + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id}, "Enable channel")); + iio_channel_enable(m_channel, mask); + } else { + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id}, "Disable channel")); + iio_channel_disable(m_channel, mask); + } +} + +uintptr_t Channel::getFirst(const struct iio_block *block) +{ + return reinterpret_cast(getFirstVoid(block)); +} + + +void* Channel::getFirstVoid(const struct iio_block *block) +{ + return iio_block_first(block, m_channel); +} + +void Channel::write(const struct iio_block* block, void *data, ssize_t sample_size, size_t nb_samples)//std::vector const &data) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + +// size_t size = data.size(); + size_t ret = iio_channel_write(m_channel, (struct iio_block*)block, data, + nb_samples * sample_size, false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Channel::write(const struct iio_block* block, std::vector const &data) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + size_t size = data.size(); + size_t ret = iio_channel_write(m_channel, (iio_block*)block, data.data(), + size * sizeof(unsigned short), false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Channel::write(const struct iio_block* block, std::vector const &data) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + size_t size = data.size(); + size_t ret = iio_channel_write(m_channel, (iio_block*)block, data.data(), + size * sizeof(double), false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + + } +} + +void Channel::write(const struct iio_block* block, double *data, unsigned int nb_samples) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + size_t ret = iio_channel_write(m_channel, (iio_block*)block, data, + nb_samples * sizeof(double), false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Channel::write(const struct iio_block* block, short *data, unsigned int nb_samples) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + size_t ret = iio_channel_write(m_channel, (iio_block*)block, data, + nb_samples * sizeof(short), false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Channel::write(const struct iio_block* block, unsigned short *data, unsigned int nb_samples) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + + size_t ret = iio_channel_write(m_channel, (iio_block*)block, data, + nb_samples * sizeof(unsigned short), false); + + if (ret == 0) { + THROW_M2K_EXCEPTION("Channel: could not write; result is 0 bytes", libm2k::EXC_INVALID_PARAMETER); + } +} + +void Channel::convert(int16_t *avg, int16_t *src) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + iio_channel_convert(m_channel, (void *)avg, (const void *)src); +} + +void Channel::convert(double *avg, int16_t *src) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + iio_channel_convert(m_channel, (void *)avg, (const void *)src); +} + +double Channel::getDoubleValue(std::string attr_name) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + double value = 0.0; + int ret = iio_attr_read_double(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, + std::to_string(value).c_str())); + return value; +} + +void Channel::setDoubleValue(std::string attr_name, double val) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_double(attr, val); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, + libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, + std::to_string(val).c_str())); +} + +void Channel::setLongValue(std::string attr_name, long long val) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_longlong(attr, val); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, + libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, + std::to_string(val).c_str())); +} + +long long Channel::getLongValue(std::string attr_name) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + long long value = 0; + int ret = iio_attr_read_longlong(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, + std::to_string(value).c_str())); + return value; +} + +void Channel::setStringValue(std::string attr_name, std::string val) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_raw(attr, val.c_str(), val.size()); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, val.c_str())); +} + +std::string Channel::getStringValue(std::string attr_name) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + char value[1024]; + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_read_raw(attr, value, sizeof(value)); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, value)); + return std::string(value); +} + +void Channel::setBoolValue(std::string attr_name, bool val) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_bool(attr, val); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, + libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, + std::to_string(val).c_str())); +} + +bool Channel::getBoolValue(std::string attr_name) +{ + if (!m_channel) { + THROW_M2K_EXCEPTION("Channel: Cannot find associated channel", libm2k::EXC_INVALID_PARAMETER); + } + bool value; + const struct iio_attr *attr = iio_channel_find_attr(m_channel, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Channel: Cannot find attribute: " + attr_name, libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_read_bool(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Channel: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, m_channel_id, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, + std::to_string(value).c_str())); + return value; +} + +std::vector Channel::getAvailableAttributeValues(const std::string &attr) +{ + std::vector values; + std::string valuesAsString; + + __try { + valuesAsString = getStringValue(std::string(attr + "_available")); + std::istringstream iss(valuesAsString); + values = std::vector(std::istream_iterator{iss}, + std::istream_iterator()); + } __catch (exception_type&) { + values.push_back(getStringValue(attr)); + } + return values; +} + +bool Channel::isValid() +{ + return !!m_channel; +} + +struct iio_channel* Channel::getChannel() +{ + return m_channel; +} + +iio_device *Channel::getDevice() +{ + return m_device; +} diff --git a/src/utils/channel_v1.hpp b/src/utils/channel_v1.hpp new file mode 100644 index 00000000..39263a93 --- /dev/null +++ b/src/utils/channel_v1.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef CHANNEL_V1_HPP +#define CHANNEL_V1_HPP + +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel +{ +public: + Channel(struct iio_device* device, unsigned int channel = 0); + Channel(struct iio_device* device, std::string channel_name, bool output); + virtual ~Channel(); + + std::string getName(); + std::string getId(); + unsigned int getIndex(); + bool isOutput(); + bool isEnabled(iio_channels_mask *mask); + unsigned int getNbAttributes(); + std::string getAttributeName(unsigned int idx); + bool hasAttribute(std::string attr); + void write(const struct iio_block* block, std::vector const &data); + void write(const struct iio_block* block, std::vector const &data); + void write(const struct iio_block* block, std::vector const &data); + void write(const struct iio_block* block, double *data, unsigned int nb_samples); + void write(const struct iio_block* block, short *data, unsigned int nb_samples); + void write(const struct iio_block* block, unsigned short *data, unsigned int nb_samples); + void write(const struct iio_block* block, void *data, ssize_t sample_size, size_t nb_samples); + + void convert(int16_t *avg, int16_t *src); + void convert(double *avg, int16_t *src); + + void setDoubleValue(std::string attr_name, double val); + double getDoubleValue(std::string attr_name); + + void setStringValue(std::string attr_name, std::string val); + std::string getStringValue(std::string attr_name); + + void setLongValue(std::string attr_name, long long val); + long long getLongValue(std::string attr_name); + + void setBoolValue(std::string attr_name, bool val); + bool getBoolValue(std::string attr_name); + + std::vector getAvailableAttributeValues(const std::string &attr); + + void enableChannel(bool enable, struct iio_channels_mask *); + uintptr_t getFirst(const struct iio_block* block); + void *getFirstVoid(const struct iio_block *block); + + bool isValid(); + struct iio_channel* getChannel(); + iio_device* getDevice(); +private: + struct iio_device *m_device; + struct iio_channel *m_channel; + + const char *m_dev_name; + const char *m_channel_id; + +}; +} +} +#endif //CHANNEL_V1_HPP diff --git a/src/utils/devicegeneric.hpp b/src/utils/devicegeneric.hpp index 2fde2f39..5ddcdbbe 100644 --- a/src/utils/devicegeneric.hpp +++ b/src/utils/devicegeneric.hpp @@ -23,110 +23,9 @@ #define DEVICEGENERIC_HPP #ifndef LIBIIO_V1 -#include +#include "devicegeneric_v0.hpp" #else -#include +#include "devicegeneric_v1.hpp" #endif -#include -#include -#include -#include -#include -#include - -namespace libm2k { -namespace utils { -class Channel; -class Buffer; - -/** - * The DeviceGeneric class is to be used in interacting with any IIO device (it should express a correspondent - * to the struct iio_device in underlying libiio. It provides basic functionality such as: - * - creating and initializing the iio_device and its corresponding channels; - * - channel handling is done in a generic way, scanning all the input and output channels; - * - all the mutators and accessors are as generic as possible, while providing a fairly simple usage scenario; - * these can be used for iio_device related attributes or for channel related attributes; in the latter case, - * the direction of the channels should be known and provided (input/output); - * - can be used to manage iio_devices with both input and output channels - * (can handle any channel type (whether it is voltage in, voltage out, altvoltage, temp, etc.); - */ -class DeviceGeneric -{ -public: - DeviceGeneric(struct iio_context* context, std::string dev_name = ""); - virtual ~DeviceGeneric(); - - virtual Channel *getChannel(unsigned int chnIdx, bool output); - virtual Channel *getChannel(std::string id, bool output); - virtual bool isChannel(unsigned int chnIdx, bool output); - - virtual void enableChannel(unsigned int chnIdx, bool enable, bool output); - virtual bool isChannelEnabled(unsigned int chnIdx, bool output); - - virtual std::string getName(); - virtual std::string getId(); - virtual unsigned int getNbAttributes(); - virtual unsigned int getNbBufferAttributes(); - virtual std::string getAttributeName(unsigned int idx); - virtual std::string getBufferAttributeName(unsigned int idx); - virtual double getDoubleValue(std::string attr); - virtual double getDoubleValue(unsigned int, std::string attr, bool output=false); - virtual double setDoubleValue(double value, std::string attr); - virtual double setDoubleValue(unsigned int chn_idx, double value, std::string attr, bool output=false); - - virtual int getLongValue(std::string attr); - virtual int getLongValue(unsigned int, std::string attr, bool output=false); - virtual int getBufferLongValue(std::string attr); - virtual int setLongValue(long long value, std::string attr); - virtual int setLongValue(unsigned int chn_idx, long long value, std::string attr, bool output=false); - virtual int setBufferLongValue(int value, std::string attr); - - virtual bool getBoolValue(std::string attr); - virtual bool getBoolValue(unsigned int, std::string attr, bool output=false); - virtual bool setBoolValue(bool value, std::string attr); - virtual bool setBoolValue(unsigned int chn_idx, bool value, std::string attr, bool output=false); - - virtual std::string setStringValue(std::string attr, std::string value); - virtual std::string setStringValue(unsigned int chn, std::string attr, - std::string value, bool output=false); - virtual std::string setBufferStringValue(std::string attr, std::string value); - virtual std::string getStringValue(std::string attr); - virtual std::string getStringValue(unsigned int chn, std::string attr, bool output=false); - virtual std::string getBufferStringValue(std::string attr); - - std::vector getAvailableAttributeValues(const std::string &attr); - std::vector getAvailableAttributeValues(unsigned int chn_idx, const std::string &attr, bool output=false); - - virtual void writeRegister(uint32_t address, uint32_t value); - - virtual std::string getHardwareRevision(); - virtual unsigned int getNbChannels(bool output); - - virtual void convertChannelHostFormat(unsigned int chn_idx, int16_t *avg, int16_t *src, bool output); - virtual void convertChannelHostFormat(unsigned int chn_idx, double *avg, int16_t *src, bool output); - - virtual void setKernelBuffersCount(unsigned int count); - virtual bool isValidDmmChannel(unsigned int chnIdx); - - virtual std::pair getContextAttr(unsigned int attrIdx); - - virtual void setCyclic(bool en); - virtual bool hasGlobalAttribute(std::string attr); - virtual bool hasBufferAttribute(std::string attr); - - virtual ssize_t getSampleSize(); - virtual unsigned int getNbSamples() const; -protected: - struct iio_context *m_context; - struct iio_device *m_dev; - std::vector m_channel_list_in; - std::vector m_channel_list_out; - Buffer* m_buffer; - const char *m_dev_name; -}; -} -} - - #endif //DEVICEGENERIC_HPP diff --git a/src/utils/devicegeneric_v0.hpp b/src/utils/devicegeneric_v0.hpp new file mode 100644 index 00000000..8caa750a --- /dev/null +++ b/src/utils/devicegeneric_v0.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEGENERIC_V0_HPP +#define DEVICEGENERIC_V0_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel; +class Buffer; + +/** + * The DeviceGeneric class is to be used in interacting with any IIO device (it should express a correspondent + * to the struct iio_device in underlying libiio. It provides basic functionality such as: + * - creating and initializing the iio_device and its corresponding channels; + * - channel handling is done in a generic way, scanning all the input and output channels; + * - all the mutators and accessors are as generic as possible, while providing a fairly simple usage scenario; + * these can be used for iio_device related attributes or for channel related attributes; in the latter case, + * the direction of the channels should be known and provided (input/output); + * - can be used to manage iio_devices with both input and output channels + * (can handle any channel type (whether it is voltage in, voltage out, altvoltage, temp, etc.); + */ +class DeviceGeneric +{ +public: + DeviceGeneric(struct iio_context* context, std::string dev_name = ""); + virtual ~DeviceGeneric(); + + virtual Channel *getChannel(unsigned int chnIdx, bool output); + virtual Channel *getChannel(std::string id, bool output); + virtual bool isChannel(unsigned int chnIdx, bool output); + + virtual void enableChannel(unsigned int chnIdx, bool enable, bool output); + virtual bool isChannelEnabled(unsigned int chnIdx, bool output); + + virtual std::string getName(); + virtual std::string getId(); + virtual unsigned int getNbAttributes(); + virtual unsigned int getNbBufferAttributes(); + virtual std::string getAttributeName(unsigned int idx); + virtual std::string getBufferAttributeName(unsigned int idx); + virtual double getDoubleValue(std::string attr); + virtual double getDoubleValue(unsigned int, std::string attr, bool output=false); + virtual double setDoubleValue(double value, std::string attr); + virtual double setDoubleValue(unsigned int chn_idx, double value, std::string attr, bool output=false); + + virtual int getLongValue(std::string attr); + virtual int getLongValue(unsigned int, std::string attr, bool output=false); + virtual int getBufferLongValue(std::string attr); + virtual int setLongValue(long long value, std::string attr); + virtual int setLongValue(unsigned int chn_idx, long long value, std::string attr, bool output=false); + virtual int setBufferLongValue(int value, std::string attr); + + virtual bool getBoolValue(std::string attr); + virtual bool getBoolValue(unsigned int, std::string attr, bool output=false); + virtual bool setBoolValue(bool value, std::string attr); + virtual bool setBoolValue(unsigned int chn_idx, bool value, std::string attr, bool output=false); + + virtual std::string setStringValue(std::string attr, std::string value); + virtual std::string setStringValue(unsigned int chn, std::string attr, + std::string value, bool output=false); + virtual std::string setBufferStringValue(std::string attr, std::string value); + virtual std::string getStringValue(std::string attr); + virtual std::string getStringValue(unsigned int chn, std::string attr, bool output=false); + virtual std::string getBufferStringValue(std::string attr); + + std::vector getAvailableAttributeValues(const std::string &attr); + std::vector getAvailableAttributeValues(unsigned int chn_idx, const std::string &attr, bool output=false); + + virtual void writeRegister(uint32_t address, uint32_t value); + + virtual std::string getHardwareRevision(); + virtual unsigned int getNbChannels(bool output); + + virtual void convertChannelHostFormat(unsigned int chn_idx, int16_t *avg, int16_t *src, bool output); + virtual void convertChannelHostFormat(unsigned int chn_idx, double *avg, int16_t *src, bool output); + + virtual void setKernelBuffersCount(unsigned int count); + virtual bool isValidDmmChannel(unsigned int chnIdx); + + virtual std::pair getContextAttr(unsigned int attrIdx); + + virtual void setCyclic(bool en); + virtual bool hasGlobalAttribute(std::string attr); + virtual bool hasBufferAttribute(std::string attr); + + virtual ssize_t getSampleSize(); + virtual unsigned int getNbSamples() const; +protected: + struct iio_context *m_context; + struct iio_device *m_dev; + std::vector m_channel_list_in; + std::vector m_channel_list_out; + Buffer* m_buffer; + const char *m_dev_name; +}; +} +} + + +#endif //DEVICEGENERIC_V0_HPP diff --git a/src/utils/devicegeneric_v1.cpp b/src/utils/devicegeneric_v1.cpp new file mode 100644 index 00000000..f319855b --- /dev/null +++ b/src/utils/devicegeneric_v1.cpp @@ -0,0 +1,726 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "devicegeneric.hpp" +#include "utils/buffer.hpp" +#include "utils/channel.hpp" +#include +#include +#include +#include "context_impl.hpp" +#include +#include +#include +#include +#include + +using namespace std; +using namespace libm2k::utils; +using namespace libm2k::context; + +#define KB_SET_MAX_RETRIES 20 + +/** Represents an iio_device **/ +DeviceGeneric::DeviceGeneric(struct iio_context* context, std::string dev_name) +{ + m_context = context; + m_dev = nullptr; + m_buffer = nullptr; + m_mask = nullptr; + + if (dev_name != "") { + m_dev = iio_context_find_device(context, dev_name.c_str()); + if (!m_dev) { + THROW_M2K_EXCEPTION("Device: No such device", libm2k::EXC_INVALID_PARAMETER); + } + m_dev_name = iio_device_get_name(m_dev); + + bool is_buffer_capable = false; + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + m_mask = iio_create_channels_mask(nb_channels); + for (unsigned int i = 0; i < nb_channels; i++) { + Channel *chn = nullptr; + chn = new Channel(m_dev, i); + if (!chn->isValid()) { + delete chn; + chn = nullptr; + continue; + } + if (!is_buffer_capable && iio_channel_is_scan_element(chn->getChannel())) { + is_buffer_capable = true; + } + if (chn->isOutput()) { + m_channel_list_out.push_back(chn); + } else { + m_channel_list_in.push_back(chn); + } + } + if (is_buffer_capable) { + __try { + m_buffer = new Buffer(m_dev, m_mask); + } __catch (exception_type &) { + delete m_buffer; + m_buffer = nullptr; + } + } + + std::sort(m_channel_list_out.begin(), m_channel_list_out.end(), [](Channel* lchn, Channel* rchn) + { + return Utils::compareNatural(lchn->getId(), rchn->getId()); + }); + std::sort(m_channel_list_in.begin(), m_channel_list_in.end(), [](Channel* lchn, Channel* rchn) + { + return Utils::compareNatural(lchn->getId(), rchn->getId()); + }); + } +} + +DeviceGeneric::~DeviceGeneric() +{ + if (m_buffer) { + delete m_buffer; + m_buffer = nullptr; + } + if (m_mask) { + iio_channels_mask_destroy(m_mask); + m_mask = nullptr; + } + for (auto ch : m_channel_list_in) { + delete ch; + } + for (auto ch : m_channel_list_out) { + delete ch; + } + m_channel_list_out.clear(); + m_channel_list_in.clear(); +} + +Channel* DeviceGeneric::getChannel(unsigned int chnIdx, bool output) +{ + if (output) { + if (chnIdx >= m_channel_list_out.size()) { + goto error; + } + return m_channel_list_out.at(chnIdx); + } else { + if (chnIdx >= m_channel_list_in.size()) { + goto error; + } + return m_channel_list_in.at(chnIdx); + } +error: + THROW_M2K_EXCEPTION("Device: No such channel: " + to_string(chnIdx), libm2k::EXC_OUT_OF_RANGE); + return nullptr; +} + +Channel* DeviceGeneric::getChannel(std::string id, bool output) +{ + std::vector tmp = output ? m_channel_list_out : m_channel_list_in; + for (auto ch : tmp) { + if (id == ch->getName()) { + return ch; + } else if (id == ch->getId()) { + return ch; + } + } + return nullptr; +} + +bool DeviceGeneric::isChannel(unsigned int chnIdx, bool output) +{ + std::string name = "voltage" + to_string(chnIdx); + auto chn = iio_device_find_channel(m_dev, name.c_str(), output); + if (chn) { + return true; + } else { + return false; + } +} + +void DeviceGeneric::enableChannel(unsigned int chnIdx, bool enable, bool output) +{ + getChannel(chnIdx, output)->enableChannel(enable, m_mask); +} + +bool DeviceGeneric::isChannelEnabled(unsigned int chnIdx, bool output) +{ + return getChannel(chnIdx, output)->isEnabled(m_mask); +} + +string DeviceGeneric::getName() +{ + if (!m_dev) { + THROW_M2K_EXCEPTION("Device: No available device", libm2k::EXC_INVALID_PARAMETER); + } + std::string name = ""; + auto n = iio_device_get_name(m_dev); + if (n) { + name = std::string(n); + } + return name; +} + +string DeviceGeneric::getId() +{ + if (!m_dev) { + THROW_M2K_EXCEPTION("Device: No available device", libm2k::EXC_INVALID_PARAMETER); + } + return iio_device_get_id(m_dev); +} + + +unsigned int DeviceGeneric::getNbAttributes() +{ + return iio_device_get_attrs_count(m_dev); +} + +unsigned int DeviceGeneric::getNbBufferAttributes() +{ + return m_buffer->getNbAttributes(); +} + +std::string DeviceGeneric::getAttributeName(unsigned int idx) +{ + std::string attrName = ""; + const struct iio_attr *attr = iio_device_get_attr(m_dev, idx); + if (attr != NULL) { + attrName = std::string(iio_attr_get_name(attr)); + } + return attrName; +} + +std::string DeviceGeneric::getBufferAttributeName(unsigned int idx) +{ + return m_buffer->getAttributeName(idx); +} + +double DeviceGeneric::getDoubleValue(std::string attr_name) +{ + double value = 0.0; + std::string dev_name = getName(); + + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + iio_attr_read_double(attr, &value); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::to_string(value))); + return value; +} + +double DeviceGeneric::getDoubleValue(unsigned int chn_idx, std::string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr + " attribute for the selected channel", libm2k::EXC_INVALID_PARAMETER); + } + return chn->getDoubleValue(attr); +} + +double DeviceGeneric::setDoubleValue(double value, std::string attr_name) +{ + std::string dev_name = iio_device_get_name(m_dev); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + iio_attr_write_double(attr, value); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, std::to_string(value))); + return getDoubleValue(attr_name); +} + +double DeviceGeneric::setDoubleValue(unsigned int chn_idx, double value, std::string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + chn->setDoubleValue(attr, value); + return chn->getDoubleValue(attr); +} + +int DeviceGeneric::getLongValue(std::string attr_name) +{ + long long value = 0; + std::string dev_name = getName(); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_read_longlong(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::to_string(value))); + return static_cast(value); +} + +int DeviceGeneric::getLongValue(unsigned int chn_idx, std::string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr + " attribute for the selected channel", libm2k::EXC_INVALID_PARAMETER); + } + + return static_cast(chn->getLongValue(attr)); +} + +int DeviceGeneric::setLongValue(long long value, std::string attr_name) +{ + std::string dev_name = iio_device_get_name(m_dev); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_longlong(attr, value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, std::to_string(value))); + return getLongValue(attr_name); +} + +int DeviceGeneric::setLongValue(unsigned int chn_idx, long long value, std::string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + chn->setLongValue(attr, value); + return static_cast(chn->getLongValue(attr)); +} + +int DeviceGeneric::getBufferLongValue(std::string attr_name) +{ + return m_buffer->getLongValue(attr_name); +} + +int DeviceGeneric::setBufferLongValue(int value, std::string attr_name) +{ + m_buffer->setLongValue(value, attr_name); + return m_buffer->getLongValue(attr_name); +} + +bool DeviceGeneric::getBoolValue(string attr_name) +{ + bool value = 0; + std::string dev_name = getName(); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_read_bool(attr, &value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, std::to_string(value))); + return value; +} + +bool DeviceGeneric::getBoolValue(unsigned int chn_idx, string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + return chn->getBoolValue(attr); +} + +bool DeviceGeneric::setBoolValue(bool value, string attr_name) +{ + std::string dev_name = iio_device_get_name(m_dev); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_bool(attr, value); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, std::to_string(value))); + return getBoolValue(attr_name); +} + +bool DeviceGeneric::setBoolValue(unsigned int chn_idx, bool value, string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + chn->setBoolValue(attr, value); + return chn->getBoolValue(attr); +} + +string DeviceGeneric::setStringValue(string attr_name, string value) +{ + std::string dev_name = iio_device_get_name(m_dev); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_write_raw(attr, value.c_str(), value.size()); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot write " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_WRITE}, value)); + return getStringValue(attr_name); +} + +string DeviceGeneric::setStringValue(unsigned int chn_idx, string attr, string value, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + chn->setStringValue(attr, value); + return chn->getStringValue(attr); +} + +string DeviceGeneric::getStringValue(string attr_name) +{ + char value[100]; + std::string dev_name = getName(); + const struct iio_attr *attr = iio_device_find_attr(m_dev, attr_name.c_str()); + if (attr == NULL) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr_name + " attribute", libm2k::EXC_INVALID_PARAMETER); + } + + int ret = iio_attr_read_raw(attr, value, sizeof(value)); + if (ret < 0) { + THROW_M2K_EXCEPTION("Device: Cannot read " + attr_name, libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({m_dev_name, attr_name.c_str(), LIBM2K_ATTRIBUTE_READ}, value)); + return std::string(value); +} + +string DeviceGeneric::getStringValue(unsigned int chn_idx, string attr, bool output) +{ + unsigned int nb_channels = iio_device_get_channels_count(m_dev); + std::string dev_name = getName(); + + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + } + + return chn->getStringValue(attr); +} + +std::string DeviceGeneric::setBufferStringValue(std::string attr, std::string value) +{ + m_buffer->setStringValue(attr, value); + +} + +std::string DeviceGeneric::getBufferStringValue(string attr_name) +{ + return m_buffer->getStringValue(attr_name); +} + +std::vector DeviceGeneric::getAvailableAttributeValues(const string &attr) +{ + std::string dev_name; + std::string valuesAsString; + std::vector values; + + dev_name = getName(); + if (!ContextImpl::iioDevHasAttribute(m_dev, attr)) { + THROW_M2K_EXCEPTION(dev_name + " has no " + attr + " attribute", libm2k::EXC_INVALID_PARAMETER); + return std::vector(); + } + + __try { + valuesAsString = getStringValue(std::string(attr + "_available")); + std::istringstream iss(valuesAsString); + values = std::vector(std::istream_iterator{iss}, + std::istream_iterator()); + } __catch (exception_type&) { + values.push_back(getStringValue(attr)); + } + return values; +} + +std::vector DeviceGeneric::getAvailableAttributeValues(unsigned int chn_idx, const std::string &attr, bool output) +{ + unsigned int nb_channels; + std::string dev_name; + + nb_channels = iio_device_get_channels_count(m_dev); + dev_name = getName(); + + if (chn_idx >= nb_channels) { + THROW_M2K_EXCEPTION(dev_name + " has no such channel", libm2k::EXC_OUT_OF_RANGE); + return std::vector(); + } + + auto chn = getChannel(chn_idx, output); + if (!chn->hasAttribute(attr)) { + THROW_M2K_EXCEPTION(dev_name + + " has no " + attr + + " attribute for the selected channel", + libm2k::EXC_INVALID_PARAMETER); + return std::vector(); + } + return chn->getAvailableAttributeValues(attr); +} + +void DeviceGeneric::writeRegister(uint32_t address, uint32_t value) +{ + int ret = iio_device_reg_write(m_dev, address, value); + if (ret) { + THROW_M2K_EXCEPTION("Device: can't write register", libm2k::EXC_INVALID_PARAMETER, ret); + } + LIBM2K_LOG(INFO, + libm2k::buildLoggingMessage({m_dev_name, std::to_string(address).c_str(), LIBM2K_ATTRIBUTE_WRITE}, + std::to_string(value))); +} + +std::string DeviceGeneric::getHardwareRevision() +{ + const struct iio_attr *attr = iio_context_find_attr(m_context, "hw_model"); + if (attr == NULL) { + return ""; + } + + const char *hw_rev_attr_val = iio_attr_get_static_value(attr); + std::string rev; + + if (hw_rev_attr_val) { + std::string const s = hw_rev_attr_val; + std::string const key = "Rev."; + unsigned long n = s.find(key); + + n += key.length(); + rev = s.substr(n, 1); + } else { + rev = "A"; + } + + return rev; +} + +unsigned int DeviceGeneric::getNbChannels(bool output) +{ + if (output) { + return m_channel_list_out.size(); + } else { + return m_channel_list_in.size(); + } +} + +void DeviceGeneric::convertChannelHostFormat(unsigned int chn_idx, int16_t *avg, int16_t *src, bool output) +{ + std::vector tmp = output ? m_channel_list_out : m_channel_list_in; + if (chn_idx < tmp.size()) { + tmp.at(chn_idx)->convert(avg, src); + } else { + THROW_M2K_EXCEPTION("Device: No such channel", libm2k::EXC_OUT_OF_RANGE); + } +} + +void DeviceGeneric::convertChannelHostFormat(unsigned int chn_idx, double *avg, int16_t *src, bool output) +{ + std::vector tmp = output ? m_channel_list_out : m_channel_list_in; + if (chn_idx < tmp.size()) { + tmp.at(chn_idx)->convert(avg, src); + } else { + THROW_M2K_EXCEPTION("Device: No such channel", libm2k::EXC_OUT_OF_RANGE); + } +} + +void DeviceGeneric::setKernelBuffersCount(unsigned int count) +{ +#ifdef LIBIIO_V1 + // TBD add logic for kernel buffers +#else + bool ok = false; + int ret; + int retry = 0; + if (!m_dev) { + THROW_M2K_EXCEPTION("Device: no such device", libm2k::EXC_OUT_OF_RANGE); + } + while (!ok && retry < KB_SET_MAX_RETRIES) { + ret = iio_device_set_kernel_buffers_count(m_dev, count); + retry++; + if (ret != -EBUSY) { + ok = true; + } else { + // If the call failed, allow more time to settle + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + } + if (ret != 0) { + THROW_M2K_EXCEPTION("Device: Cannot set the number of kernel buffers", libm2k::EXC_RUNTIME_ERROR, ret); + } + const char *deviceName = iio_device_get_name(m_dev); + LIBM2K_LOG(INFO, libm2k::buildLoggingMessage({deviceName}, "Set kernel buffers count: " + std::to_string(count))); +#endif +} + +bool DeviceGeneric::isValidDmmChannel(unsigned int chnIdx) +{ + if (chnIdx >= m_channel_list_in.size()) { + THROW_M2K_EXCEPTION("Device: no such DMM channel", libm2k::EXC_OUT_OF_RANGE); + } + + auto chn = m_channel_list_in.at(chnIdx); + if (chn->isOutput()) { + return false; + } + + if (chn->hasAttribute("raw") || chn->hasAttribute("input") || + chn->hasAttribute("processed")) { + return true; + } + return false; +} + +std::pair DeviceGeneric::getContextAttr(unsigned int attrIdx) +{ + std::pair pair; + const char *name; + const char *value; + + const struct iio_attr *attr = iio_context_get_attr(m_context, attrIdx); + if (attr == NULL) { + THROW_M2K_EXCEPTION("Device: Can't get context attribute " + std::to_string(attrIdx), libm2k::EXC_RUNTIME_ERROR); + } + name = iio_attr_get_name(attr); + value = iio_attr_get_static_value(attr); + pair.first = std::string(name); + pair.second = std::string(value); + return pair; +} + +void DeviceGeneric::setCyclic(bool enable) +{ + if (m_buffer) { + m_buffer->setCyclic(enable); + } +} + +bool DeviceGeneric::hasGlobalAttribute(string attr) +{ + return ContextImpl::iioDevHasAttribute(m_dev, attr); +} + +bool DeviceGeneric::hasBufferAttribute(string attr_name) +{ + return m_buffer->hasBufferAttribute(attr_name); +} + +ssize_t DeviceGeneric::getSampleSize() +{ + + // TBD use hw_sample_size + if (!m_dev) { + THROW_M2K_EXCEPTION("Device: No available device", libm2k::EXC_INVALID_PARAMETER); + } + return iio_device_get_sample_size(m_dev, m_mask); +} + +unsigned int DeviceGeneric::getNbSamples() const +{ + if (m_buffer) { + return m_buffer->getNbSamples(); + } + return 0; +} diff --git a/src/utils/devicegeneric_v1.hpp b/src/utils/devicegeneric_v1.hpp new file mode 100644 index 00000000..923d8a84 --- /dev/null +++ b/src/utils/devicegeneric_v1.hpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEGENERIC_V1_HPP +#define DEVICEGENERIC_V1_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace libm2k { +namespace utils { +class Channel; +class Buffer; + +/** + * The DeviceGeneric class is to be used in interacting with any IIO device (it should express a correspondent + * to the struct iio_device in underlying libiio. It provides basic functionality such as: + * - creating and initializing the iio_device and its corresponding channels; + * - channel handling is done in a generic way, scanning all the input and output channels; + * - all the mutators and accessors are as generic as possible, while providing a fairly simple usage scenario; + * these can be used for iio_device related attributes or for channel related attributes; in the latter case, + * the direction of the channels should be known and provided (input/output); + * - can be used to manage iio_devices with both input and output channels + * (can handle any channel type (whether it is voltage in, voltage out, altvoltage, temp, etc.); + */ +class DeviceGeneric +{ +public: + DeviceGeneric(struct iio_context* context, std::string dev_name = ""); + virtual ~DeviceGeneric(); + + virtual Channel *getChannel(unsigned int chnIdx, bool output); + virtual Channel *getChannel(std::string id, bool output); + virtual bool isChannel(unsigned int chnIdx, bool output); + + virtual void enableChannel(unsigned int chnIdx, bool enable, bool output); + virtual bool isChannelEnabled(unsigned int chnIdx, bool output); + + virtual std::string getName(); + virtual std::string getId(); + virtual unsigned int getNbAttributes(); + virtual unsigned int getNbBufferAttributes(); + virtual std::string getAttributeName(unsigned int idx); + virtual std::string getBufferAttributeName(unsigned int idx); + virtual double getDoubleValue(std::string attr); + virtual double getDoubleValue(unsigned int, std::string attr, bool output=false); + virtual double setDoubleValue(double value, std::string attr); + virtual double setDoubleValue(unsigned int chn_idx, double value, std::string attr, bool output=false); + + virtual int getLongValue(std::string attr); + virtual int getLongValue(unsigned int, std::string attr, bool output=false); + virtual int getBufferLongValue(std::string attr); + virtual int setLongValue(long long value, std::string attr); + virtual int setLongValue(unsigned int chn_idx, long long value, std::string attr, bool output=false); + virtual int setBufferLongValue(int value, std::string attr); + + virtual bool getBoolValue(std::string attr); + virtual bool getBoolValue(unsigned int, std::string attr, bool output=false); + virtual bool setBoolValue(bool value, std::string attr); + virtual bool setBoolValue(unsigned int chn_idx, bool value, std::string attr, bool output=false); + + virtual std::string setStringValue(std::string attr, std::string value); + virtual std::string setStringValue(unsigned int chn, std::string attr, + std::string value, bool output=false); + virtual std::string setBufferStringValue(std::string attr, std::string value); + virtual std::string getStringValue(std::string attr); + virtual std::string getStringValue(unsigned int chn, std::string attr, bool output=false); + virtual std::string getBufferStringValue(std::string attr); + + std::vector getAvailableAttributeValues(const std::string &attr); + std::vector getAvailableAttributeValues(unsigned int chn_idx, const std::string &attr, bool output=false); + + virtual void writeRegister(uint32_t address, uint32_t value); + + virtual std::string getHardwareRevision(); + virtual unsigned int getNbChannels(bool output); + + virtual void convertChannelHostFormat(unsigned int chn_idx, int16_t *avg, int16_t *src, bool output); + virtual void convertChannelHostFormat(unsigned int chn_idx, double *avg, int16_t *src, bool output); + + virtual void setKernelBuffersCount(unsigned int count); + virtual bool isValidDmmChannel(unsigned int chnIdx); + + virtual std::pair getContextAttr(unsigned int attrIdx); + + virtual void setCyclic(bool en); + virtual bool hasGlobalAttribute(std::string attr); + virtual bool hasBufferAttribute(std::string attr); + + virtual ssize_t getSampleSize(); + virtual unsigned int getNbSamples() const; +protected: + struct iio_context *m_context; + struct iio_device *m_dev; + struct iio_channels_mask *m_mask; + std::vector m_channel_list_in; + std::vector m_channel_list_out; + Buffer* m_buffer; + const char *m_dev_name; +}; +} +} + + +#endif //DEVICEGENERIC_V1_HPP diff --git a/src/utils/utils_v1.cpp b/src/utils/utils_v1.cpp new file mode 100644 index 00000000..2ecf04d5 --- /dev/null +++ b/src/utils/utils_v1.cpp @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of libm2k + * (see http://www.github.com/analogdevicesinc/libm2k). + * + * This program 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 program 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. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + */ + +#include "libm2k/m2kexceptions.hpp" +#include "libm2k/utils/utils.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace libm2k::utils; + +std::vector Utils::parseIniFile(std::string path) +{ + std::string line; + std::ifstream configFile(path); + std::string deviceName = ""; + struct ini_device_struct device; + std::vector devs = {}; + device.hw_name = ""; + device.key_val_pairs = {}; + + if (configFile.is_open()) { + while (getline(configFile, line)) { + auto section = parseIniSection(line); + + if ((deviceName.empty()) && (!section.empty())) { + deviceName = section; + device.hw_name = deviceName; + continue; + + } else if ((!deviceName.empty()) && (!section.empty())) { + /* Found a new section, save the current device struct + * and create another one */ + devs.push_back(device); + deviceName = section; + device.hw_name = deviceName; + device.key_val_pairs = {}; + continue; + + } else if (!deviceName.empty()) { + auto kv_pair = parseIniPair(line); + if ((!kv_pair.first.empty()) && (!kv_pair.second.empty())) { + device.key_val_pairs.push_back(kv_pair); + } + + } else { + THROW_M2K_EXCEPTION("Invalid configuration file: " + path, libm2k::EXC_INVALID_PARAMETER); + } + } + if (!device.key_val_pairs.empty()) { + devs.push_back(device); + } + configFile.close(); + } + return devs; +} + +std::string Utils::parseIniSection(std::string line) +{ + std::regex section_reg{R"(\[.+\])"}; + bool section_match = std::regex_match(line, section_reg); + if (section_match) { + return line.substr(1, line.size() - 2); + } + return ""; +} + + +std::pair> + Utils::parseIniPair(std::string line) +{ + std::string key = ""; + std::vector values {}; + size_t delim = line.find("="); + if (delim != std::string::npos) { + key = line.substr(0, delim); + line = line.substr(delim+1, line.size()-delim-1); + line.erase(remove(line.begin(), line.end(), ' '), line.end()); + values = split(line, ","); + for (unsigned int i = 0; i < values.size(); i++) { + if (values.at(i) == "") { + values.erase(values.begin() + i); + } + } + } + return std::pair>(key, values); +} + +std::vector Utils::split(std::string s, std::string delimiter) { + size_t pos_start = 0, pos_end, delim_len = delimiter.length(); + std::string token; + std::vector res; + while ((pos_end = s.find(delimiter, pos_start)) != string::npos) { + token = s.substr(pos_start, pos_end - pos_start); + pos_start = pos_end + delim_len; + res.push_back(token); + } + res.push_back(s.substr(pos_start)); + return res; +} + +std::unordered_set Utils::getAllDevices(iio_context *ctx) +{ + unsigned int nb_devices = iio_context_get_devices_count(ctx); + std::unordered_set device_list; + for (unsigned int i = 0; i < nb_devices; ++i) { + auto dev = iio_context_get_device(ctx, i); + auto name = iio_device_get_name(dev); + if (name) { + device_list.emplace(std::string(name)); + } else { + auto id = iio_device_get_id(dev); + device_list.emplace(std::string(id)); + } + } + return device_list; +} + +std::vector Utils::valuesForIniConfigKey(const ini_device_struct &iniconf, const string &key) +{ + for (const auto &key_value_pair : iniconf.key_val_pairs) { + if (key_value_pair.first == key) { + return key_value_pair.second; + } + } + return std::vector(); +} + +bool Utils::devicesFoundInContext(iio_context *ctx, std::vector device_list) +{ + auto context_devices = getAllDevices(ctx); + bool device_found = true; + for (const auto &device : device_list) { + if (context_devices.find(device) == context_devices.end()) { + device_found = false; + break; + } + } + return device_found; +} + +std::string Utils::getHardwareRevision(struct iio_context *ctx) +{ + std::string rev = ""; + const struct iio_attr *attr_hw_model = iio_context_find_attr(ctx, "hw_model"); + if (attr_hw_model != NULL) { + const char *hw_rev_attr_val = iio_attr_get_static_value(attr_hw_model); + if (hw_rev_attr_val) { + std::string const s = hw_rev_attr_val; + std::string const key = "Rev."; + int n = s.find(key); + + n += key.length(); + rev = s.substr(n, 1); + } + } else { + rev = "A"; + } + + return rev; +} + +std::string Utils::getFirmwareVersion(struct iio_context *ctx) +{ + std::string fw_version = ""; + const struct iio_attr *attr_fw_version = iio_context_find_attr(ctx, "fw_version"); + if (attr_fw_version != NULL) { + const char *hw_fw_version = iio_attr_get_static_value(attr_fw_version); + fw_version = hw_fw_version; + } else { + return ""; + } + + return fw_version; +} + +double Utils::average(double *data, size_t numElements) +{ + double sum = 0; + + for (size_t i = 0; i < numElements; i++) + sum += data[i]; + + return (sum / (double)numElements); +} + +DEVICE_DIRECTION Utils::getIioDeviceDirection(struct iio_device* dev) +{ + DEVICE_DIRECTION dir = NO_DIRECTION; + + unsigned int chn_count = iio_device_get_channels_count(dev); + for (unsigned int i = 0; i < chn_count; i++) { + auto chn = iio_device_get_channel(dev, i); + if (iio_channel_is_output(chn)) { + if (dir == INPUT) { + dir = BOTH; + } else if (dir != BOTH){ + dir = OUTPUT; + } + } else { + if (dir == OUTPUT) { + dir = BOTH; + } else if (dir != BOTH){ + dir = INPUT; + } + } + } + return dir; +} + +int Utils::compareVersions(std::string v1, std::string v2) +{ + while ((v1.find(".") != std::string::npos) || + (v2.find(".") != std::string::npos)) { + if (v1.find(".") != std::string::npos) { + v1.erase(v1.find("."), 1); + } + if (v2.find(".") != std::string::npos) { + v2.erase(v2.find("."), 1); + } + } + if (v1.size() < v2.size()) { + std::string surplus("0", (v2.size() - v1.size())); + v1.append(surplus); + } else if (v2.size() < v1.size()) { + std::string surplus("0", (v1.size() - v2.size())); + v2.append(surplus); + } + + return v1.compare(v2); +} + +bool Utils::compareNatural(const std::string& a, const std::string& b){ + if (a == b) { + return (a < b); + } else if (a.empty()) { + return true; + } else if (b.empty()) { + return false; + } else if (std::isdigit(a[0]) && !std::isdigit(b[0])) { + return true; + } else if (!std::isdigit(a[0]) && std::isdigit(b[0])) { + return false; + } else if (!std::isdigit(a[0]) && !std::isdigit(b[0])) { + if (a[0] == b[0]) { + return compareNatural(a.substr(1), b.substr(1)); + } + return (a < b); + } + + std::istringstream string_stream_a(a); + std::istringstream string_stream_b(b); + int int_a, int_b; + std::string a_new, b_new; + + string_stream_a >> int_a; + string_stream_b >> int_b; + if (int_a != int_b) { + return (int_a < int_b); + } + + std::getline(string_stream_a, a_new); + std::getline(string_stream_b, b_new); + return (compareNatural(a_new, b_new)); +} + +double Utils::safeStod(const std::string& to_convert) +{ + double converted_value = 0.0; + std::istringstream in_s(to_convert); + in_s.imbue(std::locale("C")); + in_s >> converted_value; + return converted_value; +}