diff options
author | Andrej Rode <andrej.rode@ettus.com> | 2017-03-27 18:05:23 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:45 -0800 |
commit | c8ae32253b649faea27dcf0d98fd0395924a2eec (patch) | |
tree | 8ce89838769d7043f06cb8dc7285b269f9754498 /host | |
parent | 53795c74aead4e6021bc788b788f8ed5b4a0166d (diff) | |
download | uhd-c8ae32253b649faea27dcf0d98fd0395924a2eec.tar.gz uhd-c8ae32253b649faea27dcf0d98fd0395924a2eec.tar.bz2 uhd-c8ae32253b649faea27dcf0d98fd0395924a2eec.zip |
usrp: add netd find and impl, add uhd rpc client wrapper
Diffstat (limited to 'host')
-rw-r--r-- | host/lib/CMakeLists.txt | 8 | ||||
-rw-r--r-- | host/lib/deps/CMakeLists.txt | 5 | ||||
-rwxr-xr-x | host/lib/deps/import_rpclib.py | 5 | ||||
-rw-r--r-- | host/lib/deps/rpclib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/usrp/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/usrp/netd/CMakeLists.txt | 29 | ||||
-rw-r--r-- | host/lib/usrp/netd/netd_impl.cpp | 330 | ||||
-rw-r--r-- | host/lib/usrp/netd/netd_impl.hpp | 82 | ||||
-rw-r--r-- | host/lib/utils/rpc.hpp | 44 |
9 files changed, 498 insertions, 7 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 2679ce302..a1c5648ad 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -61,6 +61,7 @@ MESSAGE(STATUS "") FIND_PACKAGE(USB1) FIND_PACKAGE(GPSD) FIND_PACKAGE(LIBERIO) +LIBUHD_REGISTER_COMPONENT("LIBERIO" ENABLE_LIBERIO ON "ENABLE_LIBUHD;LIBERIO_FOUND" OFF OFF) LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF OFF) LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) # Devices @@ -71,7 +72,7 @@ LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("N230" ENABLE_N230 ON "ENABLE_LIBUHD" OFF OFF) -LIBUHD_REGISTER_COMPONENT("LIBERIO" ENABLE_LIBERIO ON "ENABLE_LIBUHD;LIBERIO_FOUND" OFF OFF) +LIBUHD_REGISTER_COMPONENT("NETD" ENABLE_NETD ON "ENABLE_LIBUHD" OFF OFF) LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) ######################################################################## @@ -164,6 +165,11 @@ ENDIF(MSVC) ######################################################################## # Setup libuhd library ######################################################################## +IF(ENABLE_NETD) + INCLUDE_DIRECTORIES(deps/rpclib/include) + ADD_SUBDIRECTORY(deps) + LIST(APPEND libuhd_sources $<TARGET_OBJECTS:uhd_rpclib>) +ENDIF() ADD_LIBRARY(uhd SHARED ${libuhd_sources}) TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${libuhd_libs}) SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS") diff --git a/host/lib/deps/CMakeLists.txt b/host/lib/deps/CMakeLists.txt index 106f2cd19..bcb89d29a 100644 --- a/host/lib/deps/CMakeLists.txt +++ b/host/lib/deps/CMakeLists.txt @@ -14,5 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # - -ADD_SUBDIRECTORY(rpclib) +IF(ENABLE_NETD) + ADD_SUBDIRECTORY(rpclib) +ENDIF() diff --git a/host/lib/deps/import_rpclib.py b/host/lib/deps/import_rpclib.py index 2efd29e60..2baccab1a 100755 --- a/host/lib/deps/import_rpclib.py +++ b/host/lib/deps/import_rpclib.py @@ -46,7 +46,7 @@ REPLACE_EXPR = [ def fix_naming(workdir): """ - replace all the things + Applies all the REPLACE_EXPR in all the files """ for root, dirs, files in os.walk(workdir): for cur_file in files: @@ -78,7 +78,7 @@ def extract_rpclib_version(rpclib_root): def copy_important_files(target_root, rpclib_root): """ - copy files/subdirs we consider important + copy files/subdirs we consider important (see IMPORTANT_FILES) """ for rpc_file in os.listdir(rpclib_root): if rpc_file in IMPORTANT_FILES: @@ -110,7 +110,6 @@ def extract_rpclib(args): """ THE extraction function """ - workdir = tempfile.mkdtemp() rpclib_root = args.rpclib_root new_rpclib_root = tempfile.mkdtemp() diff --git a/host/lib/deps/rpclib/CMakeLists.txt b/host/lib/deps/rpclib/CMakeLists.txt index a1298a464..271a49854 100644 --- a/host/lib/deps/rpclib/CMakeLists.txt +++ b/host/lib/deps/rpclib/CMakeLists.txt @@ -1,4 +1,3 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0) PROJECT(UHD_RPCLIB CXX) SET(RPCLIB_DEFAULT_BUFFER_SIZE "1024 << 10" CACHE STRING "Default buffer size") diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index c3a1e6533..0f8172d41 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -41,6 +41,7 @@ INCLUDE_SUBDIRECTORY(cores) INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(common) INCLUDE_SUBDIRECTORY(device3) +INCLUDE_SUBDIRECTORY(netd) INCLUDE_SUBDIRECTORY(usrp1) INCLUDE_SUBDIRECTORY(usrp2) INCLUDE_SUBDIRECTORY(b100) diff --git a/host/lib/usrp/netd/CMakeLists.txt b/host/lib/usrp/netd/CMakeLists.txt new file mode 100644 index 000000000..a33c978c8 --- /dev/null +++ b/host/lib/usrp/netd/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2017 Ettus Research (National Instruments) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the X300 support +######################################################################## +IF(ENABLE_NETD) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/netd_impl.cpp + ) +ENDIF(ENABLE_NETD) diff --git a/host/lib/usrp/netd/netd_impl.cpp b/host/lib/usrp/netd/netd_impl.cpp new file mode 100644 index 000000000..0b4da86ef --- /dev/null +++ b/host/lib/usrp/netd/netd_impl.cpp @@ -0,0 +1,330 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "netd_impl.hpp" +#include <../device3/device3_impl.hpp> +#include <uhd/exception.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/tasks.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/asio.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread.hpp> +#include <memory> +#include <mutex> +#include <random> +#include <string> + +using namespace uhd; + +netd_mboard_impl::netd_mboard_impl(const std::string& addr) + : rpc(addr, MPM_RPC_PORT) +{ + std::map<std::string, std::string> _dev_info = + rpc.call<dev_info>("get_device_info"); + device_info = + dict<std::string, std::string>(_dev_info.begin(), _dev_info.end()); + // Get initial claim on mboard + _rpc_token = rpc.call<std::string>("claim", "UHD - Session 01"); // make this configurable with device_addr? + if (_rpc_token.empty()){ + throw uhd::value_error("netd device claiming failed!"); + } + _claimer_task = task::make([this] { + if (not this->claim()) { + throw uhd::value_error("netd device reclaiming loop failed!"); + }; + boost::this_thread::sleep_for(boost::chrono::milliseconds(1000)); + }); + std::vector<std::string> data_ifaces = + rpc.call<std::vector<std::string>>("get_interfaces", _rpc_token); + + // discover path to device and tell MPM our MAC address seen at the data + // interfaces + // move this into make_transport + //for (const auto& iface : data_ifaces) { + //std::vector<std::string> addrs = rpc.call<std::vector<std::string>>( + //"get_interface_addrs", _rpc_token, iface); + //for (const auto& iface_addr : addrs) { + //if (rpc_client(iface_addr, MPM_RPC_PORT) + //.call<bool>("probe_interface", _rpc_token)) { + //data_interfaces.emplace(iface, iface_addr); + //break; + //} + //} + //} +} +netd_mboard_impl::~netd_mboard_impl() {} + +netd_mboard_impl::uptr netd_mboard_impl::make(const std::string& addr) +{ + netd_mboard_impl::uptr mb = + netd_mboard_impl::uptr(new netd_mboard_impl(addr)); + // implicit move + return mb; +} + +bool netd_mboard_impl::claim() { return rpc.call<bool>("claim", _rpc_token); } + +netd_impl::netd_impl(const device_addr_t& device_addr) : usrp::device3_impl() +{ + UHD_LOGGER_INFO("NETD") << "NETD initialization sequence..."; + _tree->create<std::string>("/name").set("NETD - Series device"); + const device_addrs_t device_args = separate_device_addr(device_addr); + _mb.reserve(device_args.size()); + for (size_t mb_i = 0; mb_i < device_args.size(); ++mb_i) { + _mb.push_back(setup_mb(mb_i, device_args[mb_i])); + } +} + +netd_impl::~netd_impl() {} + +netd_mboard_impl::uptr netd_impl::setup_mb(const size_t mb_i, + const uhd::device_addr_t& dev_addr) +{ + const fs_path mb_path = "/mboards/" + std::to_string(mb_i); + netd_mboard_impl::uptr mb = netd_mboard_impl::make(dev_addr["addr"]); + mb->initialization_done = false; + std::vector<std::string> addrs; + const std::string eth0_addr = dev_addr["addr"]; + _tree->create<std::string>(mb_path / "name") + .set(mb->device_info.get("type", "")); + _tree->create<std::string>(mb_path / "serial") + .set(mb->device_info.get("serial", "")); + _tree->create<std::string>(mb_path / "connection") + .set(mb->device_info.get("connection", "remote")); + + for (const std::string& key : dev_addr.keys()) { + if (key.find("recv") != std::string::npos) + mb->recv_args[key] = dev_addr[key]; + if (key.find("send") != std::string::npos) + mb->send_args[key] = dev_addr[key]; + } + + // Do real MTU discovery (something similar like X300 but with MPM) + + _tree->create<size_t>(mb_path / "mtu/recv").set(1500); + _tree->create<size_t>(mb_path / "mtu/send").set(1500); + _tree->create<size_t>(mb_path / "link_max_rate").set(1e9 / 8); + + // query more information about FPGA/MPM + + // Call init on periph_manager, this will init the dboards/mboard, maybe + // even selfcal and everything + + // Query time/clock sources on mboards/dboards + // Throw rpc calls with boost bind into the property tree? + + // Query rfnoc blocks on the device (MPM may know about them?) + + // call enumerate rfnoc_blocks on the device + + // configure radio? + + // implicit move + return mb; +} + +// frame_size_t determine_max_frame_size(const std::string &addr, +// const frame_size_t &user_frame_size){ +// transport::udp_simple::sptr udp = +// transport::udp_simple::make_connected(addr, +// std::to_string(MPM_DISCOVERY_PORT)); +// std::vector<uint8_t> buffer(std::max(user_frame_size.rec)) +// } +// Everything fake below here + +both_xports_t netd_impl::make_transport(const sid_t&, + usrp::device3_impl::xport_type_t, + const uhd::device_addr_t&) +{ + //const size_t mb_index = address.get_dst_addr(); + size_t mb_index = 0; + + both_xports_t xports; + xports.endianness = uhd::ENDIANNESS_BIG; + const uhd::device_addr_t& xport_args = (xport_type == CTRL) ? uhd::device_addr_t() : args; + transport::zero_copy_xport_params default_buff_args; + + /* + std::cout << address << std::endl; + std::cout << address.get_src_addr() << std::endl; + std::cout << address.get_dst_addr() << std::endl; + */ + + std::string interface_addr = "192.168.10.2"; + const uint32_t xbar_src_addr = address.get_src_addr(); + const uint32_t xbar_src_dst = 0; + + default_buff_args.send_frame_size = 8000; + default_buff_args.recv_frame_size = 8000; + default_buff_args.num_recv_frames = 32; + default_buff_args.num_send_frames = 32; + // hardcode frame size for now + + transport::udp_zero_copy::buff_params buff_params; + auto recv = transport::udp_zero_copy::make( + interface_addr, + BOOST_STRINGIZE(49153), + default_buff_args, + buff_params, + xport_args); + uint16_t port = recv->get_local_port(); + + xports.send_sid = _mb[mb_index]->allocate_sid(port, address, xbar_src_addr, xbar_src_dst); + xports.recv_sid = xports.send_sid.reversed(); + + //std::cout << xports.send_sid << std::endl; + //std::cout << xports.recv_sid << std::endl; + + xports.recv_buff_size = buff_params.recv_buff_size; + xports.send_buff_size = buff_params.send_buff_size; + + xports.recv = recv; + xports.send = xports.recv; + + return xports; +} + +device_addrs_t netd_find_with_addr(const device_addr_t& hint_) +{ + transport::udp_simple::sptr comm = transport::udp_simple::make_broadcast( + hint_["addr"], std::to_string(MPM_DISCOVERY_PORT)); + comm->send( + boost::asio::buffer(&MPM_DISCOVERY_CMD, sizeof(MPM_DISCOVERY_CMD))); + device_addrs_t addrs; + while (true) { + char buff[4096] = {}; + const size_t nbytes = comm->recv(boost::asio::buffer(buff), 0.050); + if (nbytes == 0) { + break; + } + const char* reply = (const char*)buff; + std::string reply_string = std::string(reply); + std::vector<std::string> result; + boost::algorithm::split(result, reply_string, + [](const char& in) { return in == ';'; }, + boost::token_compress_on); + if (result.empty()) { + continue; + } + // who else is reposending to our request !? + if (result[0] != "USRP-MPM") { + continue; + } + const std::string recv_addr = comm->get_recv_addr(); + + // remove external iface addrs if executed directly on device + bool external_iface = false; + for (const auto& addr : transport::get_if_addrs()) { + if ((addr.inet == comm->get_recv_addr()) && + recv_addr != + boost::asio::ip::address_v4::loopback().to_string()) { + external_iface = true; + } + } + if (external_iface) { + continue; + } + device_addr_t new_addr; + new_addr["addr"] = recv_addr; + new_addr["type"] = "netd"; // hwd will overwrite this + // remove ident string and put other informations into device_addr dict + result.erase(result.begin()); + // parse key-value pairs in the discovery string and add them to the + // device_addr + for (const auto& el : result) { + std::vector<std::string> value; + boost::algorithm::split(value, el, + [](const char& in) { return in == '='; }, + boost::token_compress_on); + new_addr[value[0]] = value[1]; + } + addrs.push_back(new_addr); + } + return addrs; +}; + +device_addrs_t netd_find(const device_addr_t& hint_) +{ + // handle cases: + // + // - empty hint + // - multiple addrs + // - single addr + + device_addrs_t hints = separate_device_addr(hint_); + // either hints has: + // multiple entries + // -> search for multiple devices and join them back into one + // device_addr_t + // one entry with addr: + // -> search for one device with this addr + // one + // multiple addrs + if (hints.size() > 1) { + device_addrs_t found_devices; + found_devices.reserve(hints.size()); + for (const auto& hint : hints) { + if (not hint.has_key("addr")) { // maybe allow other attributes as well + return device_addrs_t(); + } + device_addrs_t reply_addrs = netd_find_with_addr(hint); + if (reply_addrs.size() > 1) { + throw uhd::value_error( + str(boost::format("Could not resolve device hint \"%s\" to " + "a single device.") % + hint.to_string())); + } else if (reply_addrs.empty()) { + return device_addrs_t(); + } + found_devices.push_back(reply_addrs[0]); + } + return device_addrs_t(1, combine_device_addrs(found_devices)); + } + hints.resize(1); + device_addr_t hint = hints[0]; + device_addrs_t addrs; + + if (hint.has_key("addr")) { + // is this safe? + return netd_find_with_addr(hint); + } + + for (const transport::if_addrs_t& if_addr : transport::get_if_addrs()) { + device_addr_t new_hint = hint; + new_hint["addr"] = if_addr.bcast; + + device_addrs_t reply_addrs = netd_find_with_addr(new_hint); + addrs.insert(addrs.begin(), reply_addrs.begin(), reply_addrs.end()); + } + return addrs; +} + +static device::sptr netd_make(const device_addr_t& device_addr) +{ + return device::sptr(boost::make_shared<netd_impl>(device_addr)); +} + +UHD_STATIC_BLOCK(register_netd_device) +{ + device::register_device(&netd_find, &netd_make, device::USRP); +} +// vim: sw=4 expandtab: diff --git a/host/lib/usrp/netd/netd_impl.hpp b/host/lib/usrp/netd/netd_impl.hpp new file mode 100644 index 000000000..418f3aef6 --- /dev/null +++ b/host/lib/usrp/netd/netd_impl.hpp @@ -0,0 +1,82 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_NETD_IMPL_HPP +#define INCLUDED_NETD_IMPL_HPP +#include "../../utils/rpc.hpp" +#include "../device3/device3_impl.hpp" +#include <uhd/stream.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/tasks.hpp> +#include <map> + +static const size_t MPM_DISCOVERY_PORT = 49600; +static const size_t MPM_RPC_PORT = 49601; +static const char MPM_DISCOVERY_CMD[] = "MPM-DISC"; +static const char MPM_ECHO_CMD[] = "MPM-ECHO"; +static const size_t NETD_10GE_DATA_FRAME_MAX_SIZE = 8000; // CHDR packet size in bytes + +struct frame_size_t +{ + size_t recv_frame_size; + size_t send_frame_size; +}; + +class netd_mboard_impl +{ + public: + using uptr = std::unique_ptr<netd_mboard_impl>; + using dev_info = std::map<std::string, std::string>; + netd_mboard_impl(const std::string& addr); + ~netd_mboard_impl(); + static uptr make(const std::string& addr); + + bool initialization_done = false; + uhd::dict<std::string, std::string> device_info; + uhd::dict<std::string, std::string> recv_args; + uhd::dict<std::string, std::string> send_args; + std::map<std::string, std::string> data_interfaces; + std::string loaded_fpga_image; + std::string xport_path; + uhd::rpc_client rpc; + + private: + bool claim(); + std::string generate_token() const; + std::string _rpc_token; + uhd::task::sptr _claimer_task; +}; + +class netd_impl : public uhd::usrp::device3_impl +{ + public: + netd_impl(const uhd::device_addr_t& device_addr); + ~netd_impl(); + + netd_mboard_impl::uptr setup_mb(const size_t mb_i, + const uhd::device_addr_t& dev_addr); + uhd::both_xports_t make_transport(const uhd::sid_t&, + uhd::usrp::device3_impl::xport_type_t, + const uhd::device_addr_t&); + + private: + std::vector<netd_mboard_impl::uptr> _mb; +}; +uhd::device_addrs_t netd_find(const uhd::device_addr_t& hint_); +#endif /* INCLUDED_NETD_IMPL_HPP */ +// vim: sw=4 expandtab: diff --git a/host/lib/utils/rpc.hpp b/host/lib/utils/rpc.hpp new file mode 100644 index 000000000..946ce90ed --- /dev/null +++ b/host/lib/utils/rpc.hpp @@ -0,0 +1,44 @@ +// +// Copyright 2017 Ettus Research (National Instruments) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UTILS_RPC_HPP +#define INCLUDED_UTILS_RPC_HPP + +#include <rpc/client.h> + +namespace uhd +{ +class rpc_client +{ + public: + rpc_client(std::string const& addr, uint16_t port) : _client(addr, port) {} + template <typename return_type, typename... Args> + return_type call(std::string const& func_name, Args&&... args) + { + std::lock_guard<std::mutex> lock(_mutex); + return _client.call(func_name, std::forward<Args>(args)...) + .template as<return_type>(); + }; + + private: + std::mutex _mutex; + ::rpc::client _client; +}; + +} + +#endif /* INCLUDED_UTILS_RPC_HPP */ |