pax_global_header00006660000000000000000000000064131376356120014521gustar00rootroot0000000000000052 comment=d07f43863b1ef79252f8029cfb5947220f21311d SoapyRemote-soapy-remote-0.4.2/000077500000000000000000000000001313763561200164155ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/.travis.yml000066400000000000000000000022741313763561200205330ustar00rootroot00000000000000######################################################################## ## Travis CI config for SoapyRemote ## ## * installs SoapySDR from source ## * confirms build and install ## * checks that drivers load ######################################################################## sudo: required dist: trusty language: cpp compiler: gcc env: global: - INSTALL_PREFIX=/usr/local - SOAPY_SDR_BRANCH=master matrix: - BUILD_TYPE=Debug - BUILD_TYPE=Release install: # install SoapySDR from source - git clone https://github.com/pothosware/SoapySDR.git - pushd SoapySDR - git checkout ${SOAPY_SDR_BRANCH} - mkdir build && cd build - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_PYTHON=OFF -DENABLE_PYTHON3=OFF - make && sudo make install - popd script: - mkdir build && cd build - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} - make && sudo make install # print info about the install - export LD_LIBRARY_PATH=${INSTALL_PREFIX}/lib:${LD_LIBRARY_PATH} - export PATH=${INSTALL_PREFIX}/bin:${PATH} - SoapySDRUtil --info - SoapySDRUtil --check=remote - SoapySDRServer --help SoapyRemote-soapy-remote-0.4.2/CMakeLists.txt000066400000000000000000000026741313763561200211660ustar00rootroot00000000000000######################################################################## # Build Soapy SDR remote support ######################################################################## cmake_minimum_required(VERSION 2.8.7) project(SoapyRemote CXX C) # select build type to get optimization flags if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") message(STATUS "Build type not specified: defaulting to release.") endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") #find soapy sdr find_package(SoapySDR "0.4.0" NO_MODULE) #enable c++11 features if(CMAKE_COMPILER_IS_GNUCXX) #C++11 is a required language feature for this project include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11) if(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() #Thread support enabled (not the same as -lpthread) list(APPEND SoapySDR_LIBRARIES -pthread) endif(CMAKE_COMPILER_IS_GNUCXX) #enable c++11 extensions for OSX if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wc++11-extensions") endif(APPLE) #common headers used by client and server include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common) add_subdirectory(common) add_subdirectory(client) add_subdirectory(server) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_subdirectory(system) endif() SoapyRemote-soapy-remote-0.4.2/Changelog.txt000066400000000000000000000053731313763561200210550ustar00rootroot00000000000000Release 0.4.2 (2017-07-31) ========================== - Fixed timeout problem in log acceptor receiver loop - Added server check for slow calls and longer timeout - This also resolves timeout errors for lengthy calls - Added network-online.target for systemd service script Release 0.4.1 (2017-06-08) ========================== - Added timeout for logger connect and control unpacker - Fixed error log formatting in the client log acceptor Release 0.4.0 (2017-04-29) ========================== - Added support for frequency corrections for fine adjustments - Added support forgetSampleRateRange() for continuous ranges - Added support for CF32 local format with CS12 remote format - Added support for CS16 local format with CS12 remote format - Added support for CS16 local format with CS8 remote format - readStream thread support for SOAPY_SDR_END_ABRUPT flag - readStream thread apply second SOAPY_SDR_END_BURST flag - Added timeout to socket connect and factory calls - Support range with step size (backwards compatible) - Added tcp support for streaming via remote:prot=tcp - Support for bulk register read/write interface APIs Release 0.3.2 (2016-12-04) ========================== - Added man page for SoapySDRServer executable Release 0.3.1 (2016-09-01) ========================== - Fixed info print for setupStream() - status socket connection - Added sysctl config file for increased default network limits - Added IP version preferences to SSDP discovery (remote:ipver=4) - Added systemd service script for SoapySDRServer - Update debian files for SoapySDR module ABI format - Preserve packet+burst boundaries in server readStream() endpoint Release 0.3.0 (2016-07-10) ========================== - Support for named register interface API - Support for getChannelInfo() API call - Moved time source calls to time API section - Support for getBandwidthRange() API call - Support for channel-specific settings API Release 0.2.1 (2016-04-21) ========================== - Fixed element send size when using multi-channel streams - Fixed bug in SoapyRPCUnpacker for std::vector - Fix use of error code in reportError() with strerror_r Release 0.2.0 (2015-11-21) ========================== - Use formatToSize() from SoapySDR library - Support CU8 native conversion to CF32 - Implement SSDP for automatic server discovery - Use native format for automatic selection - Support API to query tune argument info - Support API to query sensors meta info - Support API to query setting argument info - Support API to query stream argument info - Support API to query stream format types - Support API call to query clock rates - Support API call to query AGC mode Release 0.1.0 (2015-10-10) ========================== - First release of SoapyRemote plugin module and server application SoapyRemote-soapy-remote-0.4.2/LICENSE_1_0.txt000066400000000000000000000024721313763561200207040ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SoapyRemote-soapy-remote-0.4.2/README.md000066400000000000000000000010341313763561200176720ustar00rootroot00000000000000# Use any Soapy SDR remotely ## Build Status - Travis: [![Travis Build Status](https://travis-ci.org/pothosware/SoapyRemote.svg?branch=master)](https://travis-ci.org/pothosware/SoapyRemote) ## Dependencies * SoapySDR - https://github.com/pothosware/SoapySDR/wiki ## Documentation * https://github.com/pothosware/SoapyRemote/wiki ## Licensing information Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) SoapyRemote-soapy-remote-0.4.2/client/000077500000000000000000000000001313763561200176735ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/client/CMakeLists.txt000066400000000000000000000006251313763561200224360ustar00rootroot00000000000000######################################################################## # Build the client plugin module ######################################################################## SOAPY_SDR_MODULE_UTIL( TARGET remoteSupport SOURCES Registration.cpp Settings.cpp Streaming.cpp LogAcceptor.cpp ClientStreamData.cpp LIBRARIES SoapySDRRemoteCommon ) SoapyRemote-soapy-remote-0.4.2/client/ClientStreamData.cpp000066400000000000000000000171371313763561200235740ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "ClientStreamData.hpp" #include "SoapyStreamEndpoint.hpp" #include //memcpy #include #include ClientStreamData::ClientStreamData(void): streamId(-1), endpoint(nullptr), readHandle(0), readElemsLeft(0), scaleFactor(0.0), convertType(CONVERT_MEMCPY) { return; } void ClientStreamData::convertRecvBuffs(void * const *buffs, const size_t numElems) { assert(endpoint != nullptr); assert(endpoint->getElemSize() != 0); assert(endpoint->getNumChans() != 0); assert(not recvBuffs.empty()); switch (convertType) { /////////////////////////// case CONVERT_MEMCPY: /////////////////////////// { size_t elemSize = endpoint->getElemSize(); for (size_t i = 0; i < recvBuffs.size(); i++) { std::memcpy(buffs[i], recvBuffs[i], numElems*elemSize); } } break; /////////////////////////// case CONVERT_CF32_CS16: /////////////////////////// { const float scale = float(1.0/scaleFactor); for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (short *)recvBuffs[i]; auto out = (float *)buffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = float(in[j])*scale; } } } break; /////////////////////////// case CONVERT_CF32_CS12: /////////////////////////// { const float scale = float(1.0/scaleFactor); for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (uint8_t *)recvBuffs[i]; auto out = (float *)buffs[i]; for (size_t j = 0; j < numElems; j++) { uint16_t part0 = uint16_t(*(in++)); uint16_t part1 = uint16_t(*(in++)); uint16_t part2 = uint16_t(*(in++)); int16_t i = int16_t((part1 << 12) | (part0 << 4)); int16_t q = int16_t((part2 << 8) | (part1 & 0xf0)); *(out++) = float(i)*scale; *(out++) = float(q)*scale; } } } break; /////////////////////////// case CONVERT_CS16_CS12: /////////////////////////// { for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (uint8_t *)recvBuffs[i]; auto out = (int16_t *)buffs[i]; for (size_t j = 0; j < numElems; j++) { uint16_t part0 = uint16_t(*(in++)); uint16_t part1 = uint16_t(*(in++)); uint16_t part2 = uint16_t(*(in++)); *(out++) = int16_t((part1 << 12) | (part0 << 4)); *(out++) = int16_t((part2 << 8) | (part1 & 0xf0)); } } } break; /////////////////////////// case CONVERT_CS16_CS8: /////////////////////////// { for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (int8_t *)recvBuffs[i]; auto out = (int16_t *)buffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = int16_t(in[j]); } } } break; /////////////////////////// case CONVERT_CF32_CS8: /////////////////////////// { const float scale = float(1.0/scaleFactor); for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (int8_t *)recvBuffs[i]; auto out = (float *)buffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = float(in[j])*scale; } } } break; /////////////////////////// case CONVERT_CF32_CU8: /////////////////////////// { const float scale = float(1.0/scaleFactor); for (size_t i = 0; i < recvBuffs.size(); i++) { auto in = (int8_t *)recvBuffs[i]; auto out = (float *)buffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = float(in[j]-127)*scale; } } } break; } } void ClientStreamData::convertSendBuffs(const void * const *buffs, const size_t numElems) { assert(endpoint != nullptr); assert(endpoint->getElemSize() != 0); assert(endpoint->getNumChans() != 0); assert(not sendBuffs.empty()); switch (convertType) { /////////////////////////// case CONVERT_MEMCPY: /////////////////////////// { size_t elemSize = endpoint->getElemSize(); for (size_t i = 0; i < sendBuffs.size(); i++) { std::memcpy(sendBuffs[i], buffs[i], numElems*elemSize); } } break; /////////////////////////// case CONVERT_CF32_CS16: /////////////////////////// { float scale = float(scaleFactor); for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (float *)buffs[i]; auto out = (short *)sendBuffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = short(in[j]*scale); } } } break; /////////////////////////// case CONVERT_CF32_CS12: /////////////////////////// { const float scale = float(scaleFactor); for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (float *)buffs[i]; auto out = (uint8_t *)sendBuffs[i]; for (size_t j = 0; j < numElems; j++) { uint16_t i = uint16_t(*(in++)*scale); uint16_t q = uint16_t(*(in++)*scale); *(out++) = uint8_t(i >> 4); *(out++) = uint8_t((q & 0xf0)|(i >> 12)); *(out++) = uint8_t(q >> 8); } } } break; /////////////////////////// case CONVERT_CS16_CS12: /////////////////////////// { for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (int16_t *)buffs[i]; auto out = (uint8_t *)sendBuffs[i]; for (size_t j = 0; j < numElems; j++) { uint16_t i = uint16_t(*(in++)); uint16_t q = uint16_t(*(in++)); *(out++) = uint8_t(i >> 4); *(out++) = uint8_t((q & 0xf0)|(i >> 12)); *(out++) = uint8_t(q >> 8); } } } break; /////////////////////////// case CONVERT_CS16_CS8: /////////////////////////// { for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (int16_t *)buffs[i]; auto out = (int8_t *)sendBuffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = int8_t(in[j]); } } } break; /////////////////////////// case CONVERT_CF32_CS8: /////////////////////////// { float scale = float(scaleFactor); for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (float *)buffs[i]; auto out = (int8_t *)sendBuffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = int8_t(in[j]*scale); } } } break; /////////////////////////// case CONVERT_CF32_CU8: /////////////////////////// { float scale = float(scaleFactor); for (size_t i = 0; i < sendBuffs.size(); i++) { auto in = (float *)buffs[i]; auto out = (int8_t *)sendBuffs[i]; for (size_t j = 0; j < numElems*2; j++) { out[j] = int8_t(in[j]*scale) + 127; } } } break; } } SoapyRemote-soapy-remote-0.4.2/client/ClientStreamData.hpp000066400000000000000000000023331313763561200235710ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRPCSocket.hpp" #include #include class SoapyStreamEndpoint; enum ConvertTypes { CONVERT_MEMCPY, CONVERT_CF32_CS16, CONVERT_CF32_CS12, CONVERT_CS16_CS12, CONVERT_CS16_CS8, CONVERT_CF32_CS8, CONVERT_CF32_CU8, }; struct ClientStreamData { ClientStreamData(void); //string formats in use std::string localFormat; std::string remoteFormat; //this ID identifies the stream to the remote host int streamId; //datagram socket for stream endpoint SoapyRPCSocket streamSock; //datagram socket for status endpoint SoapyRPCSocket statusSock; //local side of the stream endpoint SoapyStreamEndpoint *endpoint; //buffer pointers to read/write API std::vector recvBuffs; std::vector sendBuffs; //read stream remainder tracking size_t readHandle; size_t readElemsLeft; //converter implementations double scaleFactor; ConvertTypes convertType; void convertRecvBuffs(void * const *buffs, const size_t numElems); void convertSendBuffs(const void * const *buffs, const size_t numElems); }; SoapyRemote-soapy-remote-0.4.2/client/LogAcceptor.cpp000066400000000000000000000112361313763561200226040ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "LogAcceptor.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyRPCPacker.hpp" #include "SoapyRPCUnpacker.hpp" #include #include //sig_atomic_t #include #include #include #include /*********************************************************************** * Log acceptor thread implementation **********************************************************************/ struct LogAcceptorThreadData { LogAcceptorThreadData(void): done(true), thread(nullptr), useCount(0) { return; } ~LogAcceptorThreadData(void) { this->shutdown(); } void activate(void); void shutdown(void); void handlerLoop(void); SoapyRPCSocket client; std::string url; sig_atomic_t done; std::thread *thread; sig_atomic_t useCount; }; void LogAcceptorThreadData::activate(void) { client = SoapyRPCSocket(); //specify a timeout on connect because the link may be lost //when the thread attempts to re-establish a connection int ret = client.connect(url, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyLogAcceptor::connect() FAIL: %s", client.lastErrorMsg()); done = true; return; } try { //startup forwarding SoapyRPCPacker packerStart(client); packerStart & SOAPY_REMOTE_START_LOG_FORWARDING; packerStart(); SoapyRPCUnpacker unpackerStart(client); done = false; thread = new std::thread(&LogAcceptorThreadData::handlerLoop, this); } catch (const std::exception &ex) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyLogAcceptor::activate() FAIL: %s", ex.what()); done = true; } } void LogAcceptorThreadData::shutdown(void) { try { //shutdown forwarding (ignore reply) SoapyRPCPacker packerStop(client); packerStop & SOAPY_REMOTE_STOP_LOG_FORWARDING; packerStop(); //graceful disconnect (ignore reply) SoapyRPCPacker packerHangup(client); packerHangup & SOAPY_REMOTE_HANGUP; packerHangup(); } catch (const std::exception &ex) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyLogAcceptor::shutdown() FAIL: %s", ex.what()); } //the thread will exit due to the requests above assert(thread != nullptr); thread->join(); delete thread; done = true; client = SoapyRPCSocket(); } void LogAcceptorThreadData::handlerLoop(void) { try { //loop while active to relay messages to logger while (true) { SoapyRPCUnpacker unpackerLogMsg(client, true, -1/*no timeout*/); if (unpackerLogMsg.done()) break; //got stop reply char logLevel = 0; std::string message; unpackerLogMsg & logLevel; unpackerLogMsg & message; SoapySDR::log(SoapySDR::LogLevel(logLevel), message); } } catch (const std::exception &ex) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyLogAcceptor::handlerLoop() FAIL: %s", ex.what()); } done = true; } /*********************************************************************** * log acceptor threads and subscription tracking **********************************************************************/ static std::mutex logMutex; //unique server id to thread data static std::map handlers; //cleanup completed and restart on errors static void threadMaintenance(void) { auto it = handlers.begin(); while (it != handlers.end()) { auto &data = it->second; //first time, or error occurred if (data.done) data.activate(); //no subscribers, erase if (data.useCount == 0) handlers.erase(it++); //next element else ++it; } } /*********************************************************************** * client subscription hooks **********************************************************************/ SoapyLogAcceptor::SoapyLogAcceptor(const std::string &url, SoapyRPCSocket &sock) { SoapyRPCPacker packer(sock); packer & SOAPY_REMOTE_GET_SERVER_ID; packer(); SoapyRPCUnpacker unpacker(sock); unpacker & _serverId; std::lock_guard lock(logMutex); auto &data = handlers[_serverId]; data.useCount++; data.url = url; threadMaintenance(); } SoapyLogAcceptor::~SoapyLogAcceptor(void) { std::lock_guard lock(logMutex); auto &data = handlers.at(_serverId); data.useCount--; threadMaintenance(); } SoapyRemote-soapy-remote-0.4.2/client/LogAcceptor.hpp000066400000000000000000000007131313763561200226070ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include class SoapyRPCSocket; /*! * Create a log acceptor to subscribe to log events from the remote server. * The acceptor avoids redundant threads by reference counting subscribers. */ class SoapyLogAcceptor { public: SoapyLogAcceptor(const std::string &url, SoapyRPCSocket &sock); ~SoapyLogAcceptor(void); private: std::string _serverId; }; SoapyRemote-soapy-remote-0.4.2/client/Registration.cpp000066400000000000000000000142741313763561200230610ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapyClient.hpp" #include "LogAcceptor.hpp" #include "SoapySSDPEndpoint.hpp" #include "SoapyURLUtils.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCPacker.hpp" #include "SoapyRPCUnpacker.hpp" #include #include #include #include /*********************************************************************** * Args translator for nested keywords **********************************************************************/ static SoapySDR::Kwargs translateArgs(const SoapySDR::Kwargs &args) { SoapySDR::Kwargs argsOut; //stop infinite loops with special keyword argsOut[SOAPY_REMOTE_KWARG_STOP] = ""; //copy all non-remote keys for (auto &pair : args) { if (pair.first == "driver") continue; //don't propagate local driver filter if (pair.first == "type") continue; //don't propagate local sub-type filter if (pair.first.find(SOAPY_REMOTE_KWARG_PREFIX) == std::string::npos) { argsOut[pair.first] = pair.second; } } //write all remote keys with prefix stripped for (auto &pair : args) { if (pair.first.find(SOAPY_REMOTE_KWARG_PREFIX) == 0) { static const size_t offset = std::string(SOAPY_REMOTE_KWARG_PREFIX).size(); argsOut[pair.first.substr(offset)] = pair.second; } } return argsOut; } /*********************************************************************** * Discovery routine -- connect to server when key specified **********************************************************************/ static std::vector findRemote(const SoapySDR::Kwargs &args) { std::vector result; if (args.count(SOAPY_REMOTE_KWARG_STOP) != 0) return result; //no remote specified, use the discovery protocol if (args.count("remote") == 0) { //On non-windows platforms the endpoint instance can last the //duration of the process because it can be cleaned up safely. //Windows has issues cleaning up threads and sockets on exit. #ifndef _MSC_VER static #endif //_MSC_VER auto ssdpEndpoint = SoapySSDPEndpoint::getInstance(); //enable forces new search queries ssdpEndpoint->enablePeriodicSearch(true); //wait maximum timeout for replies std::this_thread::sleep_for(std::chrono::microseconds(SOAPY_REMOTE_SOCKET_TIMEOUT_US)); //determine IP version preferences int ipVer(4); const auto ipVerIt = args.find("remote:ipver"); if (ipVerIt != args.end()) ipVer = std::stoi(ipVerIt->second); //spawn futures to connect to each remote std::vector> futures; for (const auto &url : SoapySSDPEndpoint::getInstance()->getServerURLs(ipVer)) { auto argsWithURL = args; argsWithURL["remote"] = url; futures.push_back(std::async(std::launch::async, &findRemote, argsWithURL)); } //wait on all futures for results for (auto &future : futures) { const auto subResult = future.get(); result.insert(result.end(), subResult.begin(), subResult.end()); } return result; } //otherwise connect to a specific url and enumerate auto url = SoapyURL(args.at("remote")); //default url parameters when not specified if (url.getScheme().empty()) url.setScheme("tcp"); if (url.getService().empty()) url.setService(SOAPY_REMOTE_DEFAULT_SERVICE); //try to connect to the remote server SoapySocketSession sess; SoapyRPCSocket s; int ret = s.connect(url.toString(), SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyRemote::find() -- connect(%s) FAIL: %s", url.toString().c_str(), s.lastErrorMsg()); return result; } //find transaction try { SoapyLogAcceptor logAcceptor(url.toString(), s); SoapyRPCPacker packer(s); packer & SOAPY_REMOTE_FIND; packer & translateArgs(args); packer(); SoapyRPCUnpacker unpacker(s); unpacker & result; //graceful disconnect SoapyRPCPacker packerHangup(s); packerHangup & SOAPY_REMOTE_HANGUP; packerHangup(); SoapyRPCUnpacker unpackerHangup(s); } catch (const std::exception &ex) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyRemote::find() -- transact FAIL: %s", ex.what()); } //remove instances of the stop key from the result for (auto &resultArgs : result) { resultArgs.erase(SOAPY_REMOTE_KWARG_STOP); if (resultArgs.count("driver") != 0) { resultArgs["remote:driver"] = resultArgs.at("driver"); resultArgs.erase("driver"); } if (resultArgs.count("type") != 0) { resultArgs["remote:type"] = resultArgs.at("type"); resultArgs.erase("type"); } resultArgs["remote"] = url.toString(); } return result; } /*********************************************************************** * Factory routine -- connect to server and create remote device **********************************************************************/ static SoapySDR::Device *makeRemote(const SoapySDR::Kwargs &args) { if (args.count(SOAPY_REMOTE_KWARG_STOP) != 0) //probably wont happen { throw std::runtime_error("SoapyRemoteDevice() -- factory loop"); } if (args.count("remote") == 0) { throw std::runtime_error("SoapyRemoteDevice() -- missing URL"); } auto url = SoapyURL(args.at("remote")); //default url parameters when not specified if (url.getScheme().empty()) url.setScheme("tcp"); if (url.getService().empty()) url.setService(SOAPY_REMOTE_DEFAULT_SERVICE); return new SoapyRemoteDevice(url.toString(), translateArgs(args)); } /*********************************************************************** * Registration **********************************************************************/ static SoapySDR::Registry registerRemote("remote", &findRemote, &makeRemote, SOAPY_SDR_ABI_VERSION); SoapyRemote-soapy-remote-0.4.2/client/Settings.cpp000066400000000000000000001150231313763561200222010ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0 #include "SoapyClient.hpp" #include "LogAcceptor.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCPacker.hpp" #include "SoapyRPCUnpacker.hpp" #include #include /******************************************************************* * Constructor ******************************************************************/ SoapyRemoteDevice::SoapyRemoteDevice(const std::string &url, const SoapySDR::Kwargs &args): _logAcceptor(nullptr), _defaultStreamProt("udp") { //try to connect to the remote server int ret = _sock.connect(url, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret != 0) { throw std::runtime_error("SoapyRemoteDevice("+url+") -- connect FAIL: " + _sock.lastErrorMsg()); } //connect the log acceptor _logAcceptor = new SoapyLogAcceptor(url, _sock); //acquire device instance SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_MAKE; packer & args; packer(); SoapyRPCUnpacker unpacker(_sock); //default stream protocol specified in device args const auto protIt = args.find("prot"); if (protIt != args.end()) _defaultStreamProt = protIt->second; } SoapyRemoteDevice::~SoapyRemoteDevice(void) { //cant throw in the destructor try { //release device instance SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_UNMAKE; packer(); SoapyRPCUnpacker unpacker(_sock); //graceful disconnect SoapyRPCPacker packerHangup(_sock); packerHangup & SOAPY_REMOTE_HANGUP; packerHangup(); SoapyRPCUnpacker unpackerHangup(_sock); } catch (const std::exception &ex) { SoapySDR::logf(SOAPY_SDR_ERROR, "~SoapyRemoteDevice() FAIL: %s", ex.what()); } //disconnect the log acceptor (does not throw) delete _logAcceptor; } /******************************************************************* * Identification API ******************************************************************/ std::string SoapyRemoteDevice::getDriverKey(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_DRIVER_KEY; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } std::string SoapyRemoteDevice::getHardwareKey(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_HARDWARE_KEY; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } SoapySDR::Kwargs SoapyRemoteDevice::getHardwareInfo(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_HARDWARE_INFO; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::Kwargs result; unpacker & result; return result; } /******************************************************************* * Channels API ******************************************************************/ void SoapyRemoteDevice::setFrontendMapping(const int direction, const std::string &mapping) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_FRONTEND_MAPPING; packer & char(direction); packer & mapping; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::getFrontendMapping(const int direction) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FRONTEND_MAPPING; packer & char(direction); packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } size_t SoapyRemoteDevice::getNumChannels(const int direction) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_NUM_CHANNELS; packer & char(direction); packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return result; } SoapySDR::Kwargs SoapyRemoteDevice::getChannelInfo(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_CHANNEL_INFO; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::Kwargs result; unpacker & result; return result; } bool SoapyRemoteDevice::getFullDuplex(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FULL_DUPLEX; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } /******************************************************************* * Antenna API ******************************************************************/ std::vector SoapyRemoteDevice::listAntennas(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_ANTENNAS; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::setAntenna(const int direction, const size_t channel, const std::string &name) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_ANTENNA; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::getAntenna(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_ANTENNA; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } /******************************************************************* * Frontend corrections API ******************************************************************/ bool SoapyRemoteDevice::hasDCOffsetMode(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_DC_OFFSET_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setDCOffsetMode(const int direction, const size_t channel, const bool automatic) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_DC_OFFSET_MODE; packer & char(direction); packer & int(channel); packer & automatic; packer(); SoapyRPCUnpacker unpacker(_sock); } bool SoapyRemoteDevice::getDCOffsetMode(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_DC_OFFSET_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } bool SoapyRemoteDevice::hasDCOffset(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_DC_OFFSET; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setDCOffset(const int direction, const size_t channel, const std::complex &offset) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_DC_OFFSET; packer & char(direction); packer & int(channel); packer & offset; packer(); SoapyRPCUnpacker unpacker(_sock); } std::complex SoapyRemoteDevice::getDCOffset(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_DC_OFFSET; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::complex result; unpacker & result; return result; } bool SoapyRemoteDevice::hasIQBalance(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_IQ_BALANCE_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setIQBalance(const int direction, const size_t channel, const std::complex &balance) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_IQ_BALANCE_MODE; packer & char(direction); packer & int(channel); packer & balance; packer(); SoapyRPCUnpacker unpacker(_sock); } std::complex SoapyRemoteDevice::getIQBalance(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_IQ_BALANCE_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::complex result; unpacker & result; return result; } bool SoapyRemoteDevice::hasFrequencyCorrection(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_FREQUENCY_CORRECTION; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setFrequencyCorrection(const int direction, const size_t channel, const double value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_FREQUENCY_CORRECTION; packer & char(direction); packer & int(channel); packer & value; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getFrequencyCorrection(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY_CORRECTION; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } /******************************************************************* * Gain API ******************************************************************/ std::vector SoapyRemoteDevice::listGains(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_GAINS; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } bool SoapyRemoteDevice::hasGainMode(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_GAIN_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setGainMode(const int direction, const size_t channel, const bool automatic) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_GAIN_MODE; packer & char(direction); packer & int(channel); packer & automatic; packer(); SoapyRPCUnpacker unpacker(_sock); } bool SoapyRemoteDevice::getGainMode(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_GAIN_MODE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } void SoapyRemoteDevice::setGain(const int direction, const size_t channel, const double value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_GAIN; packer & char(direction); packer & int(channel); packer & value; packer(); SoapyRPCUnpacker unpacker(_sock); } void SoapyRemoteDevice::setGain(const int direction, const size_t channel, const std::string &name, const double value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_GAIN_ELEMENT; packer & char(direction); packer & int(channel); packer & name; packer & value; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getGain(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_GAIN; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } double SoapyRemoteDevice::getGain(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_GAIN_ELEMENT; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } SoapySDR::Range SoapyRemoteDevice::getGainRange(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_GAIN_RANGE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::Range result; unpacker & result; return result; } SoapySDR::Range SoapyRemoteDevice::getGainRange(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_GAIN_RANGE_ELEMENT; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::Range result; unpacker & result; return result; } /******************************************************************* * Frequency API ******************************************************************/ void SoapyRemoteDevice::setFrequency(const int direction, const size_t channel, const double frequency, const SoapySDR::Kwargs &args) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_FREQUENCY; packer & char(direction); packer & int(channel); packer & frequency; packer & args; packer(); SoapyRPCUnpacker unpacker(_sock); } void SoapyRemoteDevice::setFrequency(const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_FREQUENCY_COMPONENT; packer & char(direction); packer & int(channel); packer & name; packer & frequency; packer & args; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getFrequency(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } double SoapyRemoteDevice::getFrequency(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY_COMPONENT; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } std::vector SoapyRemoteDevice::listFrequencies(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_FREQUENCIES; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } SoapySDR::RangeList SoapyRemoteDevice::getFrequencyRange(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY_RANGE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::RangeList result; unpacker & result; return result; } SoapySDR::RangeList SoapyRemoteDevice::getFrequencyRange(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY_RANGE_COMPONENT; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::RangeList result; unpacker & result; return result; } SoapySDR::ArgInfoList SoapyRemoteDevice::getFrequencyArgsInfo(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_FREQUENCY_ARGS_INFO; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::ArgInfoList result; unpacker & result; return result; } /******************************************************************* * Sample Rate API ******************************************************************/ void SoapyRemoteDevice::setSampleRate(const int direction, const size_t channel, const double rate) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_SAMPLE_RATE; packer & char(direction); packer & int(channel); packer & rate; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getSampleRate(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_SAMPLE_RATE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } std::vector SoapyRemoteDevice::listSampleRates(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_SAMPLE_RATES; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } SoapySDR::RangeList SoapyRemoteDevice::getSampleRateRange(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_SAMPLE_RATE_RANGE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::RangeList result; unpacker & result; return result; } /******************************************************************* * Bandwidth API ******************************************************************/ void SoapyRemoteDevice::setBandwidth(const int direction, const size_t channel, const double bw) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_BANDWIDTH; packer & char(direction); packer & int(channel); packer & bw; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getBandwidth(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_BANDWIDTH; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } std::vector SoapyRemoteDevice::listBandwidths(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_BANDWIDTHS; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } SoapySDR::RangeList SoapyRemoteDevice::getBandwidthRange(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_BANDWIDTH_RANGE; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::RangeList result; unpacker & result; return result; } /******************************************************************* * Clocking API ******************************************************************/ void SoapyRemoteDevice::setMasterClockRate(const double rate) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_MASTER_CLOCK_RATE; packer & rate; packer(); SoapyRPCUnpacker unpacker(_sock); } double SoapyRemoteDevice::getMasterClockRate(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_MASTER_CLOCK_RATE; packer(); SoapyRPCUnpacker unpacker(_sock); double result; unpacker & result; return result; } SoapySDR::RangeList SoapyRemoteDevice::getMasterClockRates(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_MASTER_CLOCK_RATES; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::RangeList result; unpacker & result; return result; } std::vector SoapyRemoteDevice::listClockSources(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_CLOCK_SOURCES; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::setClockSource(const std::string &source) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_CLOCK_SOURCE; packer & source; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::getClockSource(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_CLOCK_SOURCE; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } /******************************************************************* * Time API ******************************************************************/ std::vector SoapyRemoteDevice::listTimeSources(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_TIME_SOURCES; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::setTimeSource(const std::string &source) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_TIME_SOURCE; packer & source; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::getTimeSource(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_TIME_SOURCE; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } bool SoapyRemoteDevice::hasHardwareTime(const std::string &what) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_HAS_HARDWARE_TIME; packer & what; packer(); SoapyRPCUnpacker unpacker(_sock); bool result; unpacker & result; return result; } long long SoapyRemoteDevice::getHardwareTime(const std::string &what) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_HARDWARE_TIME; packer & what; packer(); SoapyRPCUnpacker unpacker(_sock); long long result; unpacker & result; return result; } void SoapyRemoteDevice::setHardwareTime(const long long timeNs, const std::string &what) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_HARDWARE_TIME; packer & timeNs; packer & what; packer(); SoapyRPCUnpacker unpacker(_sock); } void SoapyRemoteDevice::setCommandTime(const long long timeNs, const std::string &what) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SET_COMMAND_TIME; packer & timeNs; packer & what; packer(); SoapyRPCUnpacker unpacker(_sock); } /******************************************************************* * Sensor API ******************************************************************/ std::vector SoapyRemoteDevice::listSensors(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_SENSORS; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } SoapySDR::ArgInfo SoapyRemoteDevice::getSensorInfo(const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_SENSOR_INFO; packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::ArgInfo result; unpacker & result; return result; } std::string SoapyRemoteDevice::readSensor(const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_SENSOR; packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } std::vector SoapyRemoteDevice::listSensors(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_CHANNEL_SENSORS; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } SoapySDR::ArgInfo SoapyRemoteDevice::getSensorInfo(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_CHANNEL_SENSOR_INFO; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::ArgInfo result; unpacker & result; return result; } std::string SoapyRemoteDevice::readSensor(const int direction, const size_t channel, const std::string &name) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_CHANNEL_SENSOR; packer & char(direction); packer & int(channel); packer & name; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } /******************************************************************* * Register API ******************************************************************/ std::vector SoapyRemoteDevice::listRegisterInterfaces(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_REGISTER_INTERFACES; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::writeRegister(const std::string &name, const unsigned addr, const unsigned value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_REGISTER_NAMED; packer & name; packer & int(addr); packer & int(value); packer(); SoapyRPCUnpacker unpacker(_sock); } unsigned SoapyRemoteDevice::readRegister(const std::string &name, const unsigned addr) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_REGISTER_NAMED; packer & name; packer & int(addr); packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return unsigned(result); } void SoapyRemoteDevice::writeRegister(const unsigned addr, const unsigned value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_REGISTER; packer & int(addr); packer & int(value); packer(); SoapyRPCUnpacker unpacker(_sock); } unsigned SoapyRemoteDevice::readRegister(const unsigned addr) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_REGISTER; packer & int(addr); packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return unsigned(result); } void SoapyRemoteDevice::writeRegisters(const std::string &name, const unsigned addr, const std::vector &value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); std::vector val (value.begin(), value.end()); packer & SOAPY_REMOTE_WRITE_REGISTERS; packer & name; packer & int(addr); packer & val; packer(); SoapyRPCUnpacker unpacker(_sock); } std::vector SoapyRemoteDevice::readRegisters(const std::string &name, const unsigned addr, const size_t length) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_REGISTERS; packer & name; packer & int(addr); packer & int(length); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; std::vector res (result.begin(), result.end()); return res; } /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList SoapyRemoteDevice::getSettingInfo(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_SETTING_INFO; packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::ArgInfoList result; unpacker & result; return result; } void SoapyRemoteDevice::writeSetting(const std::string &key, const std::string &value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_SETTING; packer & key; packer & value; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::readSetting(const std::string &key) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_SETTING; packer & key; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } SoapySDR::ArgInfoList SoapyRemoteDevice::getSettingInfo(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_CHANNEL_SETTING_INFO; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); SoapySDR::ArgInfoList result; unpacker & result; return result; } void SoapyRemoteDevice::writeSetting(const int direction, const size_t channel, const std::string &key, const std::string &value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_CHANNEL_SETTING; packer & char(direction); packer & int(channel); packer & key; packer & value; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::readSetting(const int direction, const size_t channel, const std::string &key) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_CHANNEL_SETTING; packer & char(direction); packer & int(channel); packer & key; packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } /******************************************************************* * GPIO API ******************************************************************/ std::vector SoapyRemoteDevice::listGPIOBanks(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_GPIO_BANKS; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::writeGPIO(const std::string &bank, const unsigned value) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_GPIO; packer & bank; packer & int(value); packer(); SoapyRPCUnpacker unpacker(_sock); } void SoapyRemoteDevice::writeGPIO(const std::string &bank, const unsigned value, const unsigned mask) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_GPIO_MASKED; packer & bank; packer & int(value); packer & int(mask); packer(); SoapyRPCUnpacker unpacker(_sock); } unsigned SoapyRemoteDevice::readGPIO(const std::string &bank) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_GPIO; packer & bank; packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return unsigned(result); } void SoapyRemoteDevice::writeGPIODir(const std::string &bank, const unsigned dir) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_GPIO_DIR; packer & bank; packer & int(dir); packer(); SoapyRPCUnpacker unpacker(_sock); } void SoapyRemoteDevice::writeGPIODir(const std::string &bank, const unsigned dir, const unsigned mask) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_GPIO_DIR_MASKED; packer & bank; packer & int(dir); packer & int(mask); packer(); SoapyRPCUnpacker unpacker(_sock); } unsigned SoapyRemoteDevice::readGPIODir(const std::string &bank) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_GPIO_DIR; packer & bank; packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return unsigned(result); } /******************************************************************* * I2C API ******************************************************************/ void SoapyRemoteDevice::writeI2C(const int addr, const std::string &data) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_I2C; packer & int(addr); packer & data; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::readI2C(const int addr, const size_t numBytes) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_I2C; packer & int(addr); packer & int(numBytes); packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } /******************************************************************* * SPI API ******************************************************************/ unsigned SoapyRemoteDevice::transactSPI(const int addr, const unsigned data, const size_t numBits) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_TRANSACT_SPI; packer & int(addr); packer & int(data); packer & int(numBits); packer(); SoapyRPCUnpacker unpacker(_sock); int result; unpacker & result; return unsigned(result); } /******************************************************************* * UART API ******************************************************************/ std::vector SoapyRemoteDevice::listUARTs(void) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_LIST_UARTS; packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } void SoapyRemoteDevice::writeUART(const std::string &which, const std::string &data) { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_WRITE_UART; packer & which; packer & data; packer(); SoapyRPCUnpacker unpacker(_sock); } std::string SoapyRemoteDevice::readUART(const std::string &which, const long timeoutUs) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_READ_UART; packer & which; packer & int(timeoutUs); packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; return result; } SoapyRemote-soapy-remote-0.4.2/client/SoapyClient.hpp000066400000000000000000000322431313763561200226420ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRPCSocket.hpp" #include #include class SoapyLogAcceptor; class SoapyRemoteDevice : public SoapySDR::Device { public: SoapyRemoteDevice(const std::string &url, const SoapySDR::Kwargs &args); ~SoapyRemoteDevice(void); /******************************************************************* * Identification API ******************************************************************/ std::string getDriverKey(void) const; std::string getHardwareKey(void) const; SoapySDR::Kwargs getHardwareInfo(void) const; /******************************************************************* * Channels API ******************************************************************/ void setFrontendMapping(const int direction, const std::string &mapping); std::string getFrontendMapping(const int direction) const; size_t getNumChannels(const int direction) const; SoapySDR::Kwargs getChannelInfo(const int direction, const size_t channel) const; bool getFullDuplex(const int direction, const size_t channel) const; /******************************************************************* * Stream API ******************************************************************/ std::vector __getRemoteOnlyStreamFormats(const int direction, const size_t channel) const; std::vector getStreamFormats(const int direction, const size_t channel) const; std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; SoapySDR::Stream *setupStream( const int direction, const std::string &format, const std::vector &channels, const SoapySDR::Kwargs &args); void closeStream(SoapySDR::Stream *stream); size_t getStreamMTU(SoapySDR::Stream *stream) const; int activateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs, const size_t numElems); int deactivateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs); int readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs); int writeStream( SoapySDR::Stream *stream, const void * const *buffs, const size_t numElems, int &flags, const long long timeNs, const long timeoutUs); int readStreamStatus( SoapySDR::Stream *stream, size_t &chanMask, int &flags, long long &timeNs, const long timeoutUs); /******************************************************************* * Direct buffer access API ******************************************************************/ size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); int acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs); void releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle); int acquireWriteBuffer( SoapySDR::Stream *stream, size_t &handle, void **buffs, const long timeoutUs); void releaseWriteBuffer( SoapySDR::Stream *stream, const size_t handle, const size_t numElems, int &flags, const long long timeNs); /******************************************************************* * Antenna API ******************************************************************/ std::vector listAntennas(const int direction, const size_t channel) const; void setAntenna(const int direction, const size_t channel, const std::string &name); std::string getAntenna(const int direction, const size_t channel) const; /******************************************************************* * Frontend corrections API ******************************************************************/ bool hasDCOffsetMode(const int direction, const size_t channel) const; void setDCOffsetMode(const int direction, const size_t channel, const bool automatic); bool getDCOffsetMode(const int direction, const size_t channel) const; bool hasDCOffset(const int direction, const size_t channel) const; void setDCOffset(const int direction, const size_t channel, const std::complex &offset); std::complex getDCOffset(const int direction, const size_t channel) const; bool hasIQBalance(const int direction, const size_t channel) const; void setIQBalance(const int direction, const size_t channel, const std::complex &balance); std::complex getIQBalance(const int direction, const size_t channel) const; bool hasFrequencyCorrection(const int direction, const size_t channel) const; void setFrequencyCorrection(const int direction, const size_t channel, const double value); double getFrequencyCorrection(const int direction, const size_t channel) const; /******************************************************************* * Gain API ******************************************************************/ std::vector listGains(const int direction, const size_t channel) const; bool hasGainMode(const int direction, const size_t channel) const; void setGainMode(const int direction, const size_t channel, const bool automatic); bool getGainMode(const int direction, const size_t channel) const; void setGain(const int direction, const size_t channel, const double value); void setGain(const int direction, const size_t channel, const std::string &name, const double value); double getGain(const int direction, const size_t channel) const; double getGain(const int direction, const size_t channel, const std::string &name) const; SoapySDR::Range getGainRange(const int direction, const size_t channel) const; SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const; /******************************************************************* * Frequency API ******************************************************************/ void setFrequency(const int direction, const size_t channel, const double frequency, const SoapySDR::Kwargs &args); void setFrequency(const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args); double getFrequency(const int direction, const size_t channel) const; double getFrequency(const int direction, const size_t channel, const std::string &name) const; std::vector listFrequencies(const int direction, const size_t channel) const; SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel) const; SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const; SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; /******************************************************************* * Sample Rate API ******************************************************************/ void setSampleRate(const int direction, const size_t channel, const double rate); double getSampleRate(const int direction, const size_t channel) const; std::vector listSampleRates(const int direction, const size_t channel) const; SoapySDR::RangeList getSampleRateRange(const int direction, const size_t channel) const; /******************************************************************* * Bandwidth API ******************************************************************/ void setBandwidth(const int direction, const size_t channel, const double bw); double getBandwidth(const int direction, const size_t channel) const; std::vector listBandwidths(const int direction, const size_t channel) const; SoapySDR::RangeList getBandwidthRange(const int direction, const size_t channel) const; /******************************************************************* * Clocking API ******************************************************************/ void setMasterClockRate(const double rate); double getMasterClockRate(void) const; SoapySDR::RangeList getMasterClockRates(void) const; std::vector listClockSources(void) const; void setClockSource(const std::string &source); std::string getClockSource(void) const; /******************************************************************* * Time API ******************************************************************/ std::vector listTimeSources(void) const; void setTimeSource(const std::string &source); std::string getTimeSource(void) const; bool hasHardwareTime(const std::string &what) const; long long getHardwareTime(const std::string &what) const; void setHardwareTime(const long long timeNs, const std::string &what); void setCommandTime(const long long timeNs, const std::string &what); /******************************************************************* * Sensor API ******************************************************************/ std::vector listSensors(void) const; SoapySDR::ArgInfo getSensorInfo(const std::string &name) const; std::string readSensor(const std::string &name) const; std::vector listSensors(const int direction, const size_t channel) const; SoapySDR::ArgInfo getSensorInfo(const int direction, const size_t channel, const std::string &name) const; std::string readSensor(const int direction, const size_t channel, const std::string &name) const; /******************************************************************* * Register API ******************************************************************/ std::vector listRegisterInterfaces(void) const; void writeRegister(const std::string &name, const unsigned addr, const unsigned value); unsigned readRegister(const std::string &name, const unsigned addr) const; void writeRegister(const unsigned addr, const unsigned value); unsigned readRegister(const unsigned addr) const; void writeRegisters(const std::string &name, const unsigned addr, const std::vector &value); std::vector readRegisters(const std::string &name, const unsigned addr, const size_t length) const; /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList getSettingInfo(void) const; void writeSetting(const std::string &key, const std::string &value); std::string readSetting(const std::string &key) const; SoapySDR::ArgInfoList getSettingInfo(const int direction, const size_t channel) const; void writeSetting(const int direction, const size_t channel, const std::string &key, const std::string &value); std::string readSetting(const int direction, const size_t channel, const std::string &key) const; /******************************************************************* * GPIO API ******************************************************************/ std::vector listGPIOBanks(void) const; void writeGPIO(const std::string &bank, const unsigned value); void writeGPIO(const std::string &bank, const unsigned value, const unsigned mask); unsigned readGPIO(const std::string &bank) const; void writeGPIODir(const std::string &bank, const unsigned dir); void writeGPIODir(const std::string &bank, const unsigned dir, const unsigned mask); unsigned readGPIODir(const std::string &bank) const; /******************************************************************* * I2C API ******************************************************************/ void writeI2C(const int addr, const std::string &data); std::string readI2C(const int addr, const size_t numBytes); /******************************************************************* * SPI API ******************************************************************/ unsigned transactSPI(const int addr, const unsigned data, const size_t numBits); /******************************************************************* * UART API ******************************************************************/ std::vector listUARTs(void) const; void writeUART(const std::string &which, const std::string &data); std::string readUART(const std::string &which, const long timeoutUs) const; private: SoapySocketSession _sess; mutable SoapyRPCSocket _sock; SoapyLogAcceptor *_logAcceptor; mutable std::mutex _mutex; std::string _defaultStreamProt; }; SoapyRemote-soapy-remote-0.4.2/client/Streaming.cpp000066400000000000000000000445271313763561200223440ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include #include #include "SoapyClient.hpp" #include "ClientStreamData.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyURLUtils.hpp" #include "SoapyRPCPacker.hpp" #include "SoapyRPCUnpacker.hpp" #include "SoapyStreamEndpoint.hpp" #include //std::min, std::find std::vector SoapyRemoteDevice::__getRemoteOnlyStreamFormats(const int direction, const size_t channel) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_STREAM_FORMATS; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::vector result; unpacker & result; return result; } std::vector SoapyRemoteDevice::getStreamFormats(const int direction, const size_t channel) const { auto formats = __getRemoteOnlyStreamFormats(direction, channel); //add complex floats when a conversion is possible const bool hasCF32 = std::find(formats.begin(), formats.end(), SOAPY_SDR_CF32) != formats.end(); const bool hasCS16 = std::find(formats.begin(), formats.end(), SOAPY_SDR_CS16) != formats.end(); const bool hasCS8 = std::find(formats.begin(), formats.end(), SOAPY_SDR_CS8) != formats.end(); const bool hasCU8 = std::find(formats.begin(), formats.end(), SOAPY_SDR_CU8) != formats.end(); if (not hasCF32 and (hasCS16 or hasCS8 or hasCU8)) formats.push_back(SOAPY_SDR_CF32); return formats; } std::string SoapyRemoteDevice::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_NATIVE_STREAM_FORMAT; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); std::string result; unpacker & result; unpacker & fullScale; return result; } SoapySDR::ArgInfoList SoapyRemoteDevice::getStreamArgsInfo(const int direction, const size_t channel) const { //get the remote arguments first (careful with lock scope) SoapySDR::ArgInfoList result; { std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_GET_STREAM_ARGS_INFO; packer & char(direction); packer & int(channel); packer(); SoapyRPCUnpacker unpacker(_sock); unpacker & result; } //insert SoapyRemote stream arguments double fullScale = 0.0; SoapySDR::ArgInfo formatArg; formatArg.key = "remote:format"; formatArg.value = this->getNativeStreamFormat(direction, channel, fullScale); formatArg.name = "Remote Format"; formatArg.description = "The stream format used on the remote device."; formatArg.type = SoapySDR::ArgInfo::STRING; formatArg.options = __getRemoteOnlyStreamFormats(direction, channel); result.push_back(formatArg); SoapySDR::ArgInfo scaleArg; scaleArg.key = "remote:scale"; scaleArg.value = std::to_string(fullScale); scaleArg.name = "Remote Scale"; scaleArg.description = "The factor used to scale remote samples to full-scale floats."; scaleArg.type = SoapySDR::ArgInfo::FLOAT; result.push_back(scaleArg); SoapySDR::ArgInfo mtuArg; mtuArg.key = "remote:mtu"; mtuArg.value = std::to_string(SOAPY_REMOTE_DEFAULT_ENDPOINT_MTU); mtuArg.name = "Remote MTU"; mtuArg.units = "bytes"; mtuArg.description = "The maximum datagram transfer size in bytes."; mtuArg.type = SoapySDR::ArgInfo::INT; result.push_back(mtuArg); SoapySDR::ArgInfo windowArg; windowArg.key = "remote:window"; windowArg.value = std::to_string(SOAPY_REMOTE_DEFAULT_ENDPOINT_WINDOW); windowArg.name = "Remote Window"; windowArg.units = "bytes"; windowArg.description = "The size of the kernel socket buffer in bytes."; windowArg.type = SoapySDR::ArgInfo::INT; result.push_back(windowArg); SoapySDR::ArgInfo priorityArg; priorityArg.key = "remote:priority"; priorityArg.value = std::to_string(SOAPY_REMOTE_DEFAULT_THREAD_PRIORITY); priorityArg.name = "Remote Priority"; priorityArg.description = "Specify the scheduling priority of the server forwarding threads."; priorityArg.type = SoapySDR::ArgInfo::FLOAT; priorityArg.range = SoapySDR::Range(-1.0, 1.0); result.push_back(priorityArg); return result; } SoapySDR::Stream *SoapyRemoteDevice::setupStream( const int direction, const std::string &localFormat, const std::vector &channels_, const SoapySDR::Kwargs &args_) { //default to channel 0 when not specified //the channels vector cannot be empty //its used for stream endpoint allocation auto channels = channels_; if (channels.empty()) channels.push_back(0); SoapySDR::Kwargs args(args_); //read/write copy //use the remote device's native stream format and scale factor when the conversion is supported double nativeScaleFactor = 0.0; auto nativeFormat = this->getNativeStreamFormat(direction, channels.front(), nativeScaleFactor); const bool useNative = (localFormat == nativeFormat) or (localFormat == SOAPY_SDR_CF32 and nativeFormat == SOAPY_SDR_CS16) or (localFormat == SOAPY_SDR_CF32 and nativeFormat == SOAPY_SDR_CS12) or (localFormat == SOAPY_SDR_CS16 and nativeFormat == SOAPY_SDR_CS12) or (localFormat == SOAPY_SDR_CS16 and nativeFormat == SOAPY_SDR_CS8) or (localFormat == SOAPY_SDR_CF32 and nativeFormat == SOAPY_SDR_CS8) or (localFormat == SOAPY_SDR_CF32 and nativeFormat == SOAPY_SDR_CU8); //use the native format when the conversion is supported, //otherwise use the client's local format for the default auto remoteFormat = useNative?nativeFormat:localFormat; const auto remoteFormatIt = args.find(SOAPY_REMOTE_KWARG_FORMAT); if (remoteFormatIt != args.end()) remoteFormat = remoteFormatIt->second; //use the native scale factor when the remote format is native, //otherwise the default scale factor is the max signed integer double scaleFactor = (remoteFormat == nativeFormat)?nativeScaleFactor:double(1 << ((SoapySDR::formatToSize(remoteFormat)*4)-1)); const auto scaleFactorIt = args.find(SOAPY_REMOTE_KWARG_SCALAR); if (scaleFactorIt != args.end()) scaleFactor = std::stod(scaleFactorIt->second); //determine reliable stream mode with tcp or datagram mode std::string prot = _defaultStreamProt; const auto protIt = args.find(SOAPY_REMOTE_KWARG_PROT); if (protIt != args.end()) prot = protIt->second; const bool datagramMode = (prot == "udp"); if (prot == "udp") {} else if (prot == "tcp") {} else throw std::runtime_error( "SoapyRemote::setupStream() protcol not supported;" "expected 'udp' or 'tcp', but got '"+prot+"'"); args[SOAPY_REMOTE_KWARG_PROT] = prot; size_t mtu = datagramMode?SOAPY_REMOTE_DEFAULT_ENDPOINT_MTU:SOAPY_REMOTE_SOCKET_BUFFMAX; const auto mtuIt = args.find(SOAPY_REMOTE_KWARG_MTU); if (mtuIt != args.end()) mtu = size_t(std::stod(mtuIt->second)); args[SOAPY_REMOTE_KWARG_MTU] = std::to_string(mtu); size_t window = SOAPY_REMOTE_DEFAULT_ENDPOINT_WINDOW; const auto windowIt = args.find(SOAPY_REMOTE_KWARG_WINDOW); if (windowIt != args.end()) window = size_t(std::stod(windowIt->second)); args[SOAPY_REMOTE_KWARG_WINDOW] = std::to_string(window); SoapySDR::logf(SOAPY_SDR_INFO, "SoapyRemote::setup%sStream(remoteFormat=%s, localFormat=%s, scaleFactor=%g, mtu=%d, window=%d)", (direction == SOAPY_SDR_RX)?"Rx":"Tx", remoteFormat.c_str(), localFormat.c_str(), scaleFactor, int(mtu), int(window)); //check supported formats ConvertTypes convertType = CONVERT_MEMCPY; if (localFormat == remoteFormat) convertType = CONVERT_MEMCPY; else if (localFormat == SOAPY_SDR_CF32 and remoteFormat == SOAPY_SDR_CS16) convertType = CONVERT_CF32_CS16; else if (localFormat == SOAPY_SDR_CF32 and remoteFormat == SOAPY_SDR_CS12) convertType = CONVERT_CF32_CS12; else if (localFormat == SOAPY_SDR_CS16 and remoteFormat == SOAPY_SDR_CS12) convertType = CONVERT_CS16_CS12; else if (localFormat == SOAPY_SDR_CS16 and remoteFormat == SOAPY_SDR_CS8) convertType = CONVERT_CS16_CS8; else if (localFormat == SOAPY_SDR_CF32 and remoteFormat == SOAPY_SDR_CS8) convertType = CONVERT_CF32_CS8; else if (localFormat == SOAPY_SDR_CF32 and remoteFormat == SOAPY_SDR_CU8) convertType = CONVERT_CF32_CU8; else throw std::runtime_error( "SoapyRemote::setupStream() conversion not supported;" "localFormat="+localFormat+", remoteFormat="+remoteFormat); //allocate new local stream data ClientStreamData *data = new ClientStreamData(); data->localFormat = localFormat; data->remoteFormat = remoteFormat; data->recvBuffs.resize(channels.size()); data->sendBuffs.resize(channels.size()); data->convertType = convertType; data->scaleFactor = scaleFactor; //extract socket node information const auto localNode = SoapyURL(_sock.getsockname()).getNode(); const auto remoteNode = SoapyURL(_sock.getpeername()).getNode(); //bind the receiver side of the sockets in datagram mode std::string clientBindPort, statusBindPort; if (datagramMode) { //bind the stream socket to an automatic port const auto bindURL = SoapyURL("udp", localNode, "0").toString(); int ret = data->streamSock.bind(bindURL); if (ret != 0) { const std::string errorMsg = data->streamSock.lastErrorMsg(); delete data; throw std::runtime_error("SoapyRemote::setupStream("+bindURL+") -- bind FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Client side stream bound to %s", data->streamSock.getsockname().c_str()); clientBindPort = SoapyURL(data->streamSock.getsockname()).getService(); //bind the status socket to an automatic port ret = data->statusSock.bind(bindURL); if (ret != 0) { const std::string errorMsg = data->statusSock.lastErrorMsg(); delete data; throw std::runtime_error("SoapyRemote::setupStream("+bindURL+") -- bind FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Client side status bound to %s", data->statusSock.getsockname().c_str()); statusBindPort = SoapyURL(data->statusSock.getsockname()).getService(); } //setup the remote end of the stream std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_SETUP_STREAM; packer & char(direction); packer & remoteFormat; packer & channels; packer & args; packer & clientBindPort; packer & statusBindPort; packer(); //for tcp mode: get the binding port here and connect to it std::string serverBindPort; if (not datagramMode) { SoapyRPCUnpacker unpackerTcp(_sock); unpackerTcp & serverBindPort; const auto connectURL = SoapyURL(prot, remoteNode, serverBindPort).toString(); int ret = data->streamSock.connect(connectURL); if (ret != 0) { const std::string errorMsg = data->streamSock.lastErrorMsg(); delete data; throw std::runtime_error("SoapyRemote::setupStream("+connectURL+") -- connect FAIL: " + errorMsg); } ret = data->statusSock.connect(connectURL); if (ret != 0) { const std::string errorMsg = data->statusSock.lastErrorMsg(); delete data; throw std::runtime_error("SoapyRemote::setupStream("+connectURL+") -- connect FAIL: " + errorMsg); } } //and wait for the response with binding port and stream id SoapyRPCUnpacker unpacker(_sock); unpacker & data->streamId; unpacker & serverBindPort; //connect the sending end of the stream socket if (datagramMode) { //connect the stream socket to the specified port const auto connectURL = SoapyURL(prot, remoteNode, serverBindPort).toString(); int ret = data->streamSock.connect(connectURL); if (ret != 0) { const std::string errorMsg = data->streamSock.lastErrorMsg(); delete data; throw std::runtime_error("SoapyRemote::setupStream("+connectURL+") -- connect FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Client side stream connected to %s", data->streamSock.getpeername().c_str()); } //create endpoint data->endpoint = new SoapyStreamEndpoint(data->streamSock, data->statusSock, datagramMode, direction == SOAPY_SDR_RX, channels.size(), SoapySDR::formatToSize(remoteFormat), mtu, window); return (SoapySDR::Stream *)data; } void SoapyRemoteDevice::closeStream(SoapySDR::Stream *stream) { auto data = (ClientStreamData *)stream; std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_CLOSE_STREAM; packer & data->streamId; packer(); SoapyRPCUnpacker unpacker(_sock); //cleanup local stream data delete data->endpoint; delete data; } size_t SoapyRemoteDevice::getStreamMTU(SoapySDR::Stream *stream) const { auto data = (ClientStreamData *)stream; return data->endpoint->getBuffSize(); return SoapySDR::Device::getStreamMTU(stream); } int SoapyRemoteDevice::activateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs, const size_t numElems) { auto data = (ClientStreamData *)stream; std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_ACTIVATE_STREAM; packer & data->streamId; packer & flags; packer & timeNs; packer & int(numElems); packer(); SoapyRPCUnpacker unpacker(_sock); int result = 0; unpacker & result; return result; } int SoapyRemoteDevice::deactivateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs) { auto data = (ClientStreamData *)stream; std::lock_guard lock(_mutex); SoapyRPCPacker packer(_sock); packer & SOAPY_REMOTE_DEACTIVATE_STREAM; packer & data->streamId; packer & flags; packer & timeNs; packer(); SoapyRPCUnpacker unpacker(_sock); int result = 0; unpacker & result; return result; } int SoapyRemoteDevice::readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs) { auto data = (ClientStreamData *)stream; //call into direct buffer access (when there is no remainder) if (data->readElemsLeft == 0) { int ret = this->acquireReadBuffer(stream, data->readHandle, data->recvBuffs.data(), flags, timeNs, timeoutUs); if (ret < 0) return ret; data->readElemsLeft = size_t(ret); } //convert the buffer size_t numSamples = std::min(numElems, data->readElemsLeft); data->convertRecvBuffs(buffs, numSamples); data->readElemsLeft -= numSamples; //completed the buffer, release its handle if (data->readElemsLeft == 0) { this->releaseReadBuffer(stream, data->readHandle); } //increment pointers for the remainder conversion else { flags |= SOAPY_SDR_MORE_FRAGMENTS; const size_t offsetBytes = data->endpoint->getElemSize()*numSamples; for (size_t i = 0; i < data->recvBuffs.size(); i++) { data->recvBuffs[i] = ((char *)data->recvBuffs[i]) + offsetBytes; } } return numSamples; } int SoapyRemoteDevice::writeStream( SoapySDR::Stream *stream, const void * const *buffs, const size_t numElems, int &flags, const long long timeNs, const long timeoutUs) { auto data = (ClientStreamData *)stream; //acquire from direct buffer access size_t handle = 0; int ret = this->acquireWriteBuffer(stream, handle, data->sendBuffs.data(), timeoutUs); if (ret < 0) return ret; //only end burst if the last sample can be released const size_t numSamples = std::min(ret, numElems); if (numSamples < numElems) flags &= ~(SOAPY_SDR_END_BURST); //convert the samples data->convertSendBuffs(buffs, numSamples); //release to direct buffer access this->releaseWriteBuffer(stream, handle, numSamples, flags, timeNs); return numSamples; } int SoapyRemoteDevice::readStreamStatus( SoapySDR::Stream *stream, size_t &chanMask, int &flags, long long &timeNs, const long timeoutUs) { auto data = (ClientStreamData *)stream; auto ep = data->endpoint; if (not ep->waitStatus(timeoutUs)) return SOAPY_SDR_TIMEOUT; return ep->readStatus(chanMask, flags, timeNs); } /******************************************************************* * Direct buffer access API ******************************************************************/ size_t SoapyRemoteDevice::getNumDirectAccessBuffers(SoapySDR::Stream *stream) { auto data = (ClientStreamData *)stream; return data->endpoint->getNumBuffs(); } int SoapyRemoteDevice::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) { auto data = (ClientStreamData *)stream; data->endpoint->getAddrs(handle, buffs); return 0; } int SoapyRemoteDevice::acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs) { auto data = (ClientStreamData *)stream; auto ep = data->endpoint; if (not ep->waitRecv(timeoutUs)) return SOAPY_SDR_TIMEOUT; return ep->acquireRecv(handle, buffs, flags, timeNs); } void SoapyRemoteDevice::releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle) { auto data = (ClientStreamData *)stream; auto ep = data->endpoint; return ep->releaseRecv(handle); } int SoapyRemoteDevice::acquireWriteBuffer( SoapySDR::Stream *stream, size_t &handle, void **buffs, const long timeoutUs) { auto data = (ClientStreamData *)stream; auto ep = data->endpoint; if (not ep->waitSend(timeoutUs)) return SOAPY_SDR_TIMEOUT; return ep->acquireSend(handle, buffs); } void SoapyRemoteDevice::releaseWriteBuffer( SoapySDR::Stream *stream, const size_t handle, const size_t numElems, int &flags, const long long timeNs) { auto data = (ClientStreamData *)stream; auto ep = data->endpoint; return ep->releaseSend(handle, numElems, flags, timeNs); } SoapyRemote-soapy-remote-0.4.2/common/000077500000000000000000000000001313763561200177055ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/common/CMakeLists.txt000066400000000000000000000034451313763561200224530ustar00rootroot00000000000000######################################################################## # Build the remote support library ######################################################################## set(COMMON_SOURCES SoapyURLUtils.cpp SoapyRPCSocket.cpp SoapyRPCPacker.cpp SoapyRPCUnpacker.cpp SoapyStreamEndpoint.cpp SoapyHTTPUtils.cpp SoapySSDPEndpoint.cpp ) #configure ssdp defines configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/SoapyInfoUtils.in.cpp ${CMAKE_CURRENT_BINARY_DIR}/SoapyInfoUtils.cpp @ONLY) list(APPEND COMMON_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/SoapyInfoUtils.cpp) #check for platform-specific network header include(CheckIncludeFiles) CHECK_INCLUDE_FILES(winsock2.h HAS_WINSOCK2_H) CHECK_INCLUDE_FILES(ws2tcpip.h HAS_WS2TCPIP_H) CHECK_INCLUDE_FILES(netdb.h HAS_NETDB_H) CHECK_INCLUDE_FILES(unistd.h HAS_UNISTD_H) CHECK_INCLUDE_FILES(netinet/in.h HAS_NETINET_IN_H) CHECK_INCLUDE_FILES(netinet/tcp.h HAS_NETINET_TCP_H) CHECK_INCLUDE_FILES(sys/types.h HAS_SYS_TYPES_H) CHECK_INCLUDE_FILES(sys/socket.h HAS_SYS_SOCKET_H) CHECK_INCLUDE_FILES(arpa/inet.h HAS_ARPA_INET_H) CHECK_INCLUDE_FILES(ifaddrs.h HAS_IFADDRS_H) CHECK_INCLUDE_FILES(net/if.h HAS_NET_IF_H) CHECK_INCLUDE_FILES(fcntl.h HAS_FCNTL_H) #network libraries if (WIN32) list(APPEND SoapySDR_LIBRARIES ws2_32) endif (WIN32) #create private include header for network compatibility include_directories(${CMAKE_CURRENT_BINARY_DIR}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/SoapySocketDefs.in.hpp ${CMAKE_CURRENT_BINARY_DIR}/SoapySocketDefs.hpp) #build a static library include_directories(${SoapySDR_INCLUDE_DIRS}) add_library(SoapySDRRemoteCommon STATIC ${COMMON_SOURCES}) target_link_libraries(SoapySDRRemoteCommon ${SoapySDR_LIBRARIES}) set_property(TARGET SoapySDRRemoteCommon PROPERTY POSITION_INDEPENDENT_CODE TRUE) SoapyRemote-soapy-remote-0.4.2/common/SoapyHTTPUtils.cpp000066400000000000000000000023661313763561200232340ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapyHTTPUtils.hpp" #include SoapyHTTPHeader::SoapyHTTPHeader(const std::string &line0) { _storage = line0 + "\r\n"; } void SoapyHTTPHeader::addField(const std::string &key, const std::string &value) { _storage += key + ": " + value + "\r\n"; } void SoapyHTTPHeader::finalize(void) { _storage += "\r\n"; } SoapyHTTPHeader::SoapyHTTPHeader(const void *buff, const size_t length) { _storage = std::string((const char *)buff, length); } std::string SoapyHTTPHeader::getLine0(void) const { const auto pos = _storage.find("\r\n"); if (pos == std::string::npos) return ""; return _storage.substr(0, pos); } std::string SoapyHTTPHeader::getField(const std::string &key) const { //find the field start const std::string fieldStart("\r\n"+key+":"); auto pos = _storage.find(fieldStart); if (pos == std::string::npos) return ""; //offset from field start pos += fieldStart.length(); //offset from whitespace while (std::isspace(_storage.at(pos))) pos++; //find the field end const auto end = _storage.find("\r\n", pos); if (end == std::string::npos) return ""; return _storage.substr(pos, end-pos); } SoapyRemote-soapy-remote-0.4.2/common/SoapyHTTPUtils.hpp000066400000000000000000000017131313763561200232340ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include class SOAPY_REMOTE_API SoapyHTTPHeader { public: //! Create an HTTP header given request/response line SoapyHTTPHeader(const std::string &line0); //! Add a key/value field to the header void addField(const std::string &key, const std::string &value); //! Done adding fields to the header void finalize(void); //! Create an HTTP from a received datagram SoapyHTTPHeader(const void *buff, const size_t length); //! Get the request/response line std::string getLine0(void) const; //! Read a field from the HTTP header (empty when missing) std::string getField(const std::string &key) const; const void *data(void) const { return _storage.data(); } size_t size(void) const { return _storage.size(); } private: std::string _storage; }; SoapyRemote-soapy-remote-0.4.2/common/SoapyInfoUtils.hpp000066400000000000000000000007631313763561200233540ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include namespace SoapyInfo { //! Get the hostname SOAPY_REMOTE_API std::string getHostName(void); /*! * Generate a type1 UUID based on the current time and host ID. */ SOAPY_REMOTE_API std::string generateUUID1(void); /*! * Get the user agent string for this build. */ SOAPY_REMOTE_API std::string getUserAgent(void); }; SoapyRemote-soapy-remote-0.4.2/common/SoapyInfoUtils.in.cpp000066400000000000000000000056241313763561200237550ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapySocketDefs.hpp" #include "SoapyInfoUtils.hpp" #include //rand #include #ifdef _MSC_VER #define getpid() GetCurrentProcessId() static DWORD gethostid(void) { char szVolName[MAX_PATH]; char szFileSysName[80]; DWORD dwSerialNumber; DWORD dwMaxComponentLen; DWORD dwFileSysFlags; GetVolumeInformation( "C:\\", szVolName, MAX_PATH, &dwSerialNumber, &dwMaxComponentLen, &dwFileSysFlags, szFileSysName, sizeof(szFileSysName)); return dwSerialNumber; } #endif //_MSC_VER std::string SoapyInfo::getHostName(void) { std::string hostname; char hostnameBuff[128]; int ret = gethostname(hostnameBuff, sizeof(hostnameBuff)); if (ret == 0) hostname = std::string(hostnameBuff); else hostname = "unknown"; return hostname; } std::string SoapyInfo::generateUUID1(void) { //64-bit timestamp in nanoseconds const auto timeSinceEpoch = std::chrono::high_resolution_clock::now().time_since_epoch(); const auto timeNanoseconds = std::chrono::duration_cast(timeSinceEpoch); const unsigned long long timeNs64 = timeNanoseconds.count(); //clock sequence (random) const unsigned short clockSeq16 = short(std::rand()); //rather than node, use the host id and pid const unsigned short pid16 = short(getpid()); const unsigned int hid32 = int(gethostid()); //load up the UUID bytes unsigned char bytes[16]; bytes[0] = (unsigned char)(timeNs64 >> 24); bytes[1] = (unsigned char)(timeNs64 >> 16); bytes[2] = (unsigned char)(timeNs64 >> 8); bytes[3] = (unsigned char)(timeNs64 >> 0); bytes[4] = (unsigned char)(timeNs64 >> 40); bytes[5] = (unsigned char)(timeNs64 >> 32); bytes[6] = (unsigned char)(((timeNs64 >> 56) & 0x0F) | 0x10); //variant bytes[7] = (unsigned char)(timeNs64 >> 48); bytes[8] = (unsigned char)(((clockSeq16 >> 8) & 0x3F) | 0x80); //reserved bytes[9] = (unsigned char)(clockSeq16 >> 0); bytes[10] = (unsigned char)(pid16 >> 8); bytes[11] = (unsigned char)(pid16 >> 0); bytes[12] = (unsigned char)(hid32 >> 24); bytes[13] = (unsigned char)(hid32 >> 16); bytes[14] = (unsigned char)(hid32 >> 8); bytes[15] = (unsigned char)(hid32 >> 0); //load fields into the buffer char buff[37]; const int ret = sprintf(buff, "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" "%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); if (ret > 0) return std::string(buff, size_t(ret)); return ""; //failed } SOAPY_REMOTE_API std::string SoapyInfo::getUserAgent(void) { return "@CMAKE_SYSTEM_NAME@ UPnP/1.1 SoapyRemote/@SoapySDR_VERSION@"; } SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCPacker.cpp000066400000000000000000000127631313763561200232100ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapySocketDefs.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyRPCPacker.hpp" #include //feature defines #include //DBL_MANT_DIG #include //frexp #include //memcpy #include //malloc #include //min, max #include SoapyRPCPacker::SoapyRPCPacker(SoapyRPCSocket &sock, unsigned int remoteRPCVersion): _sock(sock), _message(NULL), _size(0), _capacity(0), _remoteRPCVersion(remoteRPCVersion) { //default allocation this->ensureSpace(512); //allot space for the header (filled in by send) SoapyRPCHeader header; this->pack(&header, sizeof(header)); } SoapyRPCPacker::~SoapyRPCPacker(void) { free(_message); _message = NULL; } void SoapyRPCPacker::send(void) { //load the trailer SoapyRPCTrailer trailer; trailer.trailerWord = htonl(SoapyRPCTrailerWord); this->pack(&trailer, sizeof(trailer)); //load the header SoapyRPCHeader *header = (SoapyRPCHeader *)_message; header->headerWord = htonl(SoapyRPCHeaderWord); header->version = htonl(SoapyRPCVersion); header->length = htonl(_size); //send the entire message size_t bytesSent = 0; while (bytesSent != _size) { const size_t toSend = std::min(SOAPY_REMOTE_SOCKET_BUFFMAX, _size-bytesSent); int ret = _sock.send(_message+bytesSent, toSend); if (ret < 0) { throw std::runtime_error("SoapyRPCPacker::send() FAIL: "+std::string(_sock.lastErrorMsg())); } bytesSent += ret; } } void SoapyRPCPacker::ensureSpace(const size_t length) { if (_size+length <= _capacity) return; const size_t newSize = std::max(_capacity*2, _size+length); _message = (char *)realloc(_message, newSize); } void SoapyRPCPacker::pack(const void *buff, const size_t length) { this->ensureSpace(length); std::memcpy(_message+_size, buff, length); _size += length; } void SoapyRPCPacker::operator&(const char value) { *this & SOAPY_REMOTE_CHAR; this->pack(value); } void SoapyRPCPacker::operator&(const bool value) { *this & SOAPY_REMOTE_BOOL; char out = value?1:0; this->pack(out); } void SoapyRPCPacker::operator&(const int value) { *this & SOAPY_REMOTE_INT32; int out = htonl(value); this->pack(&out, sizeof(out)); } void SoapyRPCPacker::operator&(const long long value) { *this & SOAPY_REMOTE_INT64; long long out = htonll(value); this->pack(&out, sizeof(out)); } void SoapyRPCPacker::operator&(const double value) { *this & SOAPY_REMOTE_FLOAT64; int exp = 0; const double x = std::frexp(value, &exp); const long long man = (long long)std::ldexp(x, DBL_MANT_DIG); *this & exp; *this & man; } void SoapyRPCPacker::operator&(const std::complex &value) { *this & SOAPY_REMOTE_COMPLEX128; *this & value.real(); *this & value.imag(); } void SoapyRPCPacker::operator&(const std::string &value) { *this & SOAPY_REMOTE_STRING; *this & int(value.size()); this->pack(value.c_str(), value.size()); } void SoapyRPCPacker::operator&(const SoapySDR::Range &value) { *this & SOAPY_REMOTE_RANGE; *this & value.minimum(); *this & value.maximum(); //a step size is sent when the remote version matches our current if (_remoteRPCVersion >= SoapyRPCVersion) { #ifdef SOAPY_SDR_API_HAS_RANGE_TYPE_STEP *this & value.step(); #else *this & double(0.0); #endif } } void SoapyRPCPacker::operator&(const SoapySDR::RangeList &value) { *this & SOAPY_REMOTE_RANGE_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & value[i]; } void SoapyRPCPacker::operator&(const std::vector &value) { *this & SOAPY_REMOTE_STRING_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & value[i]; } void SoapyRPCPacker::operator&(const std::vector &value) { *this & SOAPY_REMOTE_FLOAT64_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & value[i]; } void SoapyRPCPacker::operator&(const SoapySDR::Kwargs &value) { *this & SOAPY_REMOTE_KWARGS; *this & int(value.size()); for (auto it = value.begin(); it != value.end(); ++it) { *this & it->first; *this & it->second; } } void SoapyRPCPacker::operator&(const SoapySDR::KwargsList &value) { *this & SOAPY_REMOTE_KWARGS_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & value[i]; } void SoapyRPCPacker::operator&(const std::vector &value) { *this & SOAPY_REMOTE_SIZE_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & int(value[i]); } void SoapyRPCPacker::operator&(const SoapySDR::ArgInfo &value) { *this & SOAPY_REMOTE_ARG_INFO; *this & value.key; *this & value.value; *this & value.name; *this & value.description; *this & value.units; *this & int(value.type); *this & value.range; *this & value.options; *this & value.optionNames; } void SoapyRPCPacker::operator&(const SoapySDR::ArgInfoList &value) { *this & SOAPY_REMOTE_ARG_INFO_LIST; *this & int(value.size()); for (size_t i = 0; i < value.size(); i++) *this & value[i]; } void SoapyRPCPacker::operator&(const std::exception &value) { *this & SOAPY_REMOTE_EXCEPTION; std::string msg(value.what()); *this & msg; } SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCPacker.hpp000066400000000000000000000052501313763561200232060ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include #include #include #include #include class SoapyRPCSocket; /*! * The packer object accepts primitive Soapy SDR types * and encodes them into a portable network RPC format. */ class SOAPY_REMOTE_API SoapyRPCPacker { public: SoapyRPCPacker(SoapyRPCSocket &sock, unsigned int remoteRPCVersion = SoapyRPCVersion); ~SoapyRPCPacker(void); //! Shortcut operator for send void operator()(void) { this->send(); } //! Send the message when packing is complete void send(void); //! Pack a binary blob void pack(const void *buff, const size_t length); //! Pack a single byte void pack(const char byte) { this->ensureSpace(1); _message[_size] = byte; _size++; } //! Pack the call void operator&(const SoapyRemoteCalls value) { *this & SOAPY_REMOTE_CALL; *this & int(value); } //! Pack the type void operator&(const SoapyRemoteTypes value) { this->pack(char(value)); } //! Pack a character void operator&(const char value); //! Pack a boolean void operator&(const bool value); //! Pack a 32-bit integer void operator&(const int value); //! Pack a 64-bit integer void operator&(const long long value); //! Pack a double float void operator&(const double value); //! Pack a complex double float void operator&(const std::complex &value); //! Pack a string void operator&(const std::string &value); //! Pack a range void operator&(const SoapySDR::Range &value); //! Pack a list of ranges void operator&(const SoapySDR::RangeList &value); //! Pack a list of strings void operator&(const std::vector &value); //! Pack a list of double floats void operator&(const std::vector &value); //! Pack a kwargs dictionary void operator&(const SoapySDR::Kwargs &value); //! Pack a list of kwargs void operator&(const SoapySDR::KwargsList &value); //! Pack a list of sizes void operator&(const std::vector &value); //! Pack an arg info structure void operator&(const SoapySDR::ArgInfo &value); //! Pack a list of arg infos void operator&(const SoapySDR::ArgInfoList &value); //! Pack an exception void operator&(const std::exception &value); private: void ensureSpace(const size_t length); SoapyRPCSocket &_sock; char *_message; size_t _size; size_t _capacity; unsigned int _remoteRPCVersion; }; SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCSocket.cpp000066400000000000000000000361261313763561200232320ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapySocketDefs.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyURLUtils.hpp" #include #include //strerror #include //errno #include static std::mutex sessionMutex; static size_t sessionCount = 0; SoapySocketSession::SoapySocketSession(void) { std::lock_guard lock(sessionMutex); sessionCount++; if (sessionCount > 1) return; #ifdef _MSC_VER WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 2); int ret = WSAStartup(wVersionRequested, &wsaData); if (ret != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySocketSession::WSAStartup: %d", ret); } #endif } SoapySocketSession::~SoapySocketSession(void) { std::lock_guard lock(sessionMutex); sessionCount--; if (sessionCount > 0) return; #ifdef _MSC_VER WSACleanup(); #endif } void SoapyRPCSocket::setDefaultTcpSockOpts(void) { if (this->null()) return; int one = 1; int ret = ::setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one)); if (ret != 0) { this->reportError("setsockopt(TCP_NODELAY)"); } #ifdef TCP_QUICKACK ret = ::setsockopt(_sock, IPPROTO_TCP, TCP_QUICKACK, (const char *)&one, sizeof(one)); if (ret != 0) { this->reportError("setsockopt(TCP_QUICKACK)"); } #endif //TCP_QUICKACK } SoapyRPCSocket::SoapyRPCSocket(void): _sock(INVALID_SOCKET) { return; } SoapyRPCSocket::SoapyRPCSocket(const std::string &url): _sock(INVALID_SOCKET) { SoapyURL urlObj(url); SockAddrData addr; const auto errorMsg = urlObj.toSockAddr(addr); if (not errorMsg.empty()) { this->reportError("getaddrinfo("+url+")", errorMsg); } else { _sock = ::socket(addr.addr()->sa_family, urlObj.getType(), 0); } } SoapyRPCSocket::~SoapyRPCSocket(void) { if (this->close() != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapyRPCSocket::~SoapyRPCSocket: %s", this->lastErrorMsg()); } } bool SoapyRPCSocket::null(void) { return _sock == INVALID_SOCKET; } int SoapyRPCSocket::close(void) { if (this->null()) return 0; int ret = ::closesocket(_sock); _sock = INVALID_SOCKET; return ret; } int SoapyRPCSocket::bind(const std::string &url) { SoapyURL urlObj(url); SockAddrData addr; const auto errorMsg = urlObj.toSockAddr(addr); if (not errorMsg.empty()) { this->reportError("getaddrinfo("+url+")", errorMsg); return -1; } if (this->null()) _sock = ::socket(addr.addr()->sa_family, urlObj.getType(), 0); if (this->null()) return -1; //setup reuse address int one = 1; int ret = ::setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)); if (ret != 0) { this->reportError("setsockopt(SO_REUSEADDR)"); } #ifdef __APPLE__ ret = ::setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&one, sizeof(one)); if (ret != 0) { this->reportError("setsockopt(SO_REUSEPORT)"); } #endif //__APPLE__ if (urlObj.getType() == SOCK_STREAM) this->setDefaultTcpSockOpts(); ret = ::bind(_sock, addr.addr(), addr.addrlen()); if (ret == -1) this->reportError("bind("+url+")"); return ret; } int SoapyRPCSocket::listen(int backlog) { int ret = ::listen(_sock, backlog); if (ret == -1) this->reportError("listen()"); return ret; } SoapyRPCSocket *SoapyRPCSocket::accept(void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); int client = ::accept(_sock, (struct sockaddr*)&addr, &addrlen); if (client == INVALID_SOCKET) return NULL; SoapyRPCSocket *clientSock = new SoapyRPCSocket(); clientSock->_sock = client; clientSock->setDefaultTcpSockOpts(); return clientSock; } int SoapyRPCSocket::connect(const std::string &url) { SoapyURL urlObj(url); SockAddrData addr; const auto errorMsg = urlObj.toSockAddr(addr); if (not errorMsg.empty()) { this->reportError("getaddrinfo("+url+")", errorMsg); return -1; } if (this->null()) _sock = ::socket(addr.addr()->sa_family, urlObj.getType(), 0); if (this->null()) return -1; if (urlObj.getType() == SOCK_STREAM) this->setDefaultTcpSockOpts(); int ret = ::connect(_sock, addr.addr(), addr.addrlen()); if (ret == -1) this->reportError("connect("+url+")"); return ret; } int SoapyRPCSocket::connect(const std::string &url, const long timeoutUs) { SoapyURL urlObj(url); SockAddrData addr; const auto errorMsg = urlObj.toSockAddr(addr); if (not errorMsg.empty()) { this->reportError("getaddrinfo("+url+")", errorMsg); return -1; } if (this->null()) _sock = ::socket(addr.addr()->sa_family, urlObj.getType(), 0); if (this->null()) return -1; if (urlObj.getType() == SOCK_STREAM) this->setDefaultTcpSockOpts(); //enable non blocking int ret = this->setNonBlocking(true); if (ret != 0) return ret; //non blocking connect, check for non busy ret = ::connect(_sock, addr.addr(), addr.addrlen()); if (ret != 0 and SOCKET_ERRNO != SOCKET_EINPROGRESS) { this->reportError("connect("+url+")"); return ret; } //fill in the select structures struct timeval tv; tv.tv_sec = timeoutUs / 1000000; tv.tv_usec = timeoutUs % 1000000; fd_set fds; FD_ZERO(&fds); FD_SET(_sock, &fds); //wait for connect or timeout ret = ::select(_sock+1, NULL, &fds, NULL, &tv); if (ret != 1) { this->reportError("connect("+url+")", SOCKET_ETIMEDOUT); return -1; } //get the error code from connect() int opt = 0; socklen_t optlen = sizeof(opt); ::getsockopt(_sock, SOL_SOCKET, SO_ERROR, (char *)&opt, &optlen); if (opt != 0) { this->reportError("connect("+url+")", opt); return opt; } //revert non blocking on socket ret = this->setNonBlocking(false); if (ret != 0) return ret; return opt; } int SoapyRPCSocket::setNonBlocking(const bool nonblock) { int ret = 0; #ifdef _MSC_VER u_long mode = nonblock?1:0; // 1 to enable non-blocking socket ret = ioctlsocket(_sock, FIONBIO, &mode); #else int mode = fcntl(_sock, F_GETFL, 0); if (nonblock) mode |= O_NONBLOCK; else mode &= ~(O_NONBLOCK); ret = fcntl(_sock, F_SETFL, mode); #endif if (ret != 0) this->reportError("setNonBlocking("+std::string(nonblock?"true":"false")+")"); return ret; } /*! * OSX doesn't support automatic ipv6mr_interface = 0. * The following code attempts to work around this issue * by manually selecting a multicast capable interface. */ static int getDefaultIfaceIndex(void) { #ifdef __APPLE__ //find the first available multicast interfaces int loIface = 0, enIface = 0; struct ifaddrs *ifa = nullptr; getifaddrs(&ifa); while (ifa != nullptr) { const bool isIPv6 = ifa->ifa_addr->sa_family == AF_INET6; const bool isUp = ((ifa->ifa_flags & IFF_UP) != 0); const bool isLoopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0); const bool isMulticast = ((ifa->ifa_flags & IFF_MULTICAST) != 0); const int ifaceIndex = if_nametoindex(ifa->ifa_name); SoapySDR::logf(SOAPY_SDR_DEBUG, "Interface: #%d(%s) ipv6=%d, up=%d, lb=%d, mcast=%d", ifaceIndex, ifa->ifa_name, isIPv6, isUp, isLoopback, isMulticast); if (isIPv6 and isUp and isLoopback and isMulticast and loIface == 0) loIface = ifaceIndex; if (isIPv6 and isUp and not isLoopback and isMulticast and enIface == 0) enIface = ifaceIndex; ifa = ifa->ifa_next; } freeifaddrs(ifa); SoapySDR::logf(SOAPY_SDR_DEBUG, "Default loopback: #%d, default ethernet #%d", loIface, enIface); //prefer discovered regular interface over loopback if (enIface != 0) return enIface; if (loIface != 0) return loIface; #endif //__APPLE__ return 0; } int SoapyRPCSocket::multicastJoin(const std::string &group, const bool loop, const int ttl, int iface) { /* * Multicast join docs: * http://www.tldp.org/HOWTO/Multicast-HOWTO-6.html * http://www.tenouk.com/Module41c.html */ //lookup group url SoapyURL urlObj(group); SockAddrData addr; const auto errorMsg = urlObj.toSockAddr(addr); if (not errorMsg.empty()) { this->reportError("getaddrinfo("+group+")", errorMsg); return -1; } //create socket if null if (this->null()) _sock = ::socket(addr.addr()->sa_family, SOCK_DGRAM, 0); if (this->null()) return -1; int ret = 0; int loopInt = loop?1:0; switch(addr.addr()->sa_family) { case AF_INET: { //setup IP_MULTICAST_LOOP ret = ::setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&loopInt, sizeof(loopInt)); if (ret != 0) { this->reportError("setsockopt(IP_MULTICAST_LOOP)"); return -1; } //setup IP_MULTICAST_TTL ret = ::setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&ttl, sizeof(ttl)); if (ret != 0) { this->reportError("setsockopt(IP_MULTICAST_TTL)"); return -1; } //setup IP_ADD_MEMBERSHIP auto *addr_in = (const struct sockaddr_in *)addr.addr(); struct ip_mreq mreq; mreq.imr_multiaddr = addr_in->sin_addr; mreq.imr_interface.s_addr = INADDR_ANY; ret = ::setsockopt(_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&mreq, sizeof(mreq)); if (ret != 0) { this->reportError("setsockopt(IP_ADD_MEMBERSHIP)"); return -1; } break; } case AF_INET6: { //setup IPV6_MULTICAST_LOOP ret = ::setsockopt(_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char *)&loopInt, sizeof(loopInt)); if (ret != 0) { this->reportError("setsockopt(IPV6_MULTICAST_LOOP)"); return -1; } //setup IPV6_MULTICAST_HOPS ret = ::setsockopt(_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&ttl, sizeof(ttl)); if (ret != 0) { this->reportError("setsockopt(IPV6_MULTICAST_HOPS)"); return -1; } //setup IPV6_MULTICAST_IF if (iface == 0) iface = getDefaultIfaceIndex(); if (iface != 0) { ret = ::setsockopt(_sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char *)&iface, sizeof(iface)); if (ret != 0) { this->reportError("setsockopt(IPV6_MULTICAST_IF)"); return -1; } } //setup IPV6_ADD_MEMBERSHIP auto *addr_in6 = (const struct sockaddr_in6 *)addr.addr(); struct ipv6_mreq mreq6; mreq6.ipv6mr_multiaddr = addr_in6->sin6_addr; mreq6.ipv6mr_interface = iface; ret = ::setsockopt(_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (const char *)&mreq6, sizeof(mreq6)); if (ret != 0) { this->reportError("setsockopt(IPV6_ADD_MEMBERSHIP)"); return -1; } break; } default: break; } return 0; } int SoapyRPCSocket::send(const void *buf, size_t len, int flags) { int ret = ::send(_sock, (const char *)buf, int(len), flags); if (ret == -1) this->reportError("send()"); return ret; } int SoapyRPCSocket::recv(void *buf, size_t len, int flags) { int ret = ::recv(_sock, (char *)buf, int(len), flags); if (ret == -1) this->reportError("recv()"); return ret; } int SoapyRPCSocket::sendto(const void *buf, size_t len, const std::string &url, int flags) { SockAddrData addr; SoapyURL(url).toSockAddr(addr); int ret = ::sendto(_sock, (char *)buf, int(len), flags, addr.addr(), addr.addrlen()); if (ret == -1) this->reportError("sendto("+url+")"); return ret; } int SoapyRPCSocket::recvfrom(void *buf, size_t len, std::string &url, int flags) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); int ret = ::recvfrom(_sock, (char *)buf, int(len), flags, (struct sockaddr*)&addr, &addrlen); if (ret == -1) this->reportError("recvfrom()"); else url = SoapyURL(SockAddrData((struct sockaddr *)&addr, addrlen)).toString(); return ret; } bool SoapyRPCSocket::selectRecv(const long timeoutUs) { struct timeval tv; tv.tv_sec = timeoutUs / 1000000; tv.tv_usec = timeoutUs % 1000000; fd_set readfds; FD_ZERO(&readfds); FD_SET(_sock, &readfds); int ret = ::select(_sock+1, &readfds, NULL, NULL, &tv); if (ret == -1) this->reportError("select()"); return ret == 1; } static std::string errToString(const int err) { char buff[1024]; #ifdef _MSC_VER FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buff, sizeof(buff), NULL); return buff; #else //http://linux.die.net/man/3/strerror_r #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) || __APPLE__ strerror_r(err, buff, sizeof(buff)); #else //this version may decide to use its own internal string return strerror_r(err, buff, sizeof(buff)); #endif return buff; #endif } void SoapyRPCSocket::reportError(const std::string &what) { this->reportError(what, SOCKET_ERRNO); } void SoapyRPCSocket::reportError(const std::string &what, const int err) { if (err == 0) _lastErrorMsg = what; else this->reportError(what, std::to_string(err) + ": " + errToString(err)); } void SoapyRPCSocket::reportError(const std::string &what, const std::string &errorMsg) { _lastErrorMsg = what + " [" + errorMsg + "]"; } std::string SoapyRPCSocket::getsockname(void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); int ret = ::getsockname(_sock, (struct sockaddr *)&addr, &addrlen); if (ret == -1) this->reportError("getsockname()"); if (ret != 0) return ""; return SoapyURL(SockAddrData((struct sockaddr *)&addr, addrlen)).toString(); } std::string SoapyRPCSocket::getpeername(void) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); int ret = ::getpeername(_sock, (struct sockaddr *)&addr, &addrlen); if (ret == -1) this->reportError("getpeername()"); if (ret != 0) return ""; return SoapyURL(SockAddrData((struct sockaddr *)&addr, addrlen)).toString(); } int SoapyRPCSocket::setBuffSize(const bool isRecv, const size_t numBytes) { int opt = int(numBytes); int ret = ::setsockopt(_sock, SOL_SOCKET, isRecv?SO_RCVBUF:SO_SNDBUF, (const char *)&opt, sizeof(opt)); if (ret == -1) this->reportError("setsockopt("+std::string(isRecv?"SO_RCVBUF":"SO_SNDBUF")+")"); return ret; } int SoapyRPCSocket::getBuffSize(const bool isRecv) { int opt = 0; socklen_t optlen = sizeof(opt); int ret = ::getsockopt(_sock, SOL_SOCKET, isRecv?SO_RCVBUF:SO_SNDBUF, (char *)&opt, &optlen); if (ret == -1) this->reportError("getsockopt("+std::string(isRecv?"SO_RCVBUF":"SO_SNDBUF")+")"); if (ret != 0) return ret; //adjustment for linux kernel socket buffer doubling for bookkeeping #ifdef __linux opt = opt/2; #endif return opt; } SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCSocket.hpp000066400000000000000000000101271313763561200232300ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include #include class SockAddrData; /*! * Create one instance of the session per process to use sockets. */ class SOAPY_REMOTE_API SoapySocketSession { public: SoapySocketSession(void); ~SoapySocketSession(void); }; /*! * A simple socket wrapper with a TCP-like socket API. * The implementation may be swapped out in the future. */ class SOAPY_REMOTE_API SoapyRPCSocket { public: SoapyRPCSocket(void); /*! * Make the underlying socket (but does not bind or connect). * This function is called automatically by bind and connect, * however it can be used to test if a protocol is possible. */ SoapyRPCSocket(const std::string &url); ~SoapyRPCSocket(void); /*! * Is the socket null? * The default constructor makes a null socket. * The socket is non null after bind or connect, * and after accept returns a successful socket. */ bool null(void); /*! * Explicit close the socket, also done by destructor. */ int close(void); /*! * Server bind. * URL examples: * 0.0.0.0:1234 * [::]:1234 */ int bind(const std::string &url); /*! * Server listen. */ int listen(int backlog); /*! * Server accept connection. * Socket will be null on failure. * Caller owns the client socket. */ SoapyRPCSocket *accept(void); /*! * Client connect. * URL examples: * 10.10.1.123:1234 * [2001:db8:0:1]:1234 * hostname:1234 */ int connect(const std::string &url); /*! * Connect to client with a timeout is microseconds. */ int connect(const std::string &url, const long timeoutUs); //! set or clear non blocking on socket int setNonBlocking(const bool nonblock); /*! * Join a multi-cast group. * \param group the url for the multicast group and port number * \param loop specify to receive local loopback * \param ttl specify time to live for send packets * \param iface the IPv6 interface index or 0 for automatic */ int multicastJoin(const std::string &group, const bool loop = true, const int ttl = 1, const int iface = 0); /*! * Send the buffer and return bytes sent or error. */ int send(const void *buf, size_t len, int flags = 0); /*! * Receive into buffer and return bytes received or error. */ int recv(void *buf, size_t len, int flags = 0); /*! * Send to a specific destination. */ int sendto(const void *buf, size_t len, const std::string &url, int flags = 0); /*! * Receive from an unconnected socket. */ int recvfrom(void *buf, size_t len, std::string &url, int flags = 0); /*! * Wait for recv to become ready with timeout. * Return true for ready, false for timeout. */ bool selectRecv(const long timeoutUs); /*! * Query the last error message as a string. */ const char *lastErrorMsg(void) const { return _lastErrorMsg.c_str(); } /*! * Get the URL of the local socket. * Return an empty string on error. */ std::string getsockname(void); /*! * Get the URL of the remote socket. * Return an empty string on error. */ std::string getpeername(void); /*! * Set the socket buffer size in bytes. * \param isRecv true for RCVBUF, false for SNDBUF * \return 0 for success or negative error code. */ int setBuffSize(const bool isRecv, const size_t numBytes); /*! * Get the socket buffer size in bytes. * \param isRecv true for RCVBUF, false for SNDBUF * \return the actual size set or negative error code. */ int getBuffSize(const bool isRecv); private: int _sock; std::string _lastErrorMsg; void reportError(const std::string &what, const std::string &errorMsg); void reportError(const std::string &what, const int err); void reportError(const std::string &what); void setDefaultTcpSockOpts(void); }; SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCUnpacker.cpp000066400000000000000000000220521313763561200235430ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapySocketDefs.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyRPCUnpacker.hpp" #include "SoapyRPCPacker.hpp" #include #include //feature defines #include //DBL_MANT_DIG #include //ldexp #include //memcpy #include //malloc #include //min, max #include static void testServerConnection(const std::string &url) { SoapyRPCSocket s; int ret = s.connect(url, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret != 0) throw std::runtime_error("SoapyRPCUnpacker::recv() FAIL test server connection: "+std::string(s.lastErrorMsg())); SoapyRPCPacker packerHangup(s); packerHangup & SOAPY_REMOTE_HANGUP; packerHangup(); s.selectRecv(SOAPY_REMOTE_SOCKET_TIMEOUT_US); } SoapyRPCUnpacker::SoapyRPCUnpacker(SoapyRPCSocket &sock, const bool autoRecv, const long timeoutUs): _sock(sock), _message(NULL), _offset(0), _capacity(0), _remoteRPCVersion(SoapyRPCVersion) { //auto recv expects a reply packet within a reasonable time window //or else the link might be down, in which case we throw an error. //Calls are allowed to take a long time (up to 31 seconds). //However, we continually check that the server is active //so that we can tear down immediately if the server goes away. if (timeoutUs >= 0) { auto subTimeout = std::min(timeoutUs, 1000000); //1 second while (true) { if (_sock.selectRecv(subTimeout)) break; testServerConnection(_sock.getpeername()); subTimeout *= 2; //server is up, increase timeout check if (subTimeout >= timeoutUs) throw std::runtime_error("SoapyRPCUnpacker::recv() TIMEOUT: "+std::string(_sock.lastErrorMsg())); } } if (autoRecv) this->recv(); } SoapyRPCUnpacker::~SoapyRPCUnpacker(void) { free(_message); _message = NULL; _offset += sizeof(SoapyRPCTrailer); //consume trailer if (_offset != _capacity) { SoapySDR::logf(SOAPY_SDR_ERROR, "~SoapyRPCUnpacker: Unconsumed payload bytes %d", int(_capacity-_offset)); } } void SoapyRPCUnpacker::recv(void) { //receive the header SoapyRPCHeader header; int ret = _sock.recv(&header, sizeof(header), MSG_WAITALL); if (ret != sizeof(header)) { throw std::runtime_error("SoapyRPCUnpacker::recv(header) FAIL: "+std::string(_sock.lastErrorMsg())); } //inspect and parse the header if (ntohl(header.headerWord) != SoapyRPCHeaderWord) { throw std::runtime_error("SoapyRPCUnpacker::recv() FAIL: header word"); } _remoteRPCVersion = ntohl(header.version); //TODO ignoring the version for now //the check may need to be delicate with the version major, minor vs patch number const size_t length = ntohl(header.length); if (length <= sizeof(SoapyRPCHeader) + sizeof(SoapyRPCTrailer)) { throw std::runtime_error("SoapyRPCUnpacker::recv() FAIL: header length"); } //receive the remaining payload _capacity = length - sizeof(SoapyRPCHeader); _message = (char *)malloc(_capacity); size_t bytesReceived = 0; while (bytesReceived != _capacity) { const size_t toRecv = std::min(SOAPY_REMOTE_SOCKET_BUFFMAX, _capacity-bytesReceived); ret = _sock.recv(_message+bytesReceived, toRecv); if (ret < 0) { throw std::runtime_error("SoapyRPCUnpacker::recv(payload) FAIL: "+std::string(_sock.lastErrorMsg())); } bytesReceived += ret; } //check the trailer SoapyRPCTrailer trailer; std::memcpy(&trailer, _message + _capacity - sizeof(SoapyRPCTrailer), sizeof(trailer)); if (ntohl(trailer.trailerWord) != SoapyRPCTrailerWord) { throw std::runtime_error("SoapyRPCUnpacker::recv() FAIL: trailer word"); } //auto-consume void if (this->peekType() == SOAPY_REMOTE_VOID) { SoapyRemoteTypes type; *this & type; } //check for exceptions else if (this->peekType() == SOAPY_REMOTE_EXCEPTION) { SoapyRemoteTypes type; std::string errorMsg; *this & type; *this & errorMsg; throw std::runtime_error("RemoteError: "+errorMsg); } } void SoapyRPCUnpacker::unpack(void *buff, const size_t length) { std::memcpy(buff, this->unpack(length), length); } void *SoapyRPCUnpacker::unpack(const size_t length) { if (_offset + length > _capacity - sizeof(SoapyRPCTrailer)) { throw std::runtime_error("SoapyRPCUnpacker::unpack() OVER-CONSUME"); } void *buff = _message+_offset; _offset += length; return buff; } bool SoapyRPCUnpacker::done(void) const { return (_offset + sizeof(SoapyRPCTrailer)) == _capacity; } #define UNPACK_TYPE_HELPER(expected) \ SoapyRemoteTypes type; *this & type; \ if (type != expected) {throw std::runtime_error("SoapyRPCUnpacker type check FAIL:" #expected);} else {} void SoapyRPCUnpacker::operator&(SoapyRemoteCalls &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_CALL); int call = 0; *this & call; value = SoapyRemoteCalls(call); } void SoapyRPCUnpacker::operator&(char &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_CHAR); value = this->unpack(); } void SoapyRPCUnpacker::operator&(bool &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_BOOL); char in = this->unpack(); value = (in == 0)?false:true; } void SoapyRPCUnpacker::operator&(int &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_INT32); this->unpack(&value, sizeof(value)); value = ntohl(value); } void SoapyRPCUnpacker::operator&(long long &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_INT64); this->unpack(&value, sizeof(value)); value = ntohll(value); } void SoapyRPCUnpacker::operator&(double &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_FLOAT64); int exp = 0; long long man = 0; *this & exp; *this & man; value = std::ldexp(double(man), exp-DBL_MANT_DIG); } void SoapyRPCUnpacker::operator&(std::complex &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_COMPLEX128); double r = 0.0, i = 0.0; *this & r; *this & i; value = std::complex(r, i); } void SoapyRPCUnpacker::operator&(std::string &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_STRING); int size = 0; *this & size; value = std::string((const char *)this->unpack(size), size); } void SoapyRPCUnpacker::operator&(SoapySDR::Range &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_RANGE); double minimum = 0.0, maximum = 0.0, step = 0.0; *this & minimum; *this & maximum; //a step size is sent when the remote version matches our current if (_remoteRPCVersion >= SoapyRPCVersion) { *this & step; } #ifdef SOAPY_SDR_API_HAS_RANGE_TYPE_STEP value = SoapySDR::Range(minimum, maximum, step); #else value = SoapySDR::Range(minimum, maximum); #endif } void SoapyRPCUnpacker::operator&(SoapySDR::RangeList &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_RANGE_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < size_t(size); i++) *this & value[i]; } void SoapyRPCUnpacker::operator&(std::vector &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_STRING_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < size_t(size); i++) *this & value[i]; } void SoapyRPCUnpacker::operator&(std::vector &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_FLOAT64_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < size_t(size); i++) *this & value[i]; } void SoapyRPCUnpacker::operator&(SoapySDR::Kwargs &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_KWARGS); int size = 0; *this & size; value.clear(); for (size_t i = 0; i < size_t(size); i++) { std::string key, val; *this & key; *this & val; value[key] = val; } } void SoapyRPCUnpacker::operator&(SoapySDR::KwargsList &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_KWARGS_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < size_t(size); i++) *this & value[i]; } void SoapyRPCUnpacker::operator&(std::vector &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_SIZE_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < value.size(); i++) { *this & size; value[i] = size; } } void SoapyRPCUnpacker::operator&(SoapySDR::ArgInfo &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_ARG_INFO); *this & value.key; *this & value.value; *this & value.name; *this & value.description; *this & value.units; int intType = 0; *this & intType; value.type = SoapySDR::ArgInfo::Type(intType); *this & value.range; *this & value.options; *this & value.optionNames; } void SoapyRPCUnpacker::operator&(SoapySDR::ArgInfoList &value) { UNPACK_TYPE_HELPER(SOAPY_REMOTE_ARG_INFO_LIST); int size = 0; *this & size; value.resize(size); for (size_t i = 0; i < size_t(size); i++) *this & value[i]; } SoapyRemote-soapy-remote-0.4.2/common/SoapyRPCUnpacker.hpp000066400000000000000000000054641313763561200235600ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include #include #include #include class SoapyRPCSocket; /*! * The unpacker object receives a complete RPC message, * and unpacks the message into primitive Soapy SDR types. */ class SOAPY_REMOTE_API SoapyRPCUnpacker { public: SoapyRPCUnpacker(SoapyRPCSocket &sock, const bool autoRecv = true, const long timeoutUs = 30000000); ~SoapyRPCUnpacker(void); //! Receive a complete RPC message void recv(void); //! Unpack a binary blob of known size void unpack(void *buff, const size_t length); //! Copy-less version of unpack void *unpack(const size_t length); //! Unpack a single byte char unpack(void) { char byte = _message[_offset]; _offset++; return byte; } //! Done when no data is left to unpack bool done(void) const; //! View the next type without consuming SoapyRemoteTypes peekType(void) const { return SoapyRemoteTypes(_message[_offset]); } //! Unpack the call void operator&(SoapyRemoteCalls &value); //! Unpack the type void operator&(SoapyRemoteTypes &value) { value = SoapyRemoteTypes(this->unpack()); } //! Unpack a character void operator&(char &value); //! Unpack a boolean void operator&(bool &value); //! Unpack a 32-bit integer void operator&(int &value); //! Unpack a 64-bit integer void operator&(long long &value); //! Unpack a double float void operator&(double &value); //! Unpack a complex double float void operator&(std::complex &value); //! Unpack a string void operator&(std::string &value); //! Unpack a range void operator&(SoapySDR::Range &value); //! Unpack a list of ranges void operator&(SoapySDR::RangeList &value); //! Unpack a list of strings void operator&(std::vector &value); //! Unpack a list of double floats void operator&(std::vector &value); //! Unpack a kwargs dictionary void operator&(SoapySDR::Kwargs &value); //! Unpack a list of kwargs void operator&(SoapySDR::KwargsList &value); //! Unpack a list of sizes void operator&(std::vector &value); //! Unpack an arg info structure void operator&(SoapySDR::ArgInfo &value); //! Unpack a list of arg infos void operator&(SoapySDR::ArgInfoList &value); //! Get the received RPC version number unsigned int remoteRPCVersion(void) const { return _remoteRPCVersion; } private: void ensureSpace(const size_t length); SoapyRPCSocket &_sock; char *_message; size_t _offset; size_t _capacity; unsigned int _remoteRPCVersion; }; SoapyRemote-soapy-remote-0.4.2/common/SoapyRemoteConfig.hpp000066400000000000000000000014711313763561200240160ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include /*********************************************************************** * API export defines **********************************************************************/ #ifdef SOAPY_REMOTE_DLL // defined if SOAPY is compiled as a DLL #ifdef SOAPY_REMOTE_DLL_EXPORTS // defined if we are building the SOAPY DLL (instead of using it) #define SOAPY_REMOTE_API SOAPY_SDR_HELPER_DLL_EXPORT #else #define SOAPY_REMOTE_API SOAPY_SDR_HELPER_DLL_IMPORT #endif // SOAPY_REMOTE_DLL_EXPORTS #define SOAPY_REMOTE_LOCAL SOAPY_SDR_HELPER_DLL_LOCAL #else // SOAPY_REMOTE_DLL is not defined: this means SOAPY is a static lib. #define SOAPY_REMOTE_API SOAPY_SDR_HELPER_DLL_EXPORT #endif // SOAPY_REMOTE_DLL SoapyRemote-soapy-remote-0.4.2/common/SoapyRemoteDefs.hpp000066400000000000000000000250731313763561200234760ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" /*********************************************************************** * Key-words and their defaults **********************************************************************/ //! Use this magic stop key in the server to prevent infinite loops #define SOAPY_REMOTE_KWARG_STOP "soapy_remote_no_deeper" //! Use this key prefix to pass in args that will become local #define SOAPY_REMOTE_KWARG_PREFIX "remote:" //! Stream args key to set the format on the remote server #define SOAPY_REMOTE_KWARG_FORMAT (SOAPY_REMOTE_KWARG_PREFIX "format") //! Stream args key to set the scalar for local float conversions #define SOAPY_REMOTE_KWARG_SCALAR (SOAPY_REMOTE_KWARG_PREFIX "scalar") //! Stream args key to set the buffer MTU bytes for network transfers #define SOAPY_REMOTE_KWARG_MTU (SOAPY_REMOTE_KWARG_PREFIX "mtu") //! Stream args key to select the stream's protocol (tcp or udp) #define SOAPY_REMOTE_KWARG_PROT (SOAPY_REMOTE_KWARG_PREFIX "prot") /*! * Default stream transfer size (under network MTU). * Larger transfer sizes may not be supported in hardware * or may require tweaks to the system configuration. */ #define SOAPY_REMOTE_DEFAULT_ENDPOINT_MTU 1500 /*! * Stream args key to set the very large socket buffer size in bytes. * This sets the socket buffer size as well as the flow control window. */ #define SOAPY_REMOTE_KWARG_WINDOW (SOAPY_REMOTE_KWARG_PREFIX "window") /*! * Default number of bytes in socket buffer. * Larger buffer sizes may not be supported or * may require tweaks to the system configuration. */ #ifdef __APPLE__ //large buffer size causes crash #define SOAPY_REMOTE_DEFAULT_ENDPOINT_WINDOW (16*1024) #else #define SOAPY_REMOTE_DEFAULT_ENDPOINT_WINDOW (42*1024*1024) #endif /*! * Stream args key to set the priority of the forwarding threads. * Priority ranges: -1.0 (low), 0.0 (normal), and 1.0 (high) */ #define SOAPY_REMOTE_KWARG_PRIORITY (SOAPY_REMOTE_KWARG_PREFIX "priority") //! Default thread priority is elevated for stream forwarding #define SOAPY_REMOTE_DEFAULT_THREAD_PRIORITY double(0.5) /*********************************************************************** * Socket defaults **********************************************************************/ //! The default bind port for the remote server #define SOAPY_REMOTE_DEFAULT_SERVICE "55132" //! Use this timeout for every socket poll loop #define SOAPY_REMOTE_SOCKET_TIMEOUT_US (500*1000) //500 ms //! Backlog count for the server socket listen #define SOAPY_REMOTE_LISTEN_BACKLOG 100 /*! * The number of buffers that can be acquired. * This is the number of buffers for the direct access API. * The socket is doing all of the actual buffering, * this just allows the user to get some flexibility * with the direct access API. Otherwise, most client code * will acquire and immediately release the same handle. */ #define SOAPY_REMOTE_ENDPOINT_NUM_BUFFS 8 /*! * The maximum buffer size for single socket call. * Use this in the packer and unpacker TCP code. * Larger buffers may crash some socket implementations. * The implementation should loop until completed. */ #define SOAPY_REMOTE_SOCKET_BUFFMAX 4096 /*********************************************************************** * RPC structures and constants **********************************************************************/ //major, minor, patch when this was last updated //bump the version number when changes are made static const unsigned int SoapyRPCVersion = 0x000400; enum SoapyRemoteTypes { SOAPY_REMOTE_CHAR = 0, SOAPY_REMOTE_BOOL = 1, SOAPY_REMOTE_INT32 = 2, SOAPY_REMOTE_INT64 = 3, SOAPY_REMOTE_FLOAT64 = 4, SOAPY_REMOTE_COMPLEX128 = 5, SOAPY_REMOTE_STRING = 6, SOAPY_REMOTE_RANGE = 7, SOAPY_REMOTE_RANGE_LIST = 8, SOAPY_REMOTE_STRING_LIST = 9, SOAPY_REMOTE_FLOAT64_LIST = 10, SOAPY_REMOTE_KWARGS = 11, SOAPY_REMOTE_KWARGS_LIST = 12, SOAPY_REMOTE_EXCEPTION = 13, SOAPY_REMOTE_VOID = 14, SOAPY_REMOTE_CALL = 15, SOAPY_REMOTE_SIZE_LIST = 16, SOAPY_REMOTE_ARG_INFO = 17, SOAPY_REMOTE_ARG_INFO_LIST = 18, SOAPY_REMOTE_TYPE_MAX = 19, }; enum SoapyRemoteCalls { //factory SOAPY_REMOTE_FIND = 0, SOAPY_REMOTE_MAKE = 1, SOAPY_REMOTE_UNMAKE = 2, SOAPY_REMOTE_HANGUP = 3, //logger SOAPY_REMOTE_GET_SERVER_ID = 20, SOAPY_REMOTE_START_LOG_FORWARDING = 21, SOAPY_REMOTE_STOP_LOG_FORWARDING = 22, //identification SOAPY_REMOTE_GET_DRIVER_KEY = 100, SOAPY_REMOTE_GET_HARDWARE_KEY = 101, SOAPY_REMOTE_GET_HARDWARE_INFO = 102, //channels SOAPY_REMOTE_SET_FRONTEND_MAPPING = 200, SOAPY_REMOTE_GET_FRONTEND_MAPPING = 201, SOAPY_REMOTE_GET_NUM_CHANNELS = 202, SOAPY_REMOTE_GET_FULL_DUPLEX = 203, SOAPY_REMOTE_GET_CHANNEL_INFO = 204, //stream SOAPY_REMOTE_SETUP_STREAM = 300, SOAPY_REMOTE_CLOSE_STREAM = 301, SOAPY_REMOTE_ACTIVATE_STREAM = 302, SOAPY_REMOTE_DEACTIVATE_STREAM = 303, SOAPY_REMOTE_GET_STREAM_FORMATS = 304, SOAPY_REMOTE_GET_NATIVE_STREAM_FORMAT = 305, SOAPY_REMOTE_GET_STREAM_ARGS_INFO = 306, //antenna SOAPY_REMOTE_LIST_ANTENNAS = 500, SOAPY_REMOTE_SET_ANTENNA = 501, SOAPY_REMOTE_GET_ANTENNA = 502, //corrections SOAPY_REMOTE_HAS_DC_OFFSET_MODE = 600, SOAPY_REMOTE_SET_DC_OFFSET_MODE = 601, SOAPY_REMOTE_GET_DC_OFFSET_MODE = 602, SOAPY_REMOTE_HAS_DC_OFFSET = 603, SOAPY_REMOTE_SET_DC_OFFSET = 604, SOAPY_REMOTE_GET_DC_OFFSET = 605, SOAPY_REMOTE_HAS_IQ_BALANCE_MODE = 606, SOAPY_REMOTE_SET_IQ_BALANCE_MODE = 607, SOAPY_REMOTE_GET_IQ_BALANCE_MODE = 608, SOAPY_REMOTE_HAS_FREQUENCY_CORRECTION = 503, SOAPY_REMOTE_SET_FREQUENCY_CORRECTION = 504, SOAPY_REMOTE_GET_FREQUENCY_CORRECTION = 505, //gain SOAPY_REMOTE_LIST_GAINS = 700, SOAPY_REMOTE_SET_GAIN_MODE = 701, SOAPY_REMOTE_GET_GAIN_MODE = 702, SOAPY_REMOTE_SET_GAIN = 703, SOAPY_REMOTE_SET_GAIN_ELEMENT = 704, SOAPY_REMOTE_GET_GAIN = 705, SOAPY_REMOTE_GET_GAIN_ELEMENT = 706, SOAPY_REMOTE_GET_GAIN_RANGE = 707, SOAPY_REMOTE_GET_GAIN_RANGE_ELEMENT = 708, SOAPY_REMOTE_HAS_GAIN_MODE = 709, //frequency SOAPY_REMOTE_SET_FREQUENCY = 800, SOAPY_REMOTE_SET_FREQUENCY_COMPONENT = 801, SOAPY_REMOTE_GET_FREQUENCY = 802, SOAPY_REMOTE_GET_FREQUENCY_COMPONENT = 803, SOAPY_REMOTE_LIST_FREQUENCIES = 804, SOAPY_REMOTE_GET_FREQUENCY_RANGE = 805, SOAPY_REMOTE_GET_FREQUENCY_RANGE_COMPONENT = 806, SOAPY_REMOTE_GET_FREQUENCY_ARGS_INFO = 807, //sample rate SOAPY_REMOTE_SET_SAMPLE_RATE = 900, SOAPY_REMOTE_GET_SAMPLE_RATE = 901, SOAPY_REMOTE_LIST_SAMPLE_RATES = 902, SOAPY_REMOTE_GET_SAMPLE_RATE_RANGE = 907, //bandwidth SOAPY_REMOTE_SET_BANDWIDTH = 903, SOAPY_REMOTE_GET_BANDWIDTH = 904, SOAPY_REMOTE_LIST_BANDWIDTHS = 905, SOAPY_REMOTE_GET_BANDWIDTH_RANGE = 906, //clocking SOAPY_REMOTE_SET_MASTER_CLOCK_RATE = 1000, SOAPY_REMOTE_GET_MASTER_CLOCK_RATE = 1001, SOAPY_REMOTE_LIST_CLOCK_SOURCES = 1002, SOAPY_REMOTE_SET_CLOCK_SOURCE = 1003, SOAPY_REMOTE_GET_CLOCK_SOURCE = 1004, SOAPY_REMOTE_GET_MASTER_CLOCK_RATES = 1008, //time SOAPY_REMOTE_LIST_TIME_SOURCES = 1005, SOAPY_REMOTE_SET_TIME_SOURCE = 1006, SOAPY_REMOTE_GET_TIME_SOURCE = 1007, SOAPY_REMOTE_HAS_HARDWARE_TIME = 1100, SOAPY_REMOTE_GET_HARDWARE_TIME = 1101, SOAPY_REMOTE_SET_HARDWARE_TIME = 1102, SOAPY_REMOTE_SET_COMMAND_TIME = 1103, //sensors SOAPY_REMOTE_LIST_SENSORS = 1200, SOAPY_REMOTE_READ_SENSOR = 1201, SOAPY_REMOTE_LIST_CHANNEL_SENSORS = 1202, SOAPY_REMOTE_READ_CHANNEL_SENSOR = 1203, SOAPY_REMOTE_GET_SENSOR_INFO = 1204, SOAPY_REMOTE_GET_CHANNEL_SENSOR_INFO = 1205, //registers SOAPY_REMOTE_WRITE_REGISTER = 1300, SOAPY_REMOTE_READ_REGISTER = 1301, SOAPY_REMOTE_LIST_REGISTER_INTERFACES = 1302, SOAPY_REMOTE_WRITE_REGISTER_NAMED = 1303, SOAPY_REMOTE_READ_REGISTER_NAMED = 1304, SOAPY_REMOTE_WRITE_REGISTERS = 1305, SOAPY_REMOTE_READ_REGISTERS = 1306, //settings SOAPY_REMOTE_WRITE_SETTING = 1400, SOAPY_REMOTE_READ_SETTING = 1401, SOAPY_REMOTE_GET_SETTING_INFO = 1402, SOAPY_REMOTE_WRITE_CHANNEL_SETTING = 1403, SOAPY_REMOTE_READ_CHANNEL_SETTING = 1404, SOAPY_REMOTE_GET_CHANNEL_SETTING_INFO = 1405, //gpio SOAPY_REMOTE_LIST_GPIO_BANKS = 1500, SOAPY_REMOTE_WRITE_GPIO = 1501, SOAPY_REMOTE_WRITE_GPIO_MASKED = 1502, SOAPY_REMOTE_READ_GPIO = 1503, SOAPY_REMOTE_WRITE_GPIO_DIR = 1504, SOAPY_REMOTE_WRITE_GPIO_DIR_MASKED = 1505, SOAPY_REMOTE_READ_GPIO_DIR = 1506, //i2c SOAPY_REMOTE_WRITE_I2C = 1600, SOAPY_REMOTE_READ_I2C = 1601, //spi SOAPY_REMOTE_TRANSACT_SPI = 1700, //uart SOAPY_REMOTE_LIST_UARTS = 1801, SOAPY_REMOTE_WRITE_UART = 1802, SOAPY_REMOTE_READ_UART = 1803, }; #define SOAPY_PACKET_WORD32(str) \ ((unsigned int)(str[0]) << 24) | \ ((unsigned int)(str[1]) << 16) | \ ((unsigned int)(str[2]) << 8) | \ ((unsigned int)(str[3]) << 0) static const unsigned int SoapyRPCHeaderWord = SOAPY_PACKET_WORD32("SRPC"); static const unsigned int SoapyRPCTrailerWord = SOAPY_PACKET_WORD32("CPRS"); struct SoapyRPCHeader { unsigned int headerWord; //!< header word to identify this protocol unsigned int version; //!< version number for protocol compatibility unsigned int length; //!< complete packet length in bytes }; struct SoapyRPCTrailer { unsigned int trailerWord; //!< trailer word to identify this protocol }; SoapyRemote-soapy-remote-0.4.2/common/SoapySSDPEndpoint.cpp000066400000000000000000000331261313763561200237040ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 /* * Docs and examples: * https://stackoverflow.com/questions/13382469/ssdp-protocol-implementation * http://buildingskb.schneider-electric.com/view.php?AID=15197 * http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf */ #include #include "SoapySSDPEndpoint.hpp" #include "SoapyURLUtils.hpp" #include "SoapyInfoUtils.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyHTTPUtils.hpp" #include "SoapyRPCSocket.hpp" #include #include #include #include #include #include //! IPv4 multi-cast address for SSDP communications #define SSDP_MULTICAST_ADDR_IPV4 "239.255.255.250" //! IPv6 multi-cast address for SSDP communications #define SSDP_MULTICAST_ADDR_IPV6 "ff02::c" //! UDP service port number for SSDP communications #define SSDP_UDP_PORT_NUMBER "1900" //! service and notify target identification string #define SOAPY_REMOTE_TARGET "urn:schemas-pothosware-com:service:soapyRemote:1" //! How often search and notify packets are triggered #define TRIGGER_TIMEOUT_SECONDS 60 //! The default duration of an entry in the USN cache #define CACHE_DURATION_SECONDS 120 //! Service is active, use with multicast NOTIFY #define NTS_ALIVE "ssdp:alive" //! Service stopped, use with multicast NOTIFY #define NTS_BYEBYE "ssdp:byebye" struct SoapySSDPEndpointData { int ipVer; SoapyRPCSocket sock; std::string groupURL; std::thread *thread; std::chrono::high_resolution_clock::time_point lastTimeSearch; std::chrono::high_resolution_clock::time_point lastTimeNotify; typedef std::map> DiscoveredURLs; DiscoveredURLs usnToURL; }; static std::string timeNowGMT(void) { char buff[128]; auto t = std::time(nullptr); size_t len = std::strftime(buff, sizeof(buff), "%c %Z", std::localtime(&t)); return std::string(buff, len); } std::shared_ptr SoapySSDPEndpoint::getInstance(void) { static std::mutex singletonMutex; std::lock_guard lock(singletonMutex); static std::weak_ptr epWeak; auto epShared = epWeak.lock(); if (not epShared) epShared.reset(new SoapySSDPEndpoint()); epWeak = epShared; return epShared; } SoapySSDPEndpoint::SoapySSDPEndpoint(void): serviceRegistered(false), periodicSearchEnabled(false), periodicNotifyEnabled(false), done(false) { const bool isIPv6Supported = not SoapyRPCSocket(SoapyURL("tcp", "::", "0").toString()).null(); this->spawnHandler("0.0.0.0", SSDP_MULTICAST_ADDR_IPV4, 4); if (isIPv6Supported) this->spawnHandler("::", SSDP_MULTICAST_ADDR_IPV6, 6); } SoapySSDPEndpoint::~SoapySSDPEndpoint(void) { done = true; for (auto &data : handlers) { data->thread->join(); delete data->thread; delete data; } } void SoapySSDPEndpoint::registerService(const std::string &uuid, const std::string &service) { std::lock_guard lock(mutex); this->serviceRegistered = true; this->uuid = uuid; this->service = service; } void SoapySSDPEndpoint::enablePeriodicSearch(const bool enable) { std::lock_guard lock(mutex); periodicSearchEnabled = enable; for (auto &data : handlers) this->sendSearchHeader(data); } void SoapySSDPEndpoint::enablePeriodicNotify(const bool enable) { std::lock_guard lock(mutex); periodicNotifyEnabled = enable; for (auto &data : handlers) this->sendNotifyHeader(data, NTS_ALIVE); } std::vector SoapySSDPEndpoint::getServerURLs(const int ipVer, const bool only) { std::lock_guard lock(mutex); //create a single mapping of discovered URLs using the preferences specified SoapySSDPEndpointData::DiscoveredURLs usnPrefToURL; for (auto &data : handlers) { const bool ipVerMatch = data->ipVer == ipVer; //ignore this data set if only is specified and the IP version does not match if (only and not ipVerMatch) continue; for (auto &pair : data->usnToURL) { //ignore this URL if the entry is already present and the IP version does not match if (usnPrefToURL.count(pair.first) != 0 and not ipVerMatch) continue; usnPrefToURL[pair.first] = pair.second; } } //copy the filtered URLs into the resulting list std::vector serverURLs; for (auto &pair : usnPrefToURL) serverURLs.push_back(pair.second.first); return serverURLs; } void SoapySSDPEndpoint::spawnHandler(const std::string &bindAddr, const std::string &groupAddr, const int ipVer) { //static list of blacklisted groups //if we fail to join a group, its blacklisted //so future instances wont get the same error //thread-safe protected by the get instance call static std::set blacklistedGroups; //check the blacklist if (blacklistedGroups.find(groupAddr) != blacklistedGroups.end()) { SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapySSDPEndpoint::spawnHandler(%s) group blacklisted due to previous error", groupAddr.c_str()); return; } auto data = new SoapySSDPEndpointData(); data->ipVer = ipVer; auto &sock = data->sock; const auto groupURL = SoapyURL("udp", groupAddr, SSDP_UDP_PORT_NUMBER).toString(); int ret = sock.multicastJoin(groupURL); if (ret != 0) { blacklistedGroups.insert(groupAddr); SoapySDR::logf(SOAPY_SDR_WARNING, "SoapySSDPEndpoint failed join group %s\n %s", groupURL.c_str(), sock.lastErrorMsg()); delete data; return; } const auto bindURL = SoapyURL("udp", bindAddr, SSDP_UDP_PORT_NUMBER).toString(); ret = sock.bind(bindURL); if (ret != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySSDPEndpoint::bind(%s) failed\n %s", bindURL.c_str(), sock.lastErrorMsg()); delete data; return; } data->groupURL = groupURL; data->thread = new std::thread(&SoapySSDPEndpoint::handlerLoop, this, data); handlers.push_back(data); } void SoapySSDPEndpoint::handlerLoop(SoapySSDPEndpointData *data) { auto &sock = data->sock; std::string recvAddr; char recvBuff[SOAPY_REMOTE_DEFAULT_ENDPOINT_MTU]; while (not done) { //receive SSDP traffic if (sock.selectRecv(SOAPY_REMOTE_SOCKET_TIMEOUT_US)) { std::lock_guard lock(mutex); int ret = sock.recvfrom(recvBuff, sizeof(recvBuff), recvAddr); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySSDPEndpoint::recvfrom() = %d\n %s", ret, sock.lastErrorMsg()); return; } //parse the HTTP header SoapyHTTPHeader header(recvBuff, size_t(ret)); if (header.getLine0() == "M-SEARCH * HTTP/1.1") this->handleSearchRequest(data, header, recvAddr); if (header.getLine0() == "HTTP/1.1 200 OK") this->handleSearchResponse(data, header, recvAddr); if (header.getLine0() == "NOTIFY * HTTP/1.1") this->handleNotifyRequest(data, header, recvAddr); } //locked for all non-blocking routines below std::lock_guard lock(mutex); const auto timeNow = std::chrono::high_resolution_clock::now(); const auto triggerExpired = timeNow + std::chrono::seconds(TRIGGER_TIMEOUT_SECONDS); //remove old cache entries auto it = data->usnToURL.begin(); while (it != data->usnToURL.end()) { auto &expires = it->second.second; if (expires > timeNow) ++it; else data->usnToURL.erase(it++); } //check trigger for periodic search if (periodicSearchEnabled and data->lastTimeSearch > triggerExpired) { this->sendSearchHeader(data); } //check trigger for periodic notify if (periodicNotifyEnabled and data->lastTimeNotify > triggerExpired) { this->sendNotifyHeader(data, NTS_ALIVE); } } //disconnect notification when done if (done) { std::lock_guard lock(mutex); this->sendNotifyHeader(data, NTS_BYEBYE); } } void SoapySSDPEndpoint::sendHeader(SoapyRPCSocket &sock, const SoapyHTTPHeader &header, const std::string &addr) { int ret = sock.sendto(header.data(), header.size(), addr); if (ret != int(header.size())) { SoapySDR::logf(SOAPY_SDR_ERROR, "SoapySSDPEndpoint::sendTo(%s) = %d\n %s", addr.c_str(), ret, sock.lastErrorMsg()); } } void SoapySSDPEndpoint::sendSearchHeader(SoapySSDPEndpointData *data) { auto hostURL = SoapyURL(data->groupURL); hostURL.setScheme(""); //no scheme name SoapyHTTPHeader header("M-SEARCH * HTTP/1.1"); header.addField("HOST", hostURL.toString()); header.addField("MAN", "\"ssdp:discover\""); header.addField("MX", "2"); header.addField("ST", SOAPY_REMOTE_TARGET); header.addField("USER-AGENT", SoapyInfo::getUserAgent()); header.finalize(); this->sendHeader(data->sock, header, data->groupURL); data->lastTimeSearch = std::chrono::high_resolution_clock::now(); } void SoapySSDPEndpoint::sendNotifyHeader(SoapySSDPEndpointData *data, const std::string &nts) { if (not serviceRegistered) return; //do we have a service to advertise? auto hostURL = SoapyURL(data->groupURL); hostURL.setScheme(""); //no scheme name SoapyHTTPHeader header("NOTIFY * HTTP/1.1"); header.addField("HOST", hostURL.toString()); if (nts == NTS_ALIVE) { header.addField("CACHE-CONTROL", "max-age=" + std::to_string(CACHE_DURATION_SECONDS)); header.addField("LOCATION", SoapyURL("tcp", SoapyInfo::getHostName(), service).toString()); } header.addField("SERVER", SoapyInfo::getUserAgent()); header.addField("NT", SOAPY_REMOTE_TARGET); header.addField("USN", "uuid:"+uuid+"::"+SOAPY_REMOTE_TARGET); header.addField("NTS", nts); header.finalize(); this->sendHeader(data->sock, header, data->groupURL); data->lastTimeNotify = std::chrono::high_resolution_clock::now(); } void SoapySSDPEndpoint::handleSearchRequest(SoapySSDPEndpointData *data, const SoapyHTTPHeader &request, const std::string &recvAddr) { if (not serviceRegistered) return; //do we have a service to advertise? if (request.getField("MAN") != "\"ssdp:discover\"") return; const auto st = request.getField("ST"); const bool stForUs = (st == "ssdp:all" or st == SOAPY_REMOTE_TARGET or st == "uuid:"+uuid); if (not stForUs) return; //send a unicast response HTTP header SoapyHTTPHeader response("HTTP/1.1 200 OK"); response.addField("CACHE-CONTROL", "max-age=" + std::to_string(CACHE_DURATION_SECONDS)); response.addField("DATE", timeNowGMT()); response.addField("EXT", ""); response.addField("LOCATION", SoapyURL("tcp", SoapyInfo::getHostName(), service).toString()); response.addField("SERVER", SoapyInfo::getUserAgent()); response.addField("ST", SOAPY_REMOTE_TARGET); response.addField("USN", "uuid:"+uuid+"::"+SOAPY_REMOTE_TARGET); response.finalize(); this->sendHeader(data->sock, response, recvAddr); //The unicast response may not be received if the destination has multiple SSDP clients //because only one client on the destination host will actually receive the datagram. //To work around this limitation, a multicast notification packet is sent as well; //which will be received by all clients at the destination as well as other hosts. this->sendNotifyHeader(data, NTS_ALIVE); } static int getCacheDuration(const SoapyHTTPHeader &header) { const auto cacheControl = header.getField("CACHE-CONTROL"); if (cacheControl.empty()) return CACHE_DURATION_SECONDS; const auto maxAgePos = cacheControl.find("max-age"); const auto equalsPos = cacheControl.find("="); if (maxAgePos == std::string::npos) return CACHE_DURATION_SECONDS; if (equalsPos == std::string::npos) return CACHE_DURATION_SECONDS; if (maxAgePos > equalsPos) return CACHE_DURATION_SECONDS; auto valuePos = equalsPos + 1; while (std::isspace(cacheControl.at(valuePos))) valuePos++; const auto maxAge = cacheControl.substr(valuePos); try {return std::stoul(maxAge);} catch (...) {return CACHE_DURATION_SECONDS;} } void SoapySSDPEndpoint::handleSearchResponse(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &recvAddr) { if (header.getField("ST") != SOAPY_REMOTE_TARGET) return; this->handleRegisterService(data, header, recvAddr); } void SoapySSDPEndpoint::handleNotifyRequest(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &recvAddr) { if (header.getField("NT") != SOAPY_REMOTE_TARGET) return; this->handleRegisterService(data, header, recvAddr); } void SoapySSDPEndpoint::handleRegisterService(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &recvAddr) { //extract usn const auto usn = header.getField("USN"); if (usn.empty()) return; //handle byebye from notification packets if (header.getField("NTS") == NTS_BYEBYE) { data->usnToURL.erase(usn); return; } //format the server's url const auto location = header.getField("LOCATION"); if (location.empty()) return; const SoapyURL serverURL("tcp", SoapyURL(recvAddr).getNode(), SoapyURL(location).getService()); SoapySDR::logf(SOAPY_SDR_DEBUG, "SoapyRemote discovered %s", serverURL.toString().c_str()); //register the server const auto expires = std::chrono::high_resolution_clock::now() + std::chrono::seconds(getCacheDuration(header)); data->usnToURL[usn] = std::make_pair(serverURL.toString(), expires); } SoapyRemote-soapy-remote-0.4.2/common/SoapySSDPEndpoint.hpp000066400000000000000000000056141313763561200237120ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRPCSocket.hpp" #include #include //sig_atomic_t #include #include #include class SoapyHTTPHeader; struct SoapySSDPEndpointData; /*! * Service an SSDP endpoint to: * keep track of discovered servers of interest, * and to respond to discovery packets for us. */ class SoapySSDPEndpoint { public: //! Get a singleton instance of the endpoint static std::shared_ptr getInstance(void); /*! * Create a discovery endpoint */ SoapySSDPEndpoint(void); ~SoapySSDPEndpoint(void); /*! * Allow the endpoint to advertise that its running the RPC service */ void registerService(const std::string &uuid, const std::string &service); /*! * Enable the client endpoint to search for running services. */ void enablePeriodicSearch(const bool enable); /*! * Enable the server to send periodic notification messages. */ void enablePeriodicNotify(const bool enable); /*! * Get a list of all active server URLs. * * The same endpoint can be discovered under both IPv4 and IPv6. * When 'only' is false, the ipVer specifies the IP version preference * when both are discovered but will fallback to the other version. * But when 'only' is true, only addresses of the ipVer type are used. * * \param ipVer the preferred IP version to discover (6 or 4) * \param only true to ignore other discovered IP versions */ std::vector getServerURLs(const int ipVer = 4, const bool only = false); private: SoapySocketSession sess; //protection between threads std::mutex mutex; //service settings bool serviceRegistered; std::string uuid; std::string service; //configured messages bool periodicSearchEnabled; bool periodicNotifyEnabled; //server data std::vector handlers; //signal done to the thread sig_atomic_t done; void spawnHandler(const std::string &bindAddr, const std::string &groupAddr, const int ipVer); void handlerLoop(SoapySSDPEndpointData *data); void sendHeader(SoapyRPCSocket &sock, const SoapyHTTPHeader &header, const std::string &addr); void sendSearchHeader(SoapySSDPEndpointData *data); void sendNotifyHeader(SoapySSDPEndpointData *data, const std::string &nts); void handleSearchRequest(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &addr); void handleSearchResponse(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &addr); void handleNotifyRequest(SoapySSDPEndpointData *data, const SoapyHTTPHeader &header, const std::string &addr); void handleRegisterService(SoapySSDPEndpointData *, const SoapyHTTPHeader &header, const std::string &recvAddr); }; SoapyRemote-soapy-remote-0.4.2/common/SoapySocketDefs.in.hpp000066400000000000000000000073251313763561200241000ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // SPDX-License-Identifier: BSL-1.0 // ** This header should be included first, to avoid compile errors. // ** At least in the case of the windows header files. // This header helps to abstract network differences between platforms. // Including the correct headers for various network APIs. // And providing various typedefs and definitions when missing. #pragma once /*********************************************************************** * Windows socket headers **********************************************************************/ #cmakedefine HAS_WINSOCK2_H #ifdef HAS_WINSOCK2_H #include //htonll #endif //HAS_WINSOCK2_H #cmakedefine HAS_WS2TCPIP_H #ifdef HAS_WS2TCPIP_H #include //addrinfo typedef int socklen_t; #endif //HAS_WS2TCPIP_H /*********************************************************************** * unix socket headers **********************************************************************/ #cmakedefine HAS_UNISTD_H #ifdef HAS_UNISTD_H #include //close #define closesocket close #endif //HAS_UNISTD_H #cmakedefine HAS_NETDB_H #ifdef HAS_NETDB_H #include //addrinfo #endif //HAS_NETDB_H #cmakedefine HAS_NETINET_IN_H #ifdef HAS_NETINET_IN_H #include #endif //HAS_NETINET_IN_H #cmakedefine HAS_NETINET_TCP_H #ifdef HAS_NETINET_TCP_H #include #endif //HAS_NETINET_TCP_H #cmakedefine HAS_SYS_TYPES_H #ifdef HAS_SYS_TYPES_H #include #endif //HAS_SYS_TYPES_H #cmakedefine HAS_SYS_SOCKET_H #ifdef HAS_SYS_SOCKET_H #include #endif //HAS_SYS_SOCKET_H #cmakedefine HAS_ARPA_INET_H #ifdef HAS_ARPA_INET_H #include //inet_ntop #endif //HAS_ARPA_INET_H #cmakedefine HAS_IFADDRS_H #ifdef HAS_IFADDRS_H #include //getifaddrs #endif //HAS_IFADDRS_H #cmakedefine HAS_NET_IF_H #ifdef HAS_NET_IF_H #include //if_nametoindex #endif //HAS_NET_IF_H #cmakedefine HAS_FCNTL_H #ifdef HAS_FCNTL_H #include //fcntl and constants #endif //HAS_FCNTL_H /*********************************************************************** * htonll and ntohll for GCC **********************************************************************/ #if defined(__GNUC__) && !defined(htonll) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define htonll(x) __builtin_bswap64(x) #else //big endian #define htonll(x) (x) #endif //little endian #endif //__GNUC__ and not htonll #if defined(__GNUC__) && !defined(ntohll) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define ntohll(x) __builtin_bswap64(x) #else //big endian #define ntohll(x) (x) #endif //little endian #endif //__GNUC__ and not ntohll /*********************************************************************** * socket type definitions **********************************************************************/ #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif //INVALID_SOCKET /*********************************************************************** * socket errno **********************************************************************/ #ifdef _MSC_VER #define SOCKET_ERRNO WSAGetLastError() #define SOCKET_EINPROGRESS WSAEWOULDBLOCK #define SOCKET_ETIMEDOUT WSAETIMEDOUT #else #define SOCKET_ERRNO errno #define SOCKET_EINPROGRESS EINPROGRESS #define SOCKET_ETIMEDOUT ETIMEDOUT #endif /*********************************************************************** * OSX compatibility **********************************************************************/ #if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP) #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif #if !defined(IPV6_DROP_MEMBERSHIP) && defined(IPV6_LEAVE_GROUP) #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif SoapyRemote-soapy-remote-0.4.2/common/SoapyStreamEndpoint.cpp000066400000000000000000000320351313763561200243640ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include #include #include "SoapyStreamEndpoint.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyURLUtils.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapySocketDefs.hpp" #include //min/max #include #include #define HEADER_SIZE sizeof(StreamDatagramHeader) //use the larger IPv6 header size #define PROTO_HEADER_SIZE (40 + 8) //IPv6 + UDP struct StreamDatagramHeader { uint32_t bytes; //!< total number of bytes in datagram uint32_t sequence; //!< sequence count for flow control uint32_t elems; //!< number of elements or error code int flags; //!< flags associated with this datagram long long time; //!< time associated with this datagram }; SoapyStreamEndpoint::SoapyStreamEndpoint( SoapyRPCSocket &streamSock, SoapyRPCSocket &statusSock, const bool datagramMode, const bool isRecv, const size_t numChans, const size_t elemSize, const size_t mtu, const size_t window): _streamSock(streamSock), _statusSock(statusSock), _datagramMode(datagramMode), _xferSize(mtu-PROTO_HEADER_SIZE), _numChans(numChans), _elemSize(elemSize), _buffSize(((_xferSize-HEADER_SIZE)/numChans)/elemSize), _numBuffs(SOAPY_REMOTE_ENDPOINT_NUM_BUFFS), _nextHandleAcquire(0), _nextHandleRelease(0), _numHandlesAcquired(0), _lastSendSequence(0), _lastRecvSequence(0), _maxInFlightSeqs(0), _receiveInitial(false), _triggerAckWindow(0) { assert(not _streamSock.null()); //allocate buffer data and default state _buffData.resize(_numBuffs); for (auto &data : _buffData) { data.acquired = false; data.buff.resize(_xferSize); data.buffs.resize(_numChans); for (size_t i = 0; i < _numChans; i++) { size_t offsetBytes = HEADER_SIZE+(i*_buffSize*_elemSize); data.buffs[i] = (void*)(data.buff.data()+offsetBytes); } } //endpoints require a large socket buffer in the data direction int ret = _streamSock.setBuffSize(isRecv, window); if (ret != 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint resize socket buffer to %d KiB failed\n %s", int(window/1024), _streamSock.lastErrorMsg()); } //log when the size is not expected, users may have to tweak system parameters int actualWindow = _streamSock.getBuffSize(isRecv); if (actualWindow < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint get socket buffer size failed\n %s", _streamSock.lastErrorMsg()); actualWindow = window; } else if (size_t(actualWindow) < window) { SoapySDR::logf(SOAPY_SDR_WARNING, "StreamEndpoint resize socket buffer: set %d KiB, got %d KiB", int(window/1024), int(actualWindow/1024)); } //print summary SoapySDR::logf(SOAPY_SDR_INFO, "Configured %s endpoint: dgram=%d bytes, %d elements @ %d bytes, window=%d KiB", isRecv?"receiver":"sender", int(_xferSize), int(_buffSize*_numChans), int(_elemSize), int(actualWindow/1024)); //calculate flow control window if (isRecv) { //calculate maximum in-flight sequences allowed _maxInFlightSeqs = actualWindow/mtu; //calculate the flow control ACK conditions _triggerAckWindow = _maxInFlightSeqs/_numBuffs; //send gratuitous ack to set sender's window this->sendACK(); } else { //_maxInFlightSeqs set by flow control packet } } SoapyStreamEndpoint::~SoapyStreamEndpoint(void) { return; } void SoapyStreamEndpoint::sendACK(void) { StreamDatagramHeader header; header.bytes = htonl(sizeof(header)); header.sequence = htonl(_lastRecvSequence); header.elems = htonl(_maxInFlightSeqs); header.flags = htonl(0); header.time = htonll(0); //send the flow control ACK int ret = _streamSock.send(&header, sizeof(header)); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::sendACK(), FAILED %s", _streamSock.lastErrorMsg()); } else if (size_t(ret) != sizeof(header)) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::sendACK(%d bytes), FAILED %d", int(sizeof(header)), ret); } //update last flow control ACK state _lastSendSequence = _lastRecvSequence; } void SoapyStreamEndpoint::recvACK(void) { StreamDatagramHeader header; int ret = _streamSock.recv(&header, sizeof(header)); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::recvACK(), FAILED %s", _streamSock.lastErrorMsg()); } _receiveInitial = true; //check the header size_t bytes = ntohl(header.bytes); if (bytes > size_t(ret)) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::recvACK(%d bytes), FAILED %d", int(bytes), ret); } _lastRecvSequence = ntohl(header.sequence); _maxInFlightSeqs = ntohl(header.elems); } /*********************************************************************** * receive endpoint implementation **********************************************************************/ bool SoapyStreamEndpoint::waitRecv(const long timeoutUs) { //send gratuitous ack until something is received if (not _receiveInitial) this->sendACK(); return _streamSock.selectRecv(timeoutUs); } int SoapyStreamEndpoint::acquireRecv(size_t &handle, const void **buffs, int &flags, long long &timeNs) { int ret = 0; //no available handles, the user is hoarding them... if (_numHandlesAcquired == _buffData.size()) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::acquireRecv() -- all buffers acquired"); return SOAPY_SDR_STREAM_ERROR; } //grab the current handle handle = _nextHandleAcquire; auto &data = _buffData[handle]; //receive into the buffer assert(not _streamSock.null()); if (_datagramMode) ret = _streamSock.recv(data.buff.data(), data.buff.size()); else ret = _streamSock.recv(data.buff.data(), HEADER_SIZE, MSG_WAITALL); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::acquireRecv(), FAILED %s", _streamSock.lastErrorMsg()); return SOAPY_SDR_STREAM_ERROR; } size_t bytesRecvd = size_t(ret); _receiveInitial = true; //check the header auto header = (const StreamDatagramHeader*)data.buff.data(); size_t bytes = ntohl(header->bytes); if (_datagramMode and bytes > bytesRecvd) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::acquireRecv(%d bytes), FAILED %d\n" "This MTU setting may be unachievable. Check network configuration.", int(bytes), ret); return SOAPY_SDR_STREAM_ERROR; } else while (bytesRecvd < bytes) { ret = _streamSock.recv(data.buff.data()+bytesRecvd, std::min(SOAPY_REMOTE_SOCKET_BUFFMAX, bytes-bytesRecvd)); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::acquireRecv(), FAILED %s", _streamSock.lastErrorMsg()); return SOAPY_SDR_STREAM_ERROR; } bytesRecvd += size_t(ret); } const int numElemsOrErr = int(ntohl(header->elems)); //dropped or out of order packets //TODO return an error code, more than a notification if (uint32_t(_lastRecvSequence) != uint32_t(ntohl(header->sequence))) { SoapySDR::log(SOAPY_SDR_SSI, "S"); } //update flow control _lastRecvSequence = ntohl(header->sequence)+1; //has there been at least trigger window number of sequences since the last ACK? if (uint32_t(_lastRecvSequence-_lastSendSequence) >= _triggerAckWindow) { this->sendACK(); } //increment for next handle if (numElemsOrErr >= 0) { data.acquired = true; _nextHandleAcquire = (_nextHandleAcquire + 1)%_numBuffs; _numHandlesAcquired++; } //set output parameters this->getAddrs(handle, (void **)buffs); flags = ntohl(header->flags); timeNs = ntohll(header->time); return numElemsOrErr; } void SoapyStreamEndpoint::releaseRecv(const size_t handle) { auto &data = _buffData[handle]; data.acquired = false; //actually release in order of handle index while (_numHandlesAcquired != 0) { if (_buffData[_nextHandleRelease].acquired) break; _nextHandleRelease = (_nextHandleRelease + 1)%_numBuffs; _numHandlesAcquired--; } } /*********************************************************************** * send endpoint implementation **********************************************************************/ bool SoapyStreamEndpoint::waitSend(const long timeoutUs) { //are we within the allowed number of sequences in flight? while (not _receiveInitial or uint32_t(_lastSendSequence-_lastRecvSequence) >= _maxInFlightSeqs) { //wait for a flow control ACK to arrive if (not _streamSock.selectRecv(timeoutUs)) return false; //exhaustive receive without timeout while (_streamSock.selectRecv(0)) this->recvACK(); } return true; } int SoapyStreamEndpoint::acquireSend(size_t &handle, void **buffs) { //no available handles, the user is hoarding them... if (_numHandlesAcquired == _buffData.size()) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::acquireSend() -- all buffers acquired"); return SOAPY_SDR_STREAM_ERROR; } //grab the current handle handle = _nextHandleAcquire; auto &data = _buffData[handle]; //increment for next handle data.acquired = true; _nextHandleAcquire = (_nextHandleAcquire + 1)%_numBuffs; _numHandlesAcquired++; //set output parameters this->getAddrs(handle, buffs); return int(_buffSize); } void SoapyStreamEndpoint::releaseSend(const size_t handle, const int numElemsOrErr, int &flags, const long long timeNs) { auto &data = _buffData[handle]; data.acquired = false; //The first N-1 channels must be complete buffSize sends //due to the pointer allocation at initialization time. //The last channel can be shortened to the available numElems. const size_t totalElems = ((_numChans-1)*_buffSize) + numElemsOrErr; //load the header auto header = (StreamDatagramHeader*)data.buff.data(); size_t bytes = HEADER_SIZE + ((numElemsOrErr < 0)?0:(totalElems*_elemSize)); header->bytes = htonl(bytes); header->sequence = htonl(_lastSendSequence++); header->elems = htonl(numElemsOrErr); header->flags = htonl(flags); header->time = htonll(timeNs); //send from the buffer assert(not _streamSock.null()); size_t bytesSent = 0; while (bytesSent < bytes) { int ret = _streamSock.send(data.buff.data()+bytesSent, std::min(SOAPY_REMOTE_SOCKET_BUFFMAX, bytes-bytesSent)); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::releaseSend(), FAILED %s", _streamSock.lastErrorMsg()); break; } bytesSent += size_t(ret); if (not _datagramMode) continue; if (bytesSent != bytes) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::releaseSend(%d bytes), FAILED %d", int(bytes), ret); } } //actually release in order of handle index while (_numHandlesAcquired != 0) { if (_buffData[_nextHandleRelease].acquired) break; _nextHandleRelease = (_nextHandleRelease + 1)%_numBuffs; _numHandlesAcquired--; } } /*********************************************************************** * status endpoint implementation -- used by both directions **********************************************************************/ bool SoapyStreamEndpoint::waitStatus(const long timeoutUs) { return _statusSock.selectRecv(timeoutUs); } int SoapyStreamEndpoint::readStatus(size_t &chanMask, int &flags, long long &timeNs) { StreamDatagramHeader header; //read the status assert(not _statusSock.null()); int ret = _statusSock.recv(&header, sizeof(header)); if (ret < 0) return SOAPY_SDR_STREAM_ERROR; //check the header size_t bytes = ntohl(header.bytes); if (bytes > size_t(ret)) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::readStatus(%d bytes), FAILED %d", int(bytes), ret); return SOAPY_SDR_STREAM_ERROR; } //set output parameters chanMask = ntohl(header.sequence); flags = ntohl(header.flags); timeNs = ntohll(header.time); return int(ntohl(header.elems)); } void SoapyStreamEndpoint::writeStatus(const int code, const size_t chanMask, const int flags, const long long timeNs) { StreamDatagramHeader header; header.bytes = htonl(sizeof(header)); header.sequence = htonl(chanMask); header.flags = htonl(flags); header.time = htonll(timeNs); header.elems = htonl(code); //send the status assert(not _statusSock.null()); int ret = _statusSock.send(&header, sizeof(header)); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::writeStatus(), FAILED %s", _statusSock.lastErrorMsg()); } else if (size_t(ret) != sizeof(header)) { SoapySDR::logf(SOAPY_SDR_ERROR, "StreamEndpoint::writeStatus(%d bytes), FAILED %d", int(sizeof(header)), ret); } } SoapyRemote-soapy-remote-0.4.2/common/SoapyStreamEndpoint.hpp000066400000000000000000000101541313763561200243670ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include #include class SoapyRPCSocket; /*! * The stream endpoint supports a windowed link datagram protocol. * This endpoint can be operated in only one mode: receive or send, * and must be paired with another differently configured endpoint. */ class SOAPY_REMOTE_API SoapyStreamEndpoint { public: SoapyStreamEndpoint( SoapyRPCSocket &streamSock, SoapyRPCSocket &statusSock, const bool datagramMode, const bool isRecv, const size_t numChans, const size_t elemSize, const size_t mtu, const size_t window); ~SoapyStreamEndpoint(void); //! How many channels configured size_t getNumChans(void) const { return _numChans; } //! Element size in bytes size_t getElemSize(void) const { return _elemSize; } //! Actual buffer size in elements size_t getBuffSize(void) const { return _buffSize; } //! Actual number of buffers size_t getNumBuffs(void) const { return _numBuffs; } //! Query handle addresses void getAddrs(const size_t handle, void **buffs) const { for (size_t i = 0; i < _numChans; i++) { buffs[i] = _buffData[handle].buffs[i]; } } /******************************************************************* * receive endpoint API ******************************************************************/ /*! * Wait for a datagram to arrive at the socket * return true when ready for false for timeout. */ bool waitRecv(const long timeoutUs); /*! * Acquire a receive buffer with metadata. * return the number of elements or error code */ int acquireRecv(size_t &handle, const void **buffs, int &flags, long long &timeNs); /*! * Release the buffer when done. */ void releaseRecv(const size_t handle); /******************************************************************* * send endpoint API ******************************************************************/ /*! * Wait for the flow control to allow transmission. * return true when ready for false for timeout. */ bool waitSend(const long timeoutUs); /*! * Acquire a receive buffer with metadata. */ int acquireSend(size_t &handle, void **buffs); /*! * Release the buffer when done. * pass in the number of elements or error code */ void releaseSend(const size_t handle, const int numElemsOrErr, int &flags, const long long timeNs); /******************************************************************* * status endpoint API -- used by both directions ******************************************************************/ /*! * Wait for a status message to arrive */ bool waitStatus(const long timeoutUs); /*! * Read the stream status data. * Return 0 or error code. */ int readStatus(size_t &chanMask, int &flags, long long &timeNs); /*! * Write the stream status from the forwarder. */ void writeStatus(const int code, const size_t chanMask, const int flags, const long long timeNs); private: SoapyRPCSocket &_streamSock; SoapyRPCSocket &_statusSock; const bool _datagramMode; const size_t _xferSize; const size_t _numChans; const size_t _elemSize; const size_t _buffSize; const size_t _numBuffs; struct BufferData { std::vector buff; //actual POD std::vector buffs; //pointers bool acquired; }; std::vector _buffData; //acquire+release tracking size_t _nextHandleAcquire; size_t _nextHandleRelease; size_t _numHandlesAcquired; //sequence tracking size_t _lastSendSequence; size_t _lastRecvSequence; size_t _maxInFlightSeqs; bool _receiveInitial; //how often to send a flow control ACK? (recv only) size_t _triggerAckWindow; //flow control helpers void sendACK(void); void recvACK(void); }; SoapyRemote-soapy-remote-0.4.2/common/SoapyURLUtils.cpp000066400000000000000000000116261313763561200231160ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapySocketDefs.hpp" #include "SoapyURLUtils.hpp" #include //memset #include #include SockAddrData::SockAddrData(void) { return; } SockAddrData::SockAddrData(const struct sockaddr *addr, const int addrlen) { _storage.resize(addrlen); std::memcpy(_storage.data(), addr, addrlen); } const struct sockaddr *SockAddrData::addr(void) const { return (const struct sockaddr *)_storage.data(); } size_t SockAddrData::addrlen(void) const { return _storage.size(); } SoapyURL::SoapyURL(void) { return; } SoapyURL::SoapyURL(const std::string &scheme, const std::string &node, const std::string &service): _scheme(scheme), _node(node), _service(service) { return; } SoapyURL::SoapyURL(const std::string &url) { //extract the scheme std::string urlRest = url; auto schemeEnd = url.find("://"); if (schemeEnd != std::string::npos) { _scheme = url.substr(0, schemeEnd); urlRest = url.substr(schemeEnd+3); } //extract node name and service port bool inBracket = false; bool inService = false; for (size_t i = 0; i < urlRest.size(); i++) { const char ch = urlRest[i]; if (inBracket and ch == ']') { inBracket = false; continue; } if (not inBracket and ch == '[') { inBracket = true; continue; } if (inBracket) { _node += ch; continue; } if (inService) { _service += ch; continue; } if (not inService and ch == ':') { inService = true; continue; } if (not inService) { _node += ch; continue; } } } SoapyURL::SoapyURL(const SockAddrData &addr) { char *s = NULL; switch(addr.addr()->sa_family) { case AF_INET: { auto *addr_in = (const struct sockaddr_in *)addr.addr(); s = (char *)malloc(INET_ADDRSTRLEN); inet_ntop(AF_INET, (void *)&(addr_in->sin_addr), s, INET_ADDRSTRLEN); _node = s; _service = std::to_string(ntohs(addr_in->sin_port)); break; } case AF_INET6: { auto *addr_in6 = (const struct sockaddr_in6 *)addr.addr(); s = (char *)malloc(INET6_ADDRSTRLEN); inet_ntop(AF_INET6, (void *)&(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN); _node = s; //support scoped address node if (addr_in6->sin6_scope_id != 0) { _node += "%" + std::to_string(addr_in6->sin6_scope_id); } _service = std::to_string(ntohs(addr_in6->sin6_port)); break; } default: break; } free(s); } std::string SoapyURL::toSockAddr(SockAddrData &addr) const { SockAddrData result; //unspecified service, cant continue if (_service.empty()) return "service not specified"; //configure the hint struct addrinfo hints, *servinfo = NULL; std::memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6 hints.ai_socktype = this->getType(); //get address info int ret = getaddrinfo(_node.c_str(), _service.c_str(), &hints, &servinfo); if (ret != 0) return gai_strerror(ret); //iterate through possible matches struct addrinfo *p = NULL; for (p = servinfo; p != NULL; p = p->ai_next) { //eliminate unsupported family types if (p->ai_family != AF_INET and p->ai_family != AF_INET6) continue; //found a match assert(p->ai_family == p->ai_addr->sa_family); addr = SockAddrData(p->ai_addr, p->ai_addrlen); break; } //cleanup freeaddrinfo(servinfo); //no results if (p == NULL) return "no lookup results"; return ""; //OK } std::string SoapyURL::toString(void) const { std::string url; //add the scheme if (not _scheme.empty()) url += _scheme + "://"; //add the node with ipv6 escape brackets if (_node.find(":") != std::string::npos) url += "[" + _node + "]"; else url += _node; //and the service if (not _service.empty()) url += ":" + _service; return url; } std::string SoapyURL::getScheme(void) const { return _scheme; } std::string SoapyURL::getNode(void) const { return _node; } std::string SoapyURL::getService(void) const { return _service; } void SoapyURL::setScheme(const std::string &scheme) { _scheme = scheme; } void SoapyURL::setNode(const std::string &node) { _node = node; } void SoapyURL::setService(const std::string &service) { _service = service; } int SoapyURL::getType(void) const { if (_scheme == "tcp") return SOCK_STREAM; if (_scheme == "udp") return SOCK_DGRAM; return SOCK_STREAM; //assume } SoapyRemote-soapy-remote-0.4.2/common/SoapyURLUtils.hpp000066400000000000000000000036311313763561200231200ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRemoteConfig.hpp" #include #include #include //forward declares struct sockaddr; //! A simple storage class for a sockaddr class SOAPY_REMOTE_API SockAddrData { public: //! Create an empty socket address SockAddrData(void); //! Create a socket address from a pointer and length SockAddrData(const struct sockaddr *addr, const int addrlen); //! Get a pointer to the underlying data const struct sockaddr *addr(void) const; //! Length of the underlying structure size_t addrlen(void) const; private: std::vector _storage; }; /*! * URL parsing, manipulation, lookup. */ class SOAPY_REMOTE_API SoapyURL { public: //! Create empty url object SoapyURL(void); //! Create URL from components SoapyURL(const std::string &scheme, const std::string &node, const std::string &service); //! Parse from url markup string SoapyURL(const std::string &url); //! Create URL from socket address SoapyURL(const SockAddrData &addr); /*! * Convert to socket address + resolve address. * Return the error message on failure. */ std::string toSockAddr(SockAddrData &addr) const; /*! * Convert to URL string markup. */ std::string toString(void) const; //! Get the scheme std::string getScheme(void) const; //! Get the node std::string getNode(void) const; //! Get the service std::string getService(void) const; //! Set the scheme void setScheme(const std::string &scheme); //! Set the node void setNode(const std::string &node); //! Set the service void setService(const std::string &service); //! Get the socket type from the scheme int getType(void) const; private: std::string _scheme; std::string _node; std::string _service; }; SoapyRemote-soapy-remote-0.4.2/debian/000077500000000000000000000000001313763561200176375ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/debian/changelog000066400000000000000000000024401313763561200215110ustar00rootroot00000000000000soapyremote (0.4.2-1) unstable; urgency=low * Release 0.4.2 (2017-07-31) -- Josh Blum Mon, 31 Jul 2017 09:15:34 -0000 soapyremote (0.4.1-1) unstable; urgency=low * Release 0.4.1 (2017-06-08) -- Josh Blum Thu, 08 Jun 2017 18:18:32 -0000 soapyremote (0.4.0-1) unstable; urgency=low * Release 0.4.0 (2017-04-29) -- Josh Blum Sat, 29 Apr 2017 10:11:25 -0000 soapyremote (0.3.2-1) unstable; urgency=low * Release 0.3.2 (2016-12-04) -- Josh Blum Sun, 04 Dec 2016 18:14:54 -0800 soapyremote (0.3.1) unstable; urgency=low * Release 0.3.1 (2016-09-01) -- Josh Blum Thu, 01 Sep 2016 21:02:42 -0700 soapyremote (0.3.0) unstable; urgency=low * Release 0.3.0 (2016-07-10) -- Josh Blum Sun, 10 Jul 2016 16:48:12 -0700 soapyremote (0.2.1) unstable; urgency=low * Release 0.2.1 (2016-04-21) -- Josh Blum Thu, 21 Apr 2016 14:50:37 -0400 soapyremote (0.2.0) unstable; urgency=low * Release 0.2.0 (2015-11-21) -- Josh Blum Thu, 15 Oct 2015 19:29:23 -0700 soapyremote (0.1.0) unstable; urgency=low * Release 0.1.0 (2015-10-10) -- Josh Blum Sat, 10 Oct 2015 11:08:35 -0700 SoapyRemote-soapy-remote-0.4.2/debian/compat000066400000000000000000000000021313763561200210350ustar00rootroot000000000000009 SoapyRemote-soapy-remote-0.4.2/debian/control000066400000000000000000000022761313763561200212510ustar00rootroot00000000000000Source: soapyremote Section: libs Priority: optional Maintainer: Josh Blum Build-Depends: debhelper (>= 9.0.0), cmake, libsoapysdr-dev (>= 0.3.0) Standards-Version: 3.9.8 Homepage: https://github.com/pothosware/SoapyRemote/wiki Vcs-Git: https://github.com/pothosware/SoapyRemote.git Vcs-Browser: https://github.com/pothosware/SoapyRemote Package: soapysdr0.6-module-remote Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: Soapy Remote - Remote device support for Soapy SDR. A Soapy module that supports remote devices within the Soapy API. Package: soapysdr-module-remote Architecture: all Depends: soapysdr0.6-module-remote, ${misc:Depends} Description: Soapy Remote - Remote device support for Soapy SDR. A Soapy module that supports remote devices within the Soapy API. . This is an empty dependency package that pulls in the remote module for the default version of libsoapysdr. Package: soapysdr-server Section: libs Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, init-system-helpers Description: Soapy Remote - Remote device support for Soapy SDR. The SoapySDRServer server application for remote devices. SoapyRemote-soapy-remote-0.4.2/debian/copyright000066400000000000000000000032001313763561200215650ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: soapyremote Source: https://github.com/pothosware/SoapyRemote/wiki Files: * Copyright: Copyright (c) 2015-2017 Josh Blum Copyright (c) 2016-2016 Bastille Networks License: BSL-1.0 Boost Software License - Version 1.0 - August 17th, 2003 . Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: . The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SoapyRemote-soapy-remote-0.4.2/debian/docs000066400000000000000000000000121313763561200205030ustar00rootroot00000000000000README.md SoapyRemote-soapy-remote-0.4.2/debian/rules000077500000000000000000000006161313763561200207220ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) export DEB_HOST_MULTIARCH # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --buildsystem=cmake --parallel override_dh_auto_configure: dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)" override_dh_installchangelogs: dh_installchangelogs Changelog.txt SoapyRemote-soapy-remote-0.4.2/debian/soapysdr-server.install000066400000000000000000000001231313763561200243730ustar00rootroot00000000000000usr/bin/ #systemd-service-file-outside-lib usr/lib/systemd/ lib/ usr/lib/sysctl.d/ SoapyRemote-soapy-remote-0.4.2/debian/soapysdr-server.manpages000066400000000000000000000000301313763561200245150ustar00rootroot00000000000000server/SoapySDRServer.1 SoapyRemote-soapy-remote-0.4.2/debian/soapysdr-server.postinst000066400000000000000000000004431313763561200246150ustar00rootroot00000000000000#!/bin/sh set -e if [ "$1" = "configure" ]; then #Load the new sysctl limits so they are immediately usable. #Otherwise, the limits will be always loaded at boot time. if [ -x "`which sysctl 2>/dev/null`" ]; then sysctl --load /usr/lib/sysctl.d/SoapySDRServer.conf fi fi #DEBHELPER# SoapyRemote-soapy-remote-0.4.2/debian/soapysdr-server.postrm000066400000000000000000000004271313763561200242600ustar00rootroot00000000000000#!/bin/sh set -e if [ "$1" = "remove" ]; then deb-systemd-helper mask SoapySDRServer.service > /dev/null fi if [ "$1" = "remove" ]; then deb-systemd-helper purge SoapySDRServer.service > /dev/null deb-systemd-helper unmask SoapySDRServer.service > /dev/null fi #DEBHELPER# SoapyRemote-soapy-remote-0.4.2/debian/soapysdr-server.prerm000066400000000000000000000001731313763561200240570ustar00rootroot00000000000000#!/bin/sh set -e if [ "$1" = "remove" ]; then deb-systemd-invoke stop SoapySDRServer.service > /dev/null fi #DEBHELPER# SoapyRemote-soapy-remote-0.4.2/debian/soapysdr0.6-module-remote.install000066400000000000000000000000341313763561200260700ustar00rootroot00000000000000usr/lib/*/SoapySDR/modules* SoapyRemote-soapy-remote-0.4.2/debian/source/000077500000000000000000000000001313763561200211375ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/debian/source/format000066400000000000000000000000141313763561200223450ustar00rootroot000000000000003.0 (quilt) SoapyRemote-soapy-remote-0.4.2/server/000077500000000000000000000000001313763561200177235ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/server/CMakeLists.txt000066400000000000000000000023071313763561200224650ustar00rootroot00000000000000######################################################################## # Thread config support ######################################################################## find_library( RT_LIBRARIES NAMES rt PATHS /usr/lib /usr/lib64 ) if (RT_LIBRARIES) list(APPEND SoapySDR_LIBRARIES ${RT_LIBRARIES}) endif() if(WIN32) list(APPEND SOAPY_SERVER_SOURCES ThreadPrioWindows.cpp) elseif(UNIX) list(APPEND SOAPY_SERVER_SOURCES ThreadPrioUnix.cpp) endif() ######################################################################## # Build the remote server application ######################################################################## list(APPEND SOAPY_SERVER_SOURCES SoapyServer.cpp ServerListener.cpp ClientHandler.cpp LogForwarding.cpp ServerStreamData.cpp ) if (MSVC) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/msvc) endif () include_directories(${SoapySDR_INCLUDE_DIRS}) add_executable(SoapySDRServer ${SOAPY_SERVER_SOURCES}) target_link_libraries(SoapySDRServer ${SoapySDR_LIBRARIES} SoapySDRRemoteCommon) install(TARGETS SoapySDRServer DESTINATION bin) #install man pages for the application executable install(FILES SoapySDRServer.1 DESTINATION share/man/man1) SoapyRemote-soapy-remote-0.4.2/server/ClientHandler.cpp000066400000000000000000001440671313763561200231570ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0 #include "ClientHandler.hpp" #include "ServerStreamData.hpp" #include "LogForwarding.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyURLUtils.hpp" #include "SoapyRPCSocket.hpp" #include "SoapyRPCPacker.hpp" #include "SoapyRPCUnpacker.hpp" #include "SoapyStreamEndpoint.hpp" #include #include #include #include #include #include //! The device factory make and unmake requires a process-wide mutex static std::mutex factoryMutex; /*********************************************************************** * Client handler constructor **********************************************************************/ SoapyClientHandler::SoapyClientHandler(SoapyRPCSocket &sock, const std::string &uuid): _sock(sock), _uuid(uuid), _dev(nullptr), _logForwarder(nullptr), _nextStreamId(0) { return; } SoapyClientHandler::~SoapyClientHandler(void) { //stop all stream threads and close streams for (auto &data : _streamData) { data.second.stopThreads(); _dev->closeStream(data.second.stream); } //release the device handle if we have it if (_dev != nullptr) { std::lock_guard lock(factoryMutex); SoapySDR::Device::unmake(_dev); _dev = nullptr; } //finally stop and cleanup log forwarding delete _logForwarder; } /*********************************************************************** * Transaction handler **********************************************************************/ bool SoapyClientHandler::handleOnce(void) { if (not _sock.selectRecv(SOAPY_REMOTE_SOCKET_TIMEOUT_US)) return true; //receive the client's request SoapyRPCUnpacker unpacker(_sock, true, -1/*no timeout*/); SoapyRPCPacker packer(_sock, unpacker.remoteRPCVersion()); //handle the client's request bool again = true; try { again = this->handleOnce(unpacker, packer); } catch (const std::exception &ex) { packer & ex; } //send the result back packer(); return again; } /*********************************************************************** * Handler dispatcher implementation **********************************************************************/ bool SoapyClientHandler::handleOnce(SoapyRPCUnpacker &unpacker, SoapyRPCPacker &packer) { SoapyRemoteCalls call; unpacker & call; switch (call) { //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_FIND: //////////////////////////////////////////////////////////////////// { SoapySDR::Kwargs args; unpacker & args; packer & SoapySDR::Device::enumerate(args); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_MAKE: //////////////////////////////////////////////////////////////////// { SoapySDR::Kwargs args; unpacker & args; std::lock_guard lock(factoryMutex); if (_dev == nullptr) _dev = SoapySDR::Device::make(args); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_UNMAKE: //////////////////////////////////////////////////////////////////// { //stop all stream threads and close streams if (not _streamData.empty()) { SoapySDR::log(SOAPY_SDR_WARNING, "Performing automatic closeStream() before Device unmake."); } for (auto &data : _streamData) { data.second.stopThreads(); _dev->closeStream(data.second.stream); } _streamData.clear(); std::lock_guard lock(factoryMutex); if (_dev != nullptr) SoapySDR::Device::unmake(_dev); _dev = nullptr; packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HANGUP: //////////////////////////////////////////////////////////////////// { packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_SERVER_ID: //////////////////////////////////////////////////////////////////// { packer & _uuid; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_START_LOG_FORWARDING: //////////////////////////////////////////////////////////////////// { if (_logForwarder == nullptr) _logForwarder = new SoapyLogForwarder(_sock); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_STOP_LOG_FORWARDING: //////////////////////////////////////////////////////////////////// { if (_logForwarder != nullptr) delete _logForwarder; _logForwarder = nullptr; packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_DRIVER_KEY: //////////////////////////////////////////////////////////////////// { packer & _dev->getDriverKey(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_HARDWARE_KEY: //////////////////////////////////////////////////////////////////// { packer & _dev->getHardwareKey(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_HARDWARE_INFO: //////////////////////////////////////////////////////////////////// { packer & _dev->getHardwareInfo(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_FRONTEND_MAPPING: //////////////////////////////////////////////////////////////////// { char direction = 0; std::string mapping; unpacker & direction; unpacker & mapping; _dev->setFrontendMapping(direction, mapping); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FRONTEND_MAPPING: //////////////////////////////////////////////////////////////////// { char direction = 0; unpacker & direction; packer & _dev->getFrontendMapping(direction); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_NUM_CHANNELS: //////////////////////////////////////////////////////////////////// { char direction = 0; unpacker & direction; packer & int(_dev->getNumChannels(direction)); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FULL_DUPLEX: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getFullDuplex(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_CHANNEL_INFO: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_GET_CHANNEL_INFO packer & _dev->getChannelInfo(direction, channel); #else SoapySDR::Kwargs result; packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_STREAM_FORMATS: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getStreamFormats(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_NATIVE_STREAM_FORMAT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; double fullScale = 0.0; packer & _dev->getNativeStreamFormat(direction, channel, fullScale); packer & fullScale; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_STREAM_ARGS_INFO: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getStreamArgsInfo(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SETUP_STREAM: //////////////////////////////////////////////////////////////////// { char direction = 0; std::string format; std::vector channels; SoapySDR::Kwargs args; std::string clientBindPort; std::string statusBindPort; unpacker & direction; unpacker & format; unpacker & channels; unpacker & args; unpacker & clientBindPort; unpacker & statusBindPort; //parse args for buffer configuration size_t mtu = SOAPY_REMOTE_DEFAULT_ENDPOINT_MTU; const auto mtuIt = args.find(SOAPY_REMOTE_KWARG_MTU); if (mtuIt != args.end()) mtu = size_t(std::stod(mtuIt->second)); size_t window = SOAPY_REMOTE_DEFAULT_ENDPOINT_WINDOW; const auto windowIt = args.find(SOAPY_REMOTE_KWARG_WINDOW); if (windowIt != args.end()) window = size_t(std::stod(windowIt->second)); double priority = SOAPY_REMOTE_DEFAULT_THREAD_PRIORITY; const auto priorityIt = args.find(SOAPY_REMOTE_KWARG_PRIORITY); if (priorityIt != args.end()) priority = std::stod(priorityIt->second); std::string prot = "udp"; const auto protIt = args.find(SOAPY_REMOTE_KWARG_PROT); if (protIt != args.end()) prot = protIt->second; const bool datagramMode = (prot == "udp"); //create stream auto stream = _dev->setupStream(direction, format, channels, args); //load data structure auto &data = _streamData[_nextStreamId]; data.streamId = _nextStreamId++; data.device = _dev; data.stream = stream; data.format = format; for (const auto chan : channels) data.chanMask |= (1 << chan); data.priority = priority; //extract socket node information const auto localNode = SoapyURL(_sock.getsockname()).getNode(); const auto remoteNode = SoapyURL(_sock.getpeername()).getNode(); const auto bindURL = SoapyURL(prot, localNode, "0").toString(); std::string serverBindPort; //in udp mode connect to the bound sockets on the client side if (datagramMode) { data.streamSock = new SoapyRPCSocket(); data.statusSock = new SoapyRPCSocket(); //bind the stream socket to an automatic port int ret = data.streamSock->bind(bindURL); if (ret != 0) { const std::string errorMsg = data.streamSock->lastErrorMsg(); _streamData.erase(data.streamId); throw std::runtime_error("SoapyRemote::setupStream("+bindURL+") -- bind FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Server side stream bound to %s", data.streamSock->getsockname().c_str()); serverBindPort = SoapyURL(data.streamSock->getsockname()).getService(); //connect the stream socket to the specified port auto connectURL = SoapyURL("udp", remoteNode, clientBindPort).toString(); ret = data.streamSock->connect(connectURL); if (ret != 0) { const std::string errorMsg = data.streamSock->lastErrorMsg(); _streamData.erase(data.streamId); throw std::runtime_error("SoapyRemote::setupStream("+connectURL+") -- connect FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Server side stream connected to %s", data.streamSock->getpeername().c_str()); //connect the status socket to the specified port connectURL = SoapyURL("udp", remoteNode, statusBindPort).toString(); ret = data.statusSock->connect(connectURL); if (ret != 0) { const std::string errorMsg = data.statusSock->lastErrorMsg(); _streamData.erase(data.streamId); throw std::runtime_error("SoapyRemote::setupStream("+connectURL+") -- connect FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Server side status connected to %s", data.statusSock->getpeername().c_str()); } //in tcp mode, setup the server socket to listen, //send the binding port back to the client and //accept the client's new connections else { SoapyRPCSocket serverSocket; int ret = serverSocket.bind(bindURL); if (ret != 0) { const std::string errorMsg = serverSocket.lastErrorMsg(); _streamData.erase(data.streamId); throw std::runtime_error("SoapyRemote::setupStream("+bindURL+") -- bind FAIL: " + errorMsg); } SoapySDR::logf(SOAPY_SDR_INFO, "Server side stream bound to %s", serverSocket.getsockname().c_str()); serverBindPort = SoapyURL(serverSocket.getsockname()).getService(); serverSocket.listen(2); SoapyRPCPacker packerTcp(_sock); packerTcp & serverBindPort; packerTcp(); data.streamSock = serverSocket.accept(); data.statusSock = serverSocket.accept(); if (data.streamSock == nullptr or data.statusSock == nullptr) { const std::string errorMsg = serverSocket.lastErrorMsg(); throw std::runtime_error("SoapyRemote::setupStream("+bindURL+") -- accept FAIL: " + errorMsg); } } //create endpoint data.endpoint = new SoapyStreamEndpoint(*data.streamSock, *data.statusSock, datagramMode, direction == SOAPY_SDR_TX, channels.size(), SoapySDR::formatToSize(format), mtu, window); //start worker thread, this is not backwards, //receive from device means using a send endpoint //transmit to device means using a recv endpoint if (direction == SOAPY_SDR_RX) data.startSendThread(); if (direction == SOAPY_SDR_TX) data.startRecvThread(); data.startStatThread(); packer & data.streamId; packer & serverBindPort; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_CLOSE_STREAM: //////////////////////////////////////////////////////////////////// { int streamId = 0; unpacker & streamId; //cleanup data and stop worker thread auto &data = _streamData.at(streamId); data.stopThreads(); _dev->closeStream(data.stream); _streamData.erase(streamId); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_ACTIVATE_STREAM: //////////////////////////////////////////////////////////////////// { int streamId = 0; int flags = 0; long long timeNs = 0; int numElems = 0; unpacker & streamId; unpacker & flags; unpacker & timeNs; unpacker & numElems; auto &data = _streamData.at(streamId); packer & _dev->activateStream(data.stream, flags, timeNs, size_t(numElems)); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_DEACTIVATE_STREAM: //////////////////////////////////////////////////////////////////// { int streamId = 0; int flags = 0; long long timeNs = 0; unpacker & streamId; unpacker & flags; unpacker & timeNs; auto &data = _streamData.at(streamId); packer & _dev->deactivateStream(data.stream, flags, timeNs); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_ANTENNAS: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listAntennas(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_ANTENNA: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; _dev->setAntenna(direction, channel, name); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_ANTENNA: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getAntenna(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_DC_OFFSET_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->hasDCOffsetMode(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_DC_OFFSET_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; bool automatic = false; unpacker & direction; unpacker & channel; unpacker & automatic; _dev->setDCOffsetMode(direction, channel, automatic); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_DC_OFFSET_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getDCOffsetMode(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_DC_OFFSET: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->hasDCOffset(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_DC_OFFSET: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::complex offset; unpacker & direction; unpacker & channel; unpacker & offset; _dev->setDCOffset(direction, channel, offset); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_DC_OFFSET: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getDCOffset(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_IQ_BALANCE_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->hasIQBalance(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_IQ_BALANCE_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::complex balance; unpacker & direction; unpacker & channel; unpacker & balance; _dev->setIQBalance(direction, channel, balance); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_IQ_BALANCE_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getIQBalance(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_FREQUENCY_CORRECTION: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_FREQUENCY_CORRECTION_API packer & _dev->hasFrequencyCorrection(direction, channel); #else bool result(false); packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_FREQUENCY_CORRECTION: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; double value(0.0); unpacker & direction; unpacker & channel; unpacker & value; #ifdef SOAPY_SDR_API_HAS_FREQUENCY_CORRECTION_API _dev->setFrequencyCorrection(direction, channel, value); #endif packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY_CORRECTION: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_FREQUENCY_CORRECTION_API packer & _dev->getFrequencyCorrection(direction, channel); #else double result(0.0); packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_GAINS: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listGains(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_GAIN_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->hasGainMode(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_GAIN_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; bool automatic = false; unpacker & direction; unpacker & channel; unpacker & automatic; _dev->setGainMode(direction, channel, automatic); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_GAIN_MODE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getGainMode(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_GAIN: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; double value = 0; unpacker & direction; unpacker & channel; unpacker & value; _dev->setGain(direction, channel, value); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_GAIN_ELEMENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; double value = 0; unpacker & direction; unpacker & channel; unpacker & name; unpacker & value; _dev->setGain(direction, channel, name, value); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_GAIN: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getGain(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_GAIN_ELEMENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->getGain(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_GAIN_RANGE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getGainRange(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_GAIN_RANGE_ELEMENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->getGainRange(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_FREQUENCY: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; double value = 0; SoapySDR::Kwargs args; unpacker & direction; unpacker & channel; unpacker & value; unpacker & args; _dev->setFrequency(direction, channel, value, args); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_FREQUENCY_COMPONENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; double value = 0; SoapySDR::Kwargs args; unpacker & direction; unpacker & channel; unpacker & name; unpacker & value; unpacker & args; _dev->setFrequency(direction, channel, name, value, args); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getFrequency(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY_COMPONENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->getFrequency(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_FREQUENCIES: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listFrequencies(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY_RANGE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getFrequencyRange(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY_RANGE_COMPONENT: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->getFrequencyRange(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_FREQUENCY_ARGS_INFO: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; packer & _dev->getFrequencyArgsInfo(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_SAMPLE_RATE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; double rate = 0; unpacker & direction; unpacker & channel; unpacker & rate; _dev->setSampleRate(direction, channel, rate); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_SAMPLE_RATE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getSampleRate(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_SAMPLE_RATES: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listSampleRates(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_SAMPLE_RATE_RANGE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_GET_SAMPLE_RATE_RANGE packer & _dev->getSampleRateRange(direction, channel); #else SoapySDR::RangeList result; packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_BANDWIDTH: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; double bw = 0; unpacker & direction; unpacker & channel; unpacker & bw; _dev->setBandwidth(direction, channel, bw); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_BANDWIDTH: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->getBandwidth(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_BANDWIDTHS: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listBandwidths(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_BANDWIDTH_RANGE: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_GET_BANDWIDTH_RANGE packer & _dev->getBandwidthRange(direction, channel); #else SoapySDR::RangeList result; packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_MASTER_CLOCK_RATE: //////////////////////////////////////////////////////////////////// { double rate = 0; unpacker & rate; _dev->setMasterClockRate(rate); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_MASTER_CLOCK_RATE: //////////////////////////////////////////////////////////////////// { packer & _dev->getMasterClockRate(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_MASTER_CLOCK_RATES: //////////////////////////////////////////////////////////////////// { packer & _dev->getMasterClockRates(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_CLOCK_SOURCES: //////////////////////////////////////////////////////////////////// { packer & _dev->listClockSources(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_CLOCK_SOURCE: //////////////////////////////////////////////////////////////////// { std::string source; unpacker & source; _dev->setClockSource(source); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_CLOCK_SOURCE: //////////////////////////////////////////////////////////////////// { packer & _dev->getClockSource(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_TIME_SOURCES: //////////////////////////////////////////////////////////////////// { packer & _dev->listTimeSources(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_TIME_SOURCE: //////////////////////////////////////////////////////////////////// { std::string source; unpacker & source; _dev->setTimeSource(source); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_TIME_SOURCE: //////////////////////////////////////////////////////////////////// { packer & _dev->getTimeSource(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_HAS_HARDWARE_TIME: //////////////////////////////////////////////////////////////////// { std::string what; unpacker & what; packer & _dev->hasHardwareTime(what); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_HARDWARE_TIME: //////////////////////////////////////////////////////////////////// { std::string what; unpacker & what; packer & _dev->getHardwareTime(what); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_HARDWARE_TIME: //////////////////////////////////////////////////////////////////// { long long timeNs = 0; std::string what; unpacker & timeNs; unpacker & what; _dev->setHardwareTime(timeNs, what); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_SET_COMMAND_TIME: //////////////////////////////////////////////////////////////////// { long long timeNs = 0; std::string what; unpacker & timeNs; unpacker & what; _dev->setCommandTime(timeNs, what); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_SENSORS: //////////////////////////////////////////////////////////////////// { packer & _dev->listSensors(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_SENSOR_INFO: //////////////////////////////////////////////////////////////////// { std::string name; unpacker & name; packer & _dev->getSensorInfo(name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_SENSOR: //////////////////////////////////////////////////////////////////// { std::string name; unpacker & name; packer & _dev->readSensor(name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_CHANNEL_SENSORS: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; packer & _dev->listSensors(direction, channel); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_CHANNEL_SENSOR_INFO: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->getSensorInfo(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_CHANNEL_SENSOR: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string name; unpacker & direction; unpacker & channel; unpacker & name; packer & _dev->readSensor(direction, channel, name); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_REGISTER: //////////////////////////////////////////////////////////////////// { int addr = 0; int value = 0; unpacker & addr; unpacker & value; _dev->writeRegister(unsigned(addr), unsigned(value)); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_REGISTER: //////////////////////////////////////////////////////////////////// { int addr = 0; unpacker & addr; packer & int(_dev->readRegister(unsigned(addr))); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_REGISTER_INTERFACES: //////////////////////////////////////////////////////////////////// { #ifdef SOAPY_SDR_API_HAS_NAMED_REGISTER_API packer & _dev->listRegisterInterfaces(); #else std::vector result; packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_REGISTER_NAMED: //////////////////////////////////////////////////////////////////// { std::string name; int addr = 0; int value = 0; unpacker & name; unpacker & addr; unpacker & value; #ifdef SOAPY_SDR_API_HAS_NAMED_REGISTER_API _dev->writeRegister(name, unsigned(addr), unsigned(value)); #endif packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_REGISTER_NAMED: //////////////////////////////////////////////////////////////////// { std::string name; int addr = 0; unpacker & name; unpacker & addr; #ifdef SOAPY_SDR_API_HAS_NAMED_REGISTER_API packer & int(_dev->readRegister(name, unsigned(addr))); #else int result = 0; packer & result; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_REGISTERS: //////////////////////////////////////////////////////////////////// { std::string name; int addr = 0; std::vector value; unpacker & name; unpacker & addr; unpacker & value; #ifdef SOAPY_SDR_API_HAS_NAMED_REGISTERS_API std::vector val (value.begin(), value.end()); _dev->writeRegisters(name, unsigned(addr), val); #endif packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_REGISTERS: //////////////////////////////////////////////////////////////////// { std::string name; int addr = 0; int length; unpacker & name; unpacker & addr; unpacker & length; #ifdef SOAPY_SDR_API_HAS_NAMED_REGISTERS_API std::vector val = _dev->readRegisters(name, unsigned(addr), size_t(length)); std::vector value (val.begin(), val.end()); packer & (value); #else std::vector value; packer & (value); #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_SETTING_INFO: //////////////////////////////////////////////////////////////////// { packer & _dev->getSettingInfo(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_SETTING: //////////////////////////////////////////////////////////////////// { std::string key; std::string value; unpacker & key; unpacker & value; _dev->writeSetting(key, value); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_SETTING: //////////////////////////////////////////////////////////////////// { std::string key; unpacker & key; packer & _dev->readSetting(key); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_GET_CHANNEL_SETTING_INFO: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; unpacker & direction; unpacker & channel; #ifdef SOAPY_SDR_API_HAS_CHANNEL_SETTINGS packer & _dev->getSettingInfo(direction, channel); #else SoapySDR::ArgInfoList info; packer & info; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_CHANNEL_SETTING: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string key; std::string value; unpacker & direction; unpacker & channel; unpacker & key; unpacker & value; #ifdef SOAPY_SDR_API_HAS_CHANNEL_SETTINGS _dev->writeSetting(direction, channel, key, value); #endif packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_CHANNEL_SETTING: //////////////////////////////////////////////////////////////////// { char direction = 0; int channel = 0; std::string key; unpacker & direction; unpacker & channel; unpacker & key; #ifdef SOAPY_SDR_API_HAS_CHANNEL_SETTINGS packer & _dev->readSetting(direction, channel, key); #else std::string value; packer & value; #endif } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_GPIO_BANKS: //////////////////////////////////////////////////////////////////// { packer & _dev->listGPIOBanks(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_GPIO: //////////////////////////////////////////////////////////////////// { std::string bank; int value = 0; unpacker & bank; unpacker & value; _dev->writeGPIO(bank, unsigned(value)); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_GPIO_MASKED: //////////////////////////////////////////////////////////////////// { std::string bank; int value = 0; int mask = 0; unpacker & bank; unpacker & value; unpacker & mask; _dev->writeGPIO(bank, unsigned(value), unsigned(mask)); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_GPIO: //////////////////////////////////////////////////////////////////// { std::string bank; unpacker & bank; packer & int(_dev->readGPIO(bank)); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_GPIO_DIR: //////////////////////////////////////////////////////////////////// { std::string bank; int dir = 0; unpacker & bank; unpacker & dir; _dev->writeGPIODir(bank, unsigned(dir)); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_GPIO_DIR_MASKED: //////////////////////////////////////////////////////////////////// { std::string bank; int dir = 0; int mask = 0; unpacker & bank; unpacker & dir; unpacker & mask; _dev->writeGPIODir(bank, unsigned(dir), unsigned(mask)); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_GPIO_DIR: //////////////////////////////////////////////////////////////////// { std::string bank; unpacker & bank; packer & int(_dev->readGPIODir(bank)); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_I2C: //////////////////////////////////////////////////////////////////// { int addr = 0; std::string data; unpacker & addr; unpacker & data; _dev->writeI2C(addr, data); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_I2C: //////////////////////////////////////////////////////////////////// { int addr = 0; int numBytes = 0; unpacker & addr; unpacker & numBytes; packer & _dev->readI2C(addr, unsigned(numBytes)); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_TRANSACT_SPI: //////////////////////////////////////////////////////////////////// { int addr = 0; int data = 0; int numBits = 0; unpacker & addr; unpacker & data; unpacker & numBits; packer & int(_dev->transactSPI(addr, unsigned(data), size_t(numBits))); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_LIST_UARTS: //////////////////////////////////////////////////////////////////// { packer & _dev->listUARTs(); } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_WRITE_UART: //////////////////////////////////////////////////////////////////// { std::string which; std::string data; unpacker & which; unpacker & data; _dev->writeUART(which, data); packer & SOAPY_REMOTE_VOID; } break; //////////////////////////////////////////////////////////////////// case SOAPY_REMOTE_READ_UART: //////////////////////////////////////////////////////////////////// { std::string which; int timeoutUs = 0; unpacker & which; unpacker & timeoutUs; packer & _dev->readUART(which, long(timeoutUs)); } break; default: throw std::runtime_error( "SoapyClientHandler::handleOnce("+std::to_string(int(call))+") unknown call"); } return call != SOAPY_REMOTE_HANGUP; } SoapyRemote-soapy-remote-0.4.2/server/ClientHandler.hpp000066400000000000000000000015461313763561200231560ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include #include #include class SoapyRPCSocket; class SoapyRPCPacker; class SoapyRPCUnpacker; class SoapyLogForwarder; class ServerStreamData; namespace SoapySDR { class Device; } /*! * The client handler manages a remote client. */ class SoapyClientHandler { public: SoapyClientHandler(SoapyRPCSocket &sock, const std::string &uuid); ~SoapyClientHandler(void); //handle once, return true to call again bool handleOnce(void); private: bool handleOnce(SoapyRPCUnpacker &unpacker, SoapyRPCPacker &packer); SoapyRPCSocket &_sock; const std::string _uuid; SoapySDR::Device *_dev; SoapyLogForwarder *_logForwarder; //stream tracking int _nextStreamId; std::map _streamData; }; SoapyRemote-soapy-remote-0.4.2/server/LogForwarding.cpp000066400000000000000000000033121313763561200231720ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "LogForwarding.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyRPCPacker.hpp" #include #include #include /*********************************************************************** * socket subscribers for log forwarding **********************************************************************/ static std::mutex subscribersMutex; static std::set subscribers; /*********************************************************************** * custom log handling **********************************************************************/ static void handleLogMessage(const SoapySDRLogLevel logLevel, const char *message) { std::string strMessage(message); std::lock_guard lock(subscribersMutex); for (auto sock : subscribers) { try { SoapyRPCPacker packer(*sock); packer & char(logLevel); packer & strMessage; packer(); } catch (...) { //ignored } } } /*********************************************************************** * subscriber reregistration entry points **********************************************************************/ SoapyLogForwarder::SoapyLogForwarder(SoapyRPCSocket &sock): _sock(sock) { std::lock_guard lock(subscribersMutex); subscribers.insert(&_sock); //register the log handler, its safe to re-register every time SoapySDR::registerLogHandler(&handleLogMessage); } SoapyLogForwarder::~SoapyLogForwarder(void) { std::lock_guard lock(subscribersMutex); subscribers.erase(&_sock); } SoapyRemote-soapy-remote-0.4.2/server/LogForwarding.hpp000066400000000000000000000005371313763561200232050ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRPCSocket.hpp" /*! * Create a log forwarder to subscribe to log events from the local logger. */ class SoapyLogForwarder { public: SoapyLogForwarder(SoapyRPCSocket &sock); ~SoapyLogForwarder(void); private: SoapyRPCSocket &_sock; }; SoapyRemote-soapy-remote-0.4.2/server/ServerListener.cpp000066400000000000000000000053161313763561200234100ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapyServer.hpp" #include "SoapyRemoteDefs.hpp" #include "ClientHandler.hpp" #include "SoapyRPCSocket.hpp" #include #include /*********************************************************************** * Server thread implementation **********************************************************************/ SoapyServerThreadData::SoapyServerThreadData(void): done(false), thread(nullptr), client(nullptr) { return; } SoapyServerThreadData::~SoapyServerThreadData(void) { done = true; if (thread != nullptr) { thread->join(); } delete thread; if (client != nullptr) { std::cout << "SoapyServerListener::close()" << std::endl; } delete client; } void SoapyServerThreadData::handlerLoop(void) { SoapyClientHandler handler(*client, uuid); try { while (handler.handleOnce()) { if (done) break; } } catch (const std::exception &ex) { std::cerr << "SoapyServerListener::handlerLoop() FAIL: " << ex.what() << std::endl; } done = true; } /*********************************************************************** * Socket listener constructor **********************************************************************/ SoapyServerListener::SoapyServerListener(SoapyRPCSocket &sock, const std::string &uuid): _sock(sock), _uuid(uuid), _handlerId(0) { return; } SoapyServerListener::~SoapyServerListener(void) { auto it = _handlers.begin(); while (it != _handlers.end()) { _handlers.erase(it++); } } /*********************************************************************** * Client socket acceptor **********************************************************************/ void SoapyServerListener::handleOnce(void) { //cleanup completed threads auto it = _handlers.begin(); while (it != _handlers.end()) { auto &data = it->second; if (not data.done) ++it; else _handlers.erase(it++); } //wait with timeout for the server socket to become ready to accept if (not _sock.selectRecv(SOAPY_REMOTE_SOCKET_TIMEOUT_US)) return; SoapyRPCSocket *client = _sock.accept(); if (client == NULL) { std::cerr << "SoapyServerListener::accept() FAIL:" << _sock.lastErrorMsg() << std::endl; return; } std::cout << "SoapyServerListener::accept(" << client->getpeername() << ")" << std::endl; //setup the thread data auto &data = _handlers[_handlerId++]; data.client = client; data.uuid = _uuid; //spawn a new thread data.thread = new std::thread(&SoapyServerThreadData::handlerLoop, &data); } SoapyRemote-soapy-remote-0.4.2/server/ServerStreamData.cpp000066400000000000000000000166001313763561200236460ustar00rootroot00000000000000// Copyright (c) 2015-2017 Josh Blum // Copyright (c) 2016-2016 Bastille Networks // SPDX-License-Identifier: BSL-1.0 #include "ServerStreamData.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyStreamEndpoint.hpp" #include #include #include //min #include #include #include template void incrementBuffs(std::vector &buffs, size_t numElems, size_t elemSize) { for (auto &buff : buffs) { buff = T(size_t(buff) + (numElems*elemSize)); } } ServerStreamData::ServerStreamData(void): device(nullptr), stream(nullptr), chanMask(0), priority(0.0), streamId(-1), streamSock(nullptr), statusSock(nullptr), endpoint(nullptr), streamThread(nullptr), statusThread(nullptr), done(true) { return; } ServerStreamData::~ServerStreamData(void) { delete streamSock; delete statusSock; } void ServerStreamData::startSendThread(void) { assert(streamId != -1); done = false; streamThread = new std::thread(&ServerStreamData::sendEndpointWork, this); } void ServerStreamData::startRecvThread(void) { assert(streamId != -1); done = false; streamThread = new std::thread(&ServerStreamData::recvEndpointWork, this); } void ServerStreamData::startStatThread(void) { assert(streamId != -1); done = false; statusThread = new std::thread(&ServerStreamData::statEndpointWork, this); } void ServerStreamData::stopThreads(void) { done = true; assert(streamThread != nullptr); assert(statusThread != nullptr); streamThread->join(); statusThread->join(); delete streamThread; delete statusThread; } static void setThreadPrioWithLogging(const double priority) { const auto errorMsg = setThreadPrio(priority); if (not errorMsg.empty()) SoapySDR::logf(SOAPY_SDR_WARNING, "Set thread priority %g failed: %s", priority, errorMsg.c_str()); } void ServerStreamData::recvEndpointWork(void) { setThreadPrioWithLogging(priority); assert(endpoint != nullptr); assert(endpoint->getElemSize() != 0); assert(endpoint->getNumChans() != 0); //setup worker data structures int ret = 0; size_t handle = 0; int flags = 0; long long timeNs = 0; const auto elemSize = endpoint->getElemSize(); std::vector buffs(endpoint->getNumChans()); //loop forever until signaled done //1) wait on the endpoint to become ready //2) acquire the recv buffer from the endpoint //3) write to the device stream from the endpoint buffer //4) release the buffer back to the endpoint while (not done) { if (not endpoint->waitRecv(SOAPY_REMOTE_SOCKET_TIMEOUT_US)) continue; ret = endpoint->acquireRecv(handle, buffs.data(), flags, timeNs); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "Server-side receive endpoint: %s; worker quitting...", streamSock->lastErrorMsg()); return; } //loop to write to device size_t elemsLeft = size_t(ret); while (not done) { ret = device->writeStream(stream, buffs.data(), elemsLeft, flags, timeNs, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret == SOAPY_SDR_TIMEOUT) continue; if (ret < 0) { endpoint->writeStatus(ret, chanMask, flags, timeNs); break; //discard after error, this may have been invalid flags or time } elemsLeft -= ret; incrementBuffs(buffs, ret, elemSize); if (elemsLeft == 0) break; flags &= ~(SOAPY_SDR_HAS_TIME); //clear time for subsequent writes } //release the buffer back to the endpoint endpoint->releaseRecv(handle); } } void ServerStreamData::sendEndpointWork(void) { setThreadPrioWithLogging(priority); assert(endpoint != nullptr); assert(endpoint->getElemSize() != 0); assert(endpoint->getNumChans() != 0); //setup worker data structures int ret = 0; size_t handle = 0; int flags = 0; long long timeNs = 0; const auto elemSize = endpoint->getElemSize(); std::vector buffs(endpoint->getNumChans()); const size_t mtuElems = device->getStreamMTU(stream); //loop forever until signaled done //1) waits on the endpoint to become ready //2) acquire the send buffer from the endpoint //3) read from the device stream into the endpoint buffer //4) release the buffer back to the endpoint (sends) while (not done) { if (not endpoint->waitSend(SOAPY_REMOTE_SOCKET_TIMEOUT_US)) continue; ret = endpoint->acquireSend(handle, buffs.data()); if (ret < 0) { SoapySDR::logf(SOAPY_SDR_ERROR, "Server-side send endpoint: %s; worker quitting...", streamSock->lastErrorMsg()); return; } //Read only up to MTU size with a timeout for minimal waiting. //In the next section we will continue the read with non-blocking. size_t elemsLeft = size_t(ret); size_t elemsRead = 0; while (not done) { flags = 0; //flags is an in/out parameter and must be cleared for consistency const size_t numElems = std::min(mtuElems, elemsLeft); ret = device->readStream(stream, buffs.data(), numElems, flags, timeNs, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret == SOAPY_SDR_TIMEOUT) continue; if (ret < 0) { //ret will be propagated to remote endpoint break; } elemsLeft -= ret; elemsRead += ret; incrementBuffs(buffs, ret, elemSize); break; } //fill remaining buffer with no timeout //This is a latency optimization to forward to the host ASAP, //but to use the full bandwidth when more data is available. //Do not allow this optimization when end of burst or single packet mode to preserve boundaries static const int trailingFlags(SOAPY_SDR_END_BURST | SOAPY_SDR_ONE_PACKET | SOAPY_SDR_END_ABRUPT); if (elemsRead != 0 and elemsLeft != 0 and (flags & trailingFlags) == 0) { int flags1 = 0; long long timeNs1 = 0; ret = device->readStream(stream, buffs.data(), elemsLeft, flags1, timeNs1, 0); if (ret == SOAPY_SDR_TIMEOUT) ret = 0; //timeouts OK if (ret > 0) { elemsLeft -= ret; elemsRead += ret; } //include trailing flags that come from the second read flags |= (flags1 & trailingFlags); } //release the buffer with flags and time from the first read //if any read call returned an error, forward the error instead endpoint->releaseSend(handle, (ret < 0)?ret:elemsRead, flags, timeNs); } } void ServerStreamData::statEndpointWork(void) { assert(endpoint != nullptr); int ret = 0; size_t chanMask = 0; int flags = 0; long long timeNs = 0; while (not done) { ret = device->readStreamStatus(stream, chanMask, flags, timeNs, SOAPY_REMOTE_SOCKET_TIMEOUT_US); if (ret == SOAPY_SDR_TIMEOUT) continue; endpoint->writeStatus(ret, chanMask, flags, timeNs); //exit the thread if stream status is not supported //but only after reporting this to the local endpoint if (ret == SOAPY_SDR_NOT_SUPPORTED) return; } } SoapyRemote-soapy-remote-0.4.2/server/ServerStreamData.hpp000066400000000000000000000026121313763561200236510ustar00rootroot00000000000000// Copyright (c) 2015-2016 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include "SoapyRPCSocket.hpp" #include "ThreadPrioHelper.hpp" #include //sig_atomic_t #include #include class SoapyStreamEndpoint; namespace SoapySDR { class Device; class Stream; } /*! * Server-side stream data for client handler. * This class manages a recv/send endpoint, * and a thread to handle that endpoint. */ class ServerStreamData { public: ServerStreamData(void); ~ServerStreamData(void); SoapySDR::Device *device; SoapySDR::Stream *stream; std::string format; size_t chanMask; double priority; //this ID identifies the stream to the remote host int streamId; //datagram socket for stream endpoint SoapyRPCSocket *streamSock; //datagram socket for status endpoint SoapyRPCSocket *statusSock; //remote side of the stream endpoint SoapyStreamEndpoint *endpoint; //hooks to start/stop work void startSendThread(void); void startRecvThread(void); void startStatThread(void); void stopThreads(void); //worker implementations void recvEndpointWork(void); void sendEndpointWork(void); void statEndpointWork(void); private: //worker thread for this stream std::thread *streamThread; std::thread *statusThread; //signal done to the thread sig_atomic_t done; }; SoapyRemote-soapy-remote-0.4.2/server/SoapySDRServer.1000066400000000000000000000033011313763561200226350ustar00rootroot00000000000000.\" SoapySDRServer.1 - manpage for SoapySDRServer .\" .\" .TH SOAPYSDRSERVER 1 2016\-10\-28 "SoapyRemote 0.3.2" .SH NAME \fBSoapySDRServer\fR \- provide access to local SoapySDR devices over network .\" ---------------------------------------------------------------------------- .SH SYNOPSIS \fBSoapySDRServer\fR [\fIOPTIONS\fR] .\" ---------------------------------------------------------------------------- .SH DESCRIPTION SoapySDRServer is a server that exports all locally available SoapySDR devices over the network. On the other side, a SoapyRemote module for SoapySDR connects to this server and provides the server's modules locally. .\" ---------------------------------------------------------------------------- .SH OPTIONS .TP \fB\-\-bind\fR[=\fIIP\fR[:\fIPORT\fR]] Run server. If an \fIIP\fR argument is not given, bind to the default port 55132 on all local network addresses. \fIIP\fR is the IPv4 or IPv6 address to bind to. If the either of the special values "0.0.0.0" or "[::]" for \fIIP\fR is given it will bind to all local addresses. \fIPORT\fR is an optional port number to use instead of the default. .TP \fB\-\-help\fR Display help and exit. .\" ---------------------------------------------------------------------------- .SH HOMEPAGE SoapySDRServer is part of the .UR https://github.com/pothosware/SoapyRemote/wiki SoapyRemote project .UE . .\" ---------------------------------------------------------------------------- .SH AUTHORS The SoapyRemote client and server were written mostly by .MT josh@pothosware.com Josh Blum .ME with additional contributions from Bastille Networks. .\" .\"This man page was originally written for the Debian project by .\".MT aeb@debian.org .\"Andreas Bombe .\".ME . SoapyRemote-soapy-remote-0.4.2/server/SoapyServer.cpp000066400000000000000000000105401313763561200227110ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "SoapyServer.hpp" #include "SoapyRemoteDefs.hpp" #include "SoapyURLUtils.hpp" #include "SoapyInfoUtils.hpp" #include "SoapyRPCSocket.hpp" #include "SoapySSDPEndpoint.hpp" #include #include #include #include #include /*********************************************************************** * Print help message **********************************************************************/ static int printHelp(void) { std::cout << "Usage SoapySDRServer [options]" << std::endl; std::cout << " Options summary:" << std::endl; std::cout << " --help \t\t\t\t Print this help message" << std::endl; std::cout << " --bind \t\t\t\t Bind and serve forever" << std::endl; std::cout << std::endl; return EXIT_SUCCESS; } /*********************************************************************** * Signal handler for Ctrl + C **********************************************************************/ static sig_atomic_t serverDone = false; void sigIntHandler(const int) { std::cout << "Caught Ctrl+C, shutting down the server..." << std::endl; serverDone = true; } /*********************************************************************** * Launch the server **********************************************************************/ static int runServer(void) { SoapySocketSession sess; const bool isIPv6Supported = not SoapyRPCSocket(SoapyURL("tcp", "::", "0").toString()).null(); const auto defaultBindNode = isIPv6Supported?"::":"0.0.0.0"; //extract url from user input or generate automatically const bool optargHasURL = (optarg != NULL and not std::string(optarg).empty()); auto url = (optargHasURL)? SoapyURL(optarg) : SoapyURL("tcp", defaultBindNode, ""); //default url parameters when not specified if (url.getScheme().empty()) url.setScheme("tcp"); if (url.getService().empty()) url.setService(SOAPY_REMOTE_DEFAULT_SERVICE); //this UUID identifies the server process const auto serverUUID = SoapyInfo::generateUUID1(); std::cout << serverUUID << std::endl; std::cout << "Launching the server... " << url.toString() << std::endl; SoapyRPCSocket s; if (s.bind(url.toString()) != 0) { std::cerr << "Server socket bind FAIL: " << s.lastErrorMsg() << std::endl; return EXIT_FAILURE; } std::cout << "Server bound to " << s.getsockname() << std::endl; s.listen(SOAPY_REMOTE_LISTEN_BACKLOG); auto serverListener = new SoapyServerListener(s, serverUUID); std::cout << "Launching discovery server... " << std::endl; auto ssdpEndpoint = SoapySSDPEndpoint::getInstance(); ssdpEndpoint->registerService(serverUUID, url.getService()); ssdpEndpoint->enablePeriodicNotify(true); std::cout << "Press Ctrl+C to stop the server" << std::endl; signal(SIGINT, sigIntHandler); while (not serverDone) serverListener->handleOnce(); ssdpEndpoint->enablePeriodicNotify(false); ssdpEndpoint.reset(); std::cout << "Shutdown client handler threads" << std::endl; delete serverListener; s.close(); std::cout << "Cleanup complete, exiting" << std::endl; return EXIT_SUCCESS; } /*********************************************************************** * Parse and dispatch options **********************************************************************/ int main(int argc, char *argv[]) { std::cout << "######################################################" << std::endl; std::cout << "## Soapy Server -- Use any Soapy SDR remotely" << std::endl; std::cout << "######################################################" << std::endl; std::cout << std::endl; /******************************************************************* * parse command line options ******************************************************************/ static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"bind", optional_argument, 0, 'b'}, {0, 0, 0, 0} }; int long_index = 0; int option = 0; while ((option = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { switch (option) { case 'h': return printHelp(); case 'b': return runServer(); } } //unknown or unspecified options, do help... return printHelp(); } SoapyRemote-soapy-remote-0.4.2/server/SoapyServer.hpp000066400000000000000000000015051313763561200227170ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include //sig_atomic_t #include #include #include class SoapyRPCSocket; //! Client handler data struct SoapyServerThreadData { SoapyServerThreadData(void); ~SoapyServerThreadData(void); void handlerLoop(void); sig_atomic_t done; std::thread *thread; SoapyRPCSocket *client; std::string uuid; }; /*! * The server listener class accepts clients and spawns threads. */ class SoapyServerListener { public: SoapyServerListener(SoapyRPCSocket &sock, const std::string &uuid); ~SoapyServerListener(void); void handleOnce(void); private: SoapyRPCSocket &_sock; const std::string _uuid; size_t _handlerId; std::map _handlers; }; SoapyRemote-soapy-remote-0.4.2/server/ThreadPrioHelper.hpp000066400000000000000000000004521313763561200236360ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #pragma once #include /*! Set the thread priority: * -1.0 (low), 0.0 (normal), and 1.0 (high) * Return a empty string on success, * otherwise an error message. */ std::string setThreadPrio(const double prio); SoapyRemote-soapy-remote-0.4.2/server/ThreadPrioUnix.cpp000066400000000000000000000020671313763561200233410ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "ThreadPrioHelper.hpp" #include //memset, strerror #include //errno #include #ifdef __APPLE__ #include #endif std::string setThreadPrio(const double prio) { //no negative priorities supported on this OS if (prio <= 0.0) return ""; //determine priority bounds const int policy(SCHED_RR); const int maxPrio = sched_get_priority_max(policy); if (maxPrio < 0) return strerror(errno); const int minPrio = sched_get_priority_min(policy); if (minPrio < 0) return strerror(errno); //set realtime priority and prio number struct sched_param param; std::memset(¶m, 0, sizeof(param)); param.sched_priority = minPrio + int(prio * (maxPrio-minPrio)); #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread if (pthread_setschedparam(tID, policy, ¶m) != 0) return strerror(errno); #else if (sched_setscheduler(0, policy, ¶m) != 0) return strerror(errno); #endif return ""; } SoapyRemote-soapy-remote-0.4.2/server/ThreadPrioWindows.cpp000066400000000000000000000016471313763561200240530ustar00rootroot00000000000000// Copyright (c) 2015-2015 Josh Blum // SPDX-License-Identifier: BSL-1.0 #include "ThreadPrioHelper.hpp" #include std::string setThreadPrio(const double prio) { int nPriority(THREAD_PRIORITY_NORMAL); if (prio > 0) { if (prio > +0.75) nPriority = THREAD_PRIORITY_TIME_CRITICAL; else if (prio > +0.50) nPriority = THREAD_PRIORITY_HIGHEST; else if (prio > +0.25) nPriority = THREAD_PRIORITY_ABOVE_NORMAL; else nPriority = THREAD_PRIORITY_NORMAL; } else { if (prio < -0.75) nPriority = THREAD_PRIORITY_IDLE; else if (prio < -0.50) nPriority = THREAD_PRIORITY_LOWEST; else if (prio < -0.25) nPriority = THREAD_PRIORITY_BELOW_NORMAL; else nPriority = THREAD_PRIORITY_NORMAL; } if (SetThreadPriority(GetCurrentThread(), nPriority)) return ""; return "SetThreadPriority() fail"; } SoapyRemote-soapy-remote-0.4.2/server/msvc/000077500000000000000000000000001313763561200206735ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/server/msvc/getopt.h000066400000000000000000000373021313763561200223530ustar00rootroot00000000000000#ifndef __GETOPT_H__ /** * DISCLAIMER * This file has no copyright assigned and is placed in the Public Domain. * This file is part of the mingw-w64 runtime package. * * The mingw-w64 runtime package and its code is distributed in the hope that it * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #pragma warning(disable:4996) #define __GETOPT_H__ /* All the headers include this file. */ #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #ifdef REPLACE_GETOPT int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #undef optreset /* see getopt.h */ #define optreset __mingw_optreset int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #endif //extern int optind; /* index of first non-option in argv */ //extern int optopt; /* single option character, as parsed */ //extern int opterr; /* flag to enable built-in diagnostics... */ // /* (user may set to zero, to suppress) */ // //extern char *optarg; /* pointer to argument of current option */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #ifndef __CYGWIN__ #define __progname __argv[0] #else extern char __declspec(dllimport) *__progname; #endif #ifdef __CYGWIN__ static char EMSG[] = ""; #else #define EMSG "" #endif static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; static void _vwarnx(const char *fmt,va_list ap) { (void)fprintf(stderr,"%s: ",__progname); if (fmt != NULL) (void)vfprintf(stderr,fmt,ap); (void)fprintf(stderr,"\n"); } static void warnx(const char *fmt,...) { va_list ap; va_start(ap,fmt); _vwarnx(fmt,ap); va_end(ap); } /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } #ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the BSD getopt] */ int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } #endif /* REPLACE_GETOPT */ //extern int getopt(int nargc, char * const *nargv, const char *options); #ifdef _BSD_SOURCE /* * BSD adds the non-standard `optreset' feature, for reinitialisation * of `getopt' parsing. We support this feature, for applications which * proclaim their BSD heritage, before including this header; however, * to maintain portability, developers are advised to avoid it. */ # define optreset __mingw_optreset extern int optreset; #endif #ifdef __cplusplus } #endif /* * POSIX requires the `getopt' API to be specified in `unistd.h'; * thus, `unistd.h' includes this header. However, we do not want * to expose the `getopt_long' or `getopt_long_only' APIs, when * included in this manner. Thus, close the standard __GETOPT_H__ * declarations block, and open an additional __GETOPT_LONG_H__ * specific block, only when *not* __UNISTD_H_SOURCED__, in which * to declare the extended API. */ #endif /* !defined(__GETOPT_H__) */ #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) #define __GETOPT_LONG_H__ #ifdef __cplusplus extern "C" { #endif struct option /* specification for a long form option... */ { const char *name; /* option name, without leading hyphens */ int has_arg; /* does it take an argument? */ int *flag; /* where to save its status, or NULL */ int val; /* its associated status value */ }; enum /* permitted values for its `has_arg' field... */ { no_argument = 0, /* option never takes an argument */ required_argument, /* option always requires an argument */ optional_argument /* option may take an argument */ }; /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, ambiguous, match; #define IDENTICAL_INTERPRETATION(_x, _y) \ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ long_options[(_x)].flag == long_options[(_y)].flag && \ long_options[(_x)].val == long_options[(_y)].val) current_argv = place; match = -1; ambiguous = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; ambiguous = 0; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else if (!IDENTICAL_INTERPRETATION(i, match)) ambiguous = 1; } if (ambiguous) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); #undef IDENTICAL_INTERPRETATION } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. * * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or * optreset != 0 for GNU compatibility. */ if (posixly_correct == -1 || optreset != 0) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = (char*)strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } //extern int getopt_long(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); //extern int getopt_long_only(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); /* * Previous MinGW implementation had... */ #ifndef HAVE_DECL_GETOPT /* * ...for the long form API only; keep this for compatibility. */ # define HAVE_DECL_GETOPT 1 #endif #ifdef __cplusplus } #endif #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ SoapyRemote-soapy-remote-0.4.2/system/000077500000000000000000000000001313763561200177415ustar00rootroot00000000000000SoapyRemote-soapy-remote-0.4.2/system/CMakeLists.txt000066400000000000000000000013031313763561200224760ustar00rootroot00000000000000######################################################################## # SoapySDRServer systemd service ######################################################################## configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/SoapySDRServer.service.in ${CMAKE_CURRENT_BINARY_DIR}/SoapySDRServer.service @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/SoapySDRServer.service DESTINATION lib/systemd/system) ######################################################################## # Increase the sysctl network limits ######################################################################## install( FILES SoapySDRServer.sysctl RENAME SoapySDRServer.conf DESTINATION lib/sysctl.d) SoapyRemote-soapy-remote-0.4.2/system/SoapySDRServer.service.in000066400000000000000000000003671313763561200245710ustar00rootroot00000000000000[Unit] Description=SoapyRemote network server Wants=network-online.target After=network-online.target [Service] ExecStart=@CMAKE_INSTALL_PREFIX@/bin/SoapySDRServer --bind KillMode=process Restart=on-abnormal [Install] WantedBy=multi-user.target SoapyRemote-soapy-remote-0.4.2/system/SoapySDRServer.sysctl000066400000000000000000000002511313763561200240350ustar00rootroot00000000000000#SoapyRemote: Increase the default socket buffer sizes #to fit the default size requested for client and server. net.core.rmem_max=104857600 net.core.wmem_max=104857600