From de88e305db475b990e75af55881fac9f6e734a39 Mon Sep 17 00:00:00 2001
From: f4exb <f4exb06@gmail.com>
Date: Sat, 2 Sep 2017 04:21:41 +0200
Subject: [PATCH] PlutoSDR: added PlutoSDRDevice and PlutoSDRScan common device
 classes

---
 .gitattributes                          |   1 +
 cmake/Modules/FindLibIIO.cmake          |  28 +++
 devices/CMakeLists.txt                  |   6 +
 devices/plutosdr/CMakeLists.txt         |  48 ++++
 devices/plutosdr/deviceplutosdrbox.cpp  | 280 ++++++++++++++++++++++++
 devices/plutosdr/deviceplutosdrbox.h    |  75 +++++++
 devices/plutosdr/deviceplutosdrscan.cpp | 115 ++++++++++
 devices/plutosdr/deviceplutosdrscan.h   |  45 ++++
 8 files changed, 598 insertions(+)
 create mode 100644 .gitattributes
 create mode 100644 cmake/Modules/FindLibIIO.cmake
 create mode 100644 devices/plutosdr/CMakeLists.txt
 create mode 100644 devices/plutosdr/deviceplutosdrbox.cpp
 create mode 100644 devices/plutosdr/deviceplutosdrbox.h
 create mode 100644 devices/plutosdr/deviceplutosdrscan.cpp
 create mode 100644 devices/plutosdr/deviceplutosdrscan.h

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..176a458f9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/cmake/Modules/FindLibIIO.cmake b/cmake/Modules/FindLibIIO.cmake
new file mode 100644
index 000000000..358236e32
--- /dev/null
+++ b/cmake/Modules/FindLibIIO.cmake
@@ -0,0 +1,28 @@
+if(NOT LIBIIO_FOUND)
+
+  pkg_check_modules (LIBIIO_PKG libiio)
+  find_path(LIBIIO_INCLUDE_DIR NAMES iio.h
+    PATHS
+    ${LIBIIO_PKG_INCLUDE_DIRS}
+    /usr/include
+    /usr/local/include
+  )
+
+  find_library(LIBIIO_LIBRARIES NAMES iio
+    PATHS
+    ${LIBIIO_PKG_LIBRARY_DIRS}
+    /usr/lib
+    /usr/local/lib
+  )
+
+  if(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES)
+    set(LIBIIO_FOUND TRUE CACHE INTERNAL "libiio found")
+    message(STATUS "Found libiio: ${LIBIIO_INCLUDE_DIR}, ${LIBIIO_LIBRARIES}")
+  else(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES)
+    set(LIBIIO_FOUND FALSE CACHE INTERNAL "libiio found")
+    message(STATUS "libiio not found.")
+  endif(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES)
+
+  mark_as_advanced(LIBIIO_INCLUDE_DIR LIBIIO_LIBRARIES)
+
+endif(NOT LIBIIO_FOUND)
diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt
index c87807943..ebd573256 100644
--- a/devices/CMakeLists.txt
+++ b/devices/CMakeLists.txt
@@ -6,6 +6,7 @@ if (BUILD_DEBIAN)
     add_subdirectory(bladerf)
     add_subdirectory(hackrf)
     add_subdirectory(limesdr)
+    add_subdirectory(plutosdr)
 else(BUILD_DEBIAN)
     find_package(LibBLADERF)
     if(LIBUSB_FOUND AND LIBBLADERF_FOUND)
@@ -22,4 +23,9 @@ else(BUILD_DEBIAN)
         add_subdirectory(limesdr)
     endif(LIBUSB_FOUND AND LIMESUITE_FOUND)
     
+    find_package(LibIIO)
+    if(LIBUSB_FOUND AND LIBIIO_FOUND)
+        add_subdirectory(plutosdr)
+    endif(LIBUSB_FOUND AND LIBIIO_FOUND)
+    
 endif (BUILD_DEBIAN)
diff --git a/devices/plutosdr/CMakeLists.txt b/devices/plutosdr/CMakeLists.txt
new file mode 100644
index 000000000..6ad4a038c
--- /dev/null
+++ b/devices/plutosdr/CMakeLists.txt
@@ -0,0 +1,48 @@
+project(plutosdrdevice)
+
+set (CMAKE_CXX_STANDARD 11)
+
+set(plutosdrdevice_SOURCES
+    deviceplutosdrbox.cpp
+    deviceplutosdrscan.cpp
+)
+
+set(plutosdrdevice_HEADERS
+    deviceplutsdrobox.h
+    deviceplutosdrscan.h
+)
+
+if (BUILD_DEBIAN)
+include_directories(
+    .
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${LIBIIOSRC}
+)
+else (BUILD_DEBIAN)
+include_directories(
+    .
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${LIBIIO_INCLUDE_DIR}
+)
+endif (BUILD_DEBIAN)
+
+#add_definitions(${QT_DEFINITIONS})
+#add_definitions(-DQT_SHARED)
+
+add_library(plutosdrdevice SHARED
+    ${plutosdrdevice_SOURCES}
+)
+
+if (BUILD_DEBIAN)
+target_link_libraries(plutosdrdevice
+    libiio
+    sdrbase
+)
+else (BUILD_DEBIAN)
+target_link_libraries(plutosdrdevice
+    ${LIBIIO_LIBRARIES}
+    sdrbase
+)
+endif (BUILD_DEBIAN)
+
+install(TARGETS plutosdrdevice DESTINATION lib)
diff --git a/devices/plutosdr/deviceplutosdrbox.cpp b/devices/plutosdr/deviceplutosdrbox.cpp
new file mode 100644
index 000000000..c5c1d294d
--- /dev/null
+++ b/devices/plutosdr/deviceplutosdrbox.cpp
@@ -0,0 +1,280 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 Edouard Griffiths, F4EXB                                   //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+//                                                                               //
+// 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 General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <iostream>
+#include <cstdio>
+#include <cstring>
+#include <regex>
+#include <iio.h>
+
+#include "deviceplutosdrbox.h"
+
+DevicePlutoSDRBox::DevicePlutoSDRBox(const std::string& uri) :
+        m_chnRx0(0),
+        m_chnTx0(0),
+        m_rxBuf(0),
+        m_txBuf(0)
+{
+    m_ctx = iio_create_context_from_uri(uri.c_str());
+    m_devPhy = iio_context_find_device(m_ctx, "ad9361-phy");
+    m_devRx = iio_context_find_device(m_ctx, "cf-ad9361-lpc");
+    m_devTx = iio_context_find_device(m_ctx, "cf-ad9361-dds-core-lpc");
+    m_valid = m_ctx && m_devPhy && m_devRx && m_devTx;
+}
+
+DevicePlutoSDRBox::~DevicePlutoSDRBox()
+{
+    deleteRxBuffer();
+    deleteTxBuffer();
+    closeRx();
+    closeTx();
+    if (m_ctx) { iio_context_destroy(m_ctx); }
+}
+
+void DevicePlutoSDRBox::set_params(DeviceType devType,
+        const std::vector<std::string>& params)
+{
+    iio_device *dev;
+
+    switch (devType)
+    {
+    case DEVICE_PHY:
+        dev = m_devPhy;
+        break;
+    case DEVICE_RX:
+        dev = m_devRx;
+        break;
+    case DEVICE_TX:
+        dev = m_devTx;
+        break;
+    default:
+        dev = m_devPhy;
+        break;
+    }
+
+    for (std::vector<std::string>::const_iterator it = params.begin(); it != params.end(); ++it)
+    {
+        struct iio_channel *chn = 0;
+        const char *attr = 0;
+        std::size_t pos;
+        int ret;
+
+        pos = it->find('=');
+
+        if (pos == std::string::npos)
+        {
+            std::cerr << "PlutoSDRDevice::set_params: Misformed line: " << *it << std::endl;
+            continue;
+        }
+
+        std::string key = it->substr(0, pos);
+        std::string val = it->substr(pos + 1, std::string::npos);
+
+        ret = iio_device_identify_filename(dev, key.c_str(), &chn, &attr);
+
+        if (ret)
+        {
+            std::cerr << "PlutoSDRDevice::set_params: Parameter not recognized: " << key << std::endl;
+            continue;
+        }
+
+        if (chn) {
+            ret = iio_channel_attr_write(chn, attr, val.c_str());
+        } else if (iio_device_find_attr(dev, attr)) {
+            ret = iio_device_attr_write(dev, attr, val.c_str());
+        } else {
+            ret = iio_device_debug_attr_write(dev, attr, val.c_str());
+        }
+
+        if (ret < 0)
+        {
+            std::cerr << "PlutoSDRDevice::set_params: Unable to write attribute " << key <<  ": " << ret << std::endl;
+        }
+    }
+}
+
+bool DevicePlutoSDRBox::openRx()
+{
+    if (!m_chnRx0) {
+        m_chnRx0 = iio_device_find_channel(m_devRx, "voltage0", false);
+    }
+
+    if (m_chnRx0) {
+        iio_channel_enable(m_chnRx0);
+        return true;
+    } else {
+        std::cerr << "PlutoSDRDevice::openRx: failed" << std::endl;
+        return false;
+    }
+}
+
+bool DevicePlutoSDRBox::openTx()
+{
+    if (!m_chnTx0) {
+        m_chnTx0 = iio_device_find_channel(m_devTx, "voltage0", true);
+    }
+
+    if (m_chnTx0) {
+        iio_channel_enable(m_chnTx0);
+        return true;
+    } else {
+        std::cerr << "PlutoSDRDevice::openTx: failed" << std::endl;
+        return false;
+    }
+}
+
+void DevicePlutoSDRBox::closeRx()
+{
+    if (m_chnRx0) { iio_channel_disable(m_chnRx0); }
+}
+
+void DevicePlutoSDRBox::closeTx()
+{
+    if (m_chnTx0) { iio_channel_disable(m_chnTx0); }
+}
+
+struct iio_buffer *DevicePlutoSDRBox::createRxBuffer(unsigned int size, bool cyclic)
+{
+    if (m_devRx) {
+        m_rxBuf = iio_device_create_buffer(m_devRx, size, cyclic ? '\1' : '\0');
+    } else {
+        m_rxBuf = 0;
+    }
+
+    return m_rxBuf;
+}
+
+struct iio_buffer *DevicePlutoSDRBox::createTxBuffer(unsigned int size, bool cyclic)
+{
+    if (m_devTx) {
+        m_txBuf =  iio_device_create_buffer(m_devTx, size, cyclic ? '\1' : '\0');
+    } else {
+        m_txBuf = 0;
+    }
+
+    return m_txBuf;
+}
+
+void DevicePlutoSDRBox::deleteRxBuffer()
+{
+    if (m_rxBuf) {
+        iio_buffer_destroy(m_rxBuf);
+        m_rxBuf = 0;
+    }
+}
+
+void DevicePlutoSDRBox::deleteTxBuffer()
+{
+    if (m_txBuf) {
+        iio_buffer_destroy(m_txBuf);
+        m_txBuf = 0;
+    }
+}
+
+ssize_t DevicePlutoSDRBox::getRxSampleSize()
+{
+    if (m_devRx) {
+        return iio_device_get_sample_size(m_devRx);
+    } else {
+        return 0;
+    }
+}
+
+ssize_t DevicePlutoSDRBox::getTxSampleSize()
+{
+    if (m_devTx) {
+        return iio_device_get_sample_size(m_devTx);
+    } else {
+        return 0;
+    }
+}
+
+ssize_t DevicePlutoSDRBox::rxBufferRefill()
+{
+    if (m_rxBuf) {
+        return iio_buffer_refill(m_rxBuf);
+    } else {
+        return 0;
+    }
+}
+
+ssize_t DevicePlutoSDRBox::txBufferPush()
+{
+    if (m_txBuf) {
+        return iio_buffer_push(m_txBuf);
+    } else {
+        return 0;
+    }
+}
+
+std::ptrdiff_t DevicePlutoSDRBox::rxBufferStep()
+{
+    if (m_rxBuf) {
+        return iio_buffer_step(m_rxBuf);
+    } else {
+        return 0;
+    }
+}
+
+char* DevicePlutoSDRBox::rxBufferEnd()
+{
+    if (m_rxBuf) {
+        return (char *) iio_buffer_end(m_rxBuf);
+    } else {
+        return 0;
+    }
+}
+
+char* DevicePlutoSDRBox::rxBufferFirst()
+{
+    if (m_rxBuf) {
+        return (char *) iio_buffer_first(m_rxBuf, m_chnRx0);
+    } else {
+        return 0;
+    }
+}
+
+std::ptrdiff_t DevicePlutoSDRBox::txBufferStep()
+{
+    if (m_txBuf) {
+        return iio_buffer_step(m_txBuf);
+    } else {
+        return 0;
+    }
+}
+
+char* DevicePlutoSDRBox::txBufferEnd()
+{
+    if (m_txBuf) {
+        return (char *) iio_buffer_end(m_txBuf);
+    } else {
+        return 0;
+    }
+}
+
+char* DevicePlutoSDRBox::txBufferFirst()
+{
+    if (m_txBuf) {
+        return (char *) iio_buffer_first(m_txBuf, m_chnTx0);
+    } else {
+        return 0;
+    }
+}
+
+
+
+
+
diff --git a/devices/plutosdr/deviceplutosdrbox.h b/devices/plutosdr/deviceplutosdrbox.h
new file mode 100644
index 000000000..71c12ce3b
--- /dev/null
+++ b/devices/plutosdr/deviceplutosdrbox.h
@@ -0,0 +1,75 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 Edouard Griffiths, F4EXB                                   //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+//                                                                               //
+// 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 General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_
+#define DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_
+
+#include "deviceplutosdrscan.h"
+
+class DevicePlutoSDRBox
+{
+public:
+    typedef enum
+    {
+        DEVICE_PHY,
+        DEVICE_RX,
+        DEVICE_TX
+    } DeviceType;
+
+    struct Sample {
+        int16_t i;
+        int16_t q;
+    };
+
+    DevicePlutoSDRBox(const std::string& uri);
+    ~DevicePlutoSDRBox();
+    bool isValid() const { return m_valid; }
+
+    void set_params(DeviceType devType, const std::vector<std::string> &params);
+    bool openRx();
+    bool openTx();
+    void closeRx();
+    void closeTx();
+    struct iio_buffer *createRxBuffer(unsigned int size, bool cyclic);
+    struct iio_buffer *createTxBuffer(unsigned int size, bool cyclic);
+    void deleteRxBuffer();
+    void deleteTxBuffer();
+    ssize_t getRxSampleSize();
+    ssize_t getTxSampleSize();
+    struct iio_channel *getRxChannel0() { return m_chnRx0; }
+    struct iio_channel *getTxChannel0() { return m_chnTx0; }
+    ssize_t rxBufferRefill();
+    ssize_t txBufferPush();
+    std::ptrdiff_t rxBufferStep();
+    char* rxBufferEnd();
+    char* rxBufferFirst();
+    std::ptrdiff_t txBufferStep();
+    char* txBufferEnd();
+    char* txBufferFirst();
+
+private:
+    struct iio_context *m_ctx;
+    struct iio_device  *m_devPhy;
+    struct iio_device  *m_devRx;
+    struct iio_device  *m_devTx;
+    struct iio_channel *m_chnRx0;
+    struct iio_channel *m_chnTx0;
+    struct iio_buffer  *m_rxBuf;
+    struct iio_buffer  *m_txBuf;
+    bool m_valid;
+};
+
+#endif /* DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_ */
diff --git a/devices/plutosdr/deviceplutosdrscan.cpp b/devices/plutosdr/deviceplutosdrscan.cpp
new file mode 100644
index 000000000..e14d8e8e7
--- /dev/null
+++ b/devices/plutosdr/deviceplutosdrscan.cpp
@@ -0,0 +1,115 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 Edouard Griffiths, F4EXB                                   //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+//                                                                               //
+// 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 General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <iostream>
+#include <cstdio>
+#include <cstring>
+#include <regex>
+#include <iio.h>
+
+#include "deviceplutosdrscan.h"
+
+void DevicePlutoSDRScan::scan()
+{
+    int i, num_contexts;
+    struct iio_scan_context *scan_ctx;
+    struct iio_context_info **info;
+
+    scan_ctx = iio_create_scan_context(0, 0);
+
+    if (!scan_ctx)
+    {
+        std::cerr << "PlutoSDRScan::scan: could not create scan context" << std::endl;
+        return;
+    }
+
+    num_contexts = iio_scan_context_get_info_list(scan_ctx, &info);
+
+    if (num_contexts < 0)
+    {
+        std::cerr << "PlutoSDRScan::scan: could not get contexts" << std::endl;
+        return;
+    }
+
+    for (i = 0; i < num_contexts; i++)
+    {
+        const char *description = iio_context_info_get_description(info[i]);
+        const char *uri = iio_context_info_get_uri(info[i]);
+        printf("PlutoSDRScan::scan: %d: %s [%s]\n", i, description, uri);
+        char *pch = strstr(const_cast<char*>(description), "PlutoSDR");
+
+        if (pch)
+        {
+            m_scans.push_back({std::string(description), std::string("TBD"), std::string(uri)});
+            m_urilMap[m_scans.back().m_uri] = &m_scans.back();
+
+            std::regex desc_regex(".*serial=(.+)");
+            std::smatch desc_match;
+            std::regex_search(m_scans.back().m_name, desc_match, desc_regex);
+
+            if (desc_match.size() == 2)
+            {
+                m_scans.back().m_serial = desc_match[1];
+                m_serialMap[m_scans.back().m_serial] = &m_scans.back();
+            }
+        }
+    }
+
+    iio_context_info_list_free(info);
+    iio_scan_context_destroy(scan_ctx);
+}
+
+const std::string* DevicePlutoSDRScan::getURIAt(unsigned int index) const
+{
+    if (index < m_scans.size()) {
+        return &(m_scans[index].m_uri);
+    } else {
+        return 0;
+    }
+}
+
+const std::string* DevicePlutoSDRScan::getSerialAt(unsigned int index) const
+{
+    if (index < m_scans.size()) {
+        return &(m_scans[index].m_serial);
+    } else {
+        return 0;
+    }
+}
+
+const std::string* DevicePlutoSDRScan::getURIFromSerial(
+        const std::string& serial) const
+{
+    std::map<std::string, DeviceScan*>::const_iterator it = m_serialMap.find(serial);
+    if (it == m_serialMap.end()) {
+        return 0;
+    } else {
+        return &((it->second)->m_uri);
+    }
+}
+
+void DevicePlutoSDRScan::getSerials(std::vector<std::string>& serials) const
+{
+    std::vector<DeviceScan>::const_iterator it = m_scans.begin();
+    serials.clear();
+
+    for (; it != m_scans.end(); ++it) {
+        serials.push_back(it->m_serial);
+    }
+}
+
+
+
diff --git a/devices/plutosdr/deviceplutosdrscan.h b/devices/plutosdr/deviceplutosdrscan.h
new file mode 100644
index 000000000..20384abc9
--- /dev/null
+++ b/devices/plutosdr/deviceplutosdrscan.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 Edouard Griffiths, F4EXB                                   //
+//                                                                               //
+// This program is free software; you can redistribute it and/or modify          //
+// it under the terms of the GNU General Public License as published by          //
+// the Free Software Foundation as version 3 of the License, or                  //
+//                                                                               //
+// 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 General Public License V3 for more details.                               //
+//                                                                               //
+// You should have received a copy of the GNU General Public License             //
+// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_
+#define DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_
+
+class DevicePlutoSDRScan
+{
+public:
+    struct DeviceScan
+    {
+        std::string m_name;
+        std::string m_serial;
+        std::string m_uri;
+    };
+
+    void scan();
+    int getNbDevices() const { return m_scans.size(); }
+    const std::string* getURIAt(unsigned int index) const;
+    const std::string* getSerialAt(unsigned int index) const ;
+    const std::string* getURIFromSerial(const std::string& serial) const;
+    void getSerials(std::vector<std::string>& serials) const;
+
+private:
+    std::vector<DeviceScan> m_scans;
+    std::map<std::string, DeviceScan*> m_serialMap;
+    std::map<std::string, DeviceScan*> m_urilMap;
+};
+
+
+
+#endif /* DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_ */