aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport/nirio/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/transport/nirio/rpc')
-rw-r--r--host/lib/transport/nirio/rpc/CMakeLists.txt29
-rw-r--r--host/lib/transport/nirio/rpc/rpc_client.cpp201
-rw-r--r--host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp229
3 files changed, 459 insertions, 0 deletions
diff --git a/host/lib/transport/nirio/rpc/CMakeLists.txt b/host/lib/transport/nirio/rpc/CMakeLists.txt
new file mode 100644
index 000000000..02c16d2ff
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/CMakeLists.txt
@@ -0,0 +1,29 @@
+#
+# Copyright 2013 Ettus Research LLC
+#
+# 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
+########################################################################
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/rpc_client.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrprio_rpc_client.cpp
+)
diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp
new file mode 100644
index 000000000..a5f8cf412
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/rpc_client.cpp
@@ -0,0 +1,201 @@
+///
+// Copyright 2013 Ettus Research LLC
+//
+// 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 <uhd/transport/nirio/rpc/rpc_client.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#define CHAIN_BLOCKING_XFER(func, exp, status) \
+ if (status) { \
+ status = (static_cast<size_t>((func)) == exp); \
+ } else { \
+ UHD_LOG << "rpc_client operation skipped: " #func "\n"; \
+ } \
+
+namespace uhd { namespace usrprio_rpc {
+
+using boost::asio::ip::tcp;
+
+rpc_client::rpc_client (
+ const std::string& server,
+ const std::string& port,
+ boost::uint32_t process_id,
+ boost::uint32_t host_id
+) : _socket(_io_service)
+{
+ //Fill in handshake info
+ _hshake_args_client.version = CURRENT_VERSION;
+ _hshake_args_client.oldest_comp_version = OLDEST_COMPATIBLE_VERSION;
+ _hshake_args_client.client_id = build_client_id(host_id, process_id);
+ _hshake_args_client.boost_archive_version = boost_serialization_archive_utils::get_version();
+
+ try {
+ //Synchronous resolve + connect
+ tcp::resolver resolver(_io_service);
+ tcp::resolver::query query(tcp::v4(), server, port);
+ tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::connect(_socket, iterator);
+ UHD_LOG << "rpc_client connected to server." << std::endl;
+
+ try {
+ //Perform handshake
+ bool status = true;
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&_hshake_args_client, sizeof(_hshake_args_client))),
+ sizeof(_hshake_args_client), status);
+ CHAIN_BLOCKING_XFER(
+ boost::asio::read(_socket, boost::asio::buffer(&_hshake_args_server, sizeof(_hshake_args_server))),
+ sizeof(_hshake_args_server), status);
+
+ _request.header.client_id = _hshake_args_server.client_id;
+
+ if (_hshake_args_server.version >= _hshake_args_client.oldest_comp_version &&
+ _hshake_args_client.version >= _hshake_args_server.oldest_comp_version &&
+ status)
+ {
+ UHD_LOG << "rpc_client bound to server." << std::endl;
+ _wait_for_next_response_header();
+
+ //Spawn a thread for the io_service callback handler. This thread will run until rpc_client is destroyed.
+ _io_service_thread.reset(new boost::thread(boost::bind(&boost::asio::io_service::run, &_io_service)));
+ } else {
+ UHD_LOG << "rpc_client handshake failed." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category());
+ }
+ UHD_LOG << boost::format("rpc_client archive = %d, rpc_server archive = %d\n.") %
+ _hshake_args_client.boost_archive_version %
+ _hshake_args_server.boost_archive_version;
+ } catch (boost::exception&) {
+ UHD_LOG << "rpc_client handshake aborted." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_refused, boost::system::system_category());
+ }
+ } catch (boost::exception&) {
+ UHD_LOG << "rpc_client connection request cancelled/aborted." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category());
+ }
+}
+
+rpc_client::~rpc_client () {
+ _stop_io_service();
+}
+
+const boost::system::error_code& rpc_client::call(
+ func_id_t func_id,
+ const func_args_writer_t& in_args,
+ func_args_reader_t &out_args,
+ boost::posix_time::milliseconds timeout
+)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ if (_io_service_thread.get()) {
+ _request.header.func_id = func_id;
+ in_args.store(_request.data);
+ _request.header.func_args_size = _request.data.size();
+
+ _exec_err.clear();
+
+ //Send function call header and args
+ bool status = true;
+ try {
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&_request.header, sizeof(_request.header))),
+ sizeof(_request.header), status);
+ CHAIN_BLOCKING_XFER(
+ boost::asio::write(_socket, boost::asio::buffer(&(*_request.data.begin()), _request.data.size())),
+ _request.data.size(), status);
+ } catch (boost::exception&) {
+ status = false;
+ }
+
+ //Wait for response using condition variable
+ if (status) {
+ if (!_exec_gate.timed_wait(lock, timeout)) {
+ UHD_LOG << "rpc_client function timed out." << std::endl;
+ _exec_err.assign(boost::asio::error::timed_out, boost::system::system_category());
+ }
+ } else {
+ UHD_LOG << "rpc_client connection dropped." << std::endl;
+ _exec_err.assign(boost::asio::error::connection_aborted, boost::system::system_category());
+ _stop_io_service();
+ }
+
+ //Verify that we are talking to the correct endpoint
+ if ((_request.header.client_id != _response.header.client_id) && !_exec_err) {
+ UHD_LOG << "rpc_client confused about who its talking to." << std::endl;
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+ }
+
+ if (!_exec_err) out_args.load(_response.data);
+ }
+
+ return _exec_err;
+}
+
+void rpc_client::_handle_response_hdr(const boost::system::error_code& err, size_t transferred, size_t expected)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _exec_err = err;
+ if (!_exec_err && (transferred == expected)) {
+ //Response header received. Verify that it is expected
+ if (func_args_header_t::match_function(_request.header, _response.header)) {
+ _response.data.resize(_response.header.func_args_size);
+
+ //Wait for response data
+ boost::asio::async_read(_socket,
+ boost::asio::buffer(&(*_response.data.begin()), _response.data.size()),
+ boost::bind(&rpc_client::_handle_response_data, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred,
+ _response.data.size()));
+ } else {
+ //Unexpected response. Ignore it.
+ UHD_LOG << "rpc_client received garbage responses." << std::endl;
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+
+ _wait_for_next_response_header();
+ }
+ }
+
+ if (_exec_err) _exec_gate.notify_all();
+}
+
+void rpc_client::_handle_response_data(const boost::system::error_code& err, size_t transferred, size_t expected)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _exec_err = err;
+ if (transferred != expected) {
+ _exec_err.assign(boost::asio::error::operation_aborted, boost::system::system_category());
+ }
+
+ _exec_gate.notify_all();
+
+ _wait_for_next_response_header();
+}
+
+void rpc_client::_wait_for_next_response_header() {
+ //_mutex must be locked when this call is made
+ boost::asio::async_read(
+ _socket,
+ boost::asio::buffer(&_response.header, sizeof(_response.header)),
+ boost::bind(&rpc_client::_handle_response_hdr, this,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred,
+ sizeof(_response.header)));
+}
+
+}}
diff --git a/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp
new file mode 100644
index 000000000..1a1f1cd21
--- /dev/null
+++ b/host/lib/transport/nirio/rpc/usrprio_rpc_client.cpp
@@ -0,0 +1,229 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// 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 <uhd/transport/nirio/rpc/usrprio_rpc_client.hpp>
+#include <uhd/utils/platform.hpp>
+
+namespace uhd { namespace usrprio_rpc {
+
+usrprio_rpc_client::usrprio_rpc_client(
+ std::string server,
+ std::string port
+) : _rpc_client(server, port, uhd::get_process_id(), uhd::get_host_id()),
+ _timeout(boost::posix_time::milliseconds(DEFAULT_TIMEOUT_IN_MS))
+{
+ _ctor_status = _rpc_client.status() ? NiRio_Status_RpcConnectionError : NiRio_Status_Success;
+}
+
+usrprio_rpc_client::~usrprio_rpc_client()
+{
+}
+
+nirio_status usrprio_rpc_client::niusrprio_enumerate(NIUSRPRIO_ENUMERATE_ARGS)
+/*
+#define NIUSRPRIO_ENUMERATE_ARGS \
+ usrprio_device_info_vtr& device_info_vtr
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+ boost::uint32_t vtr_size = 0;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_ENUMERATE, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ out_args >> vtr_size;
+ }
+ if (nirio_status_not_fatal(status) && vtr_size > 0) {
+ device_info_vtr.resize(vtr_size);
+ for (size_t i = 0; i < (size_t)vtr_size; i++) {
+ usrprio_device_info info;
+ out_args >> info;
+ device_info_vtr[i] = info;
+ }
+ }
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_open_session(NIUSRPRIO_OPEN_SESSION_ARGS)
+/*
+#define NIUSRPRIO_OPEN_SESSION_ARGS \
+ const std::string& resource, \
+ const std::string& path, \
+ const std::string& signature, \
+ const boost::uint16_t& download_fpga
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+ in_args << path;
+ in_args << signature;
+ in_args << download_fpga;
+
+ //Open needs a longer timeout because the FPGA download can take upto 6 secs and the NiFpga libload can take 4.
+ static const boost::uint32_t OPEN_TIMEOUT = 15000;
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_OPEN_SESSION, in_args, out_args, boost::posix_time::milliseconds(OPEN_TIMEOUT)));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_close_session(NIUSRPRIO_CLOSE_SESSION_ARGS)
+/*
+#define NIUSRPRIO_CLOSE_SESSION_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_CLOSE_SESSION, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_reset_device(NIUSRPRIO_RESET_SESSION_ARGS)
+/*
+#define NIUSRPRIO_RESET_SESSION_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_RESET_SESSION, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_get_interface_path(NIUSRPRIO_GET_INTERFACE_PATH_ARGS)
+/*
+#define NIUSRPRIO_GET_INTERFACE_PATH_ARGS \
+ const std::string& resource, \
+ std::string& interface_path
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_GET_INTERFACE_PATH, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ out_args >> interface_path;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_download_fpga_to_flash(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS)
+/*
+#define NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH_ARGS \
+ const boost::uint32_t& interface_num, \
+ const std::string& bitstream_path
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+ in_args << bitstream_path;
+
+ static const boost::uint32_t DOWNLOAD_FPGA_TIMEOUT = 1200000;
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_DOWNLOAD_FPGA_TO_FLASH, in_args, out_args,
+ boost::posix_time::milliseconds(DOWNLOAD_FPGA_TIMEOUT)));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::niusrprio_download_bitstream_to_fpga(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS)
+/*
+#define NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA_ARGS \
+ const std::string& resource
+*/
+{
+ usrprio_rpc::func_args_writer_t in_args;
+ usrprio_rpc::func_args_reader_t out_args;
+ nirio_status status = NiRio_Status_Success;
+
+ in_args << resource;
+
+ status = _boost_error_to_nirio_status(
+ _rpc_client.call(NIUSRPRIO_DOWNLOAD_BITSTREAM_TO_FPGA, in_args, out_args, _timeout));
+
+ if (nirio_status_not_fatal(status)) {
+ out_args >> status;
+ }
+
+ return status;
+}
+
+nirio_status usrprio_rpc_client::_boost_error_to_nirio_status(const boost::system::error_code& err) {
+ if (err) {
+ switch (err.value()) {
+ case boost::asio::error::connection_aborted:
+ case boost::asio::error::connection_refused:
+ case boost::asio::error::eof:
+ return NiRio_Status_RpcSessionError;
+ case boost::asio::error::timed_out:
+ case boost::asio::error::operation_aborted:
+ return NiRio_Status_RpcOperationError;
+ default:
+ return NiRio_Status_SoftwareFault;
+ }
+ } else {
+ return NiRio_Status_Success;
+ }
+}
+
+}}