summaryrefslogtreecommitdiffstats
path: root/host/lib/transport
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/transport')
-rw-r--r--host/lib/transport/udp.cpp67
-rw-r--r--host/lib/transport/udp_simple.cpp158
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp154
-rw-r--r--host/lib/transport/vrt.cpp109
4 files changed, 421 insertions, 67 deletions
diff --git a/host/lib/transport/udp.cpp b/host/lib/transport/udp.cpp
deleted file mode 100644
index 06defb107..000000000
--- a/host/lib/transport/udp.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-//
-// Copyright 2010 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/udp.hpp>
-#include <boost/format.hpp>
-#include <boost/assign/list_of.hpp>
-#include <iostream>
-
-uhd::transport::udp::udp(const std::string &addr, const std::string &port, bool bcast){
- //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
-
- // resolve the address
- boost::asio::ip::udp::resolver resolver(_io_service);
- boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port);
- _receiver_endpoint = *resolver.resolve(query);
-
- // Create and open the socket
- _socket = new boost::asio::ip::udp::socket(_io_service);
- _socket->open(boost::asio::ip::udp::v4());
-
- if (bcast){
- // Allow broadcasting
- boost::asio::socket_base::broadcast option(true);
- _socket->set_option(option);
- }
-
-}
-
-uhd::transport::udp::~udp(void){
- delete _socket;
-}
-
-void uhd::transport::udp::send(const std::vector<boost::asio::const_buffer> &buffs){
- _socket->send_to(buffs, _receiver_endpoint);
-}
-
-void uhd::transport::udp::send(const boost::asio::const_buffer &buff){
- std::vector<boost::asio::const_buffer> buffs = boost::assign::list_of(buff);
- send(buffs);
-}
-
-uhd::shared_iovec uhd::transport::udp::recv(void){
- //allocate a buffer for the number of bytes available (could be zero)
- uhd::shared_iovec iov(_socket->available());
- //call recv only if data is available
- if (iov.len != 0){
- _socket->receive_from(
- boost::asio::buffer(iov.base, iov.len),
- _sender_endpoint
- );
- }
- return iov;
-}
diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp
new file mode 100644
index 000000000..3c8ecb70d
--- /dev/null
+++ b/host/lib/transport/udp_simple.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright 2010 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/udp_simple.hpp>
+#include <boost/thread.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+using namespace uhd::transport;
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * A receive timeout for a socket:
+ *
+ * It seems that asio cannot have timeouts with synchronous io.
+ * However, we can implement a polling loop that will timeout.
+ * This is okay bacause this is the slow-path implementation.
+ *
+ * \param socket the asio socket
+ */
+static void reasonable_recv_timeout(
+ boost::asio::ip::udp::socket &socket
+){
+ boost::asio::deadline_timer timer(socket.get_io_service());
+ timer.expires_from_now(boost::posix_time::milliseconds(100));
+ while (not (socket.available() or timer.expires_from_now().is_negative())){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+}
+
+/***********************************************************************
+ * UDP connected implementation class
+ **********************************************************************/
+class udp_connected_impl : public udp_simple{
+public:
+ //structors
+ udp_connected_impl(const std::string &addr, const std::string &port);
+ ~udp_connected_impl(void);
+
+ //send/recv
+ size_t send(const boost::asio::const_buffer &buff);
+ size_t recv(const boost::asio::mutable_buffer &buff);
+
+private:
+ boost::asio::ip::udp::socket *_socket;
+ boost::asio::io_service _io_service;
+};
+
+udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){
+ //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
+
+ // resolve the address
+ boost::asio::ip::udp::resolver resolver(_io_service);
+ boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port);
+ boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
+
+ // Create, open, and connect the socket
+ _socket = new boost::asio::ip::udp::socket(_io_service);
+ _socket->open(boost::asio::ip::udp::v4());
+ _socket->connect(receiver_endpoint);
+}
+
+udp_connected_impl::~udp_connected_impl(void){
+ delete _socket;
+}
+
+size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){
+ return _socket->send(boost::asio::buffer(buff));
+}
+
+size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff){
+ reasonable_recv_timeout(*_socket);
+ if (not _socket->available()) return 0;
+ return _socket->receive(boost::asio::buffer(buff));
+}
+
+/***********************************************************************
+ * UDP broadcast implementation class
+ **********************************************************************/
+class udp_broadcast_impl : public udp_simple{
+public:
+ //structors
+ udp_broadcast_impl(const std::string &addr, const std::string &port);
+ ~udp_broadcast_impl(void);
+
+ //send/recv
+ size_t send(const boost::asio::const_buffer &buff);
+ size_t recv(const boost::asio::mutable_buffer &buff);
+
+private:
+ boost::asio::ip::udp::socket *_socket;
+ boost::asio::ip::udp::endpoint _receiver_endpoint;
+ boost::asio::io_service _io_service;
+};
+
+udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){
+ //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
+
+ // resolve the address
+ boost::asio::ip::udp::resolver resolver(_io_service);
+ boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port);
+ _receiver_endpoint = *resolver.resolve(query);
+
+ // Create and open the socket
+ _socket = new boost::asio::ip::udp::socket(_io_service);
+ _socket->open(boost::asio::ip::udp::v4());
+
+ // Allow broadcasting
+ boost::asio::socket_base::broadcast option(true);
+ _socket->set_option(option);
+
+}
+
+udp_broadcast_impl::~udp_broadcast_impl(void){
+ delete _socket;
+}
+
+size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){
+ return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint);
+}
+
+size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff){
+ reasonable_recv_timeout(*_socket);
+ if (not _socket->available()) return 0;
+ boost::asio::ip::udp::endpoint sender_endpoint;
+ return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint);
+}
+
+/***********************************************************************
+ * UDP public make functions
+ **********************************************************************/
+udp_simple::sptr udp_simple::make_connected(
+ const std::string &addr, const std::string &port
+){
+ return sptr(new udp_connected_impl(addr, port));
+}
+
+udp_simple::sptr udp_simple::make_broadcast(
+ const std::string &addr, const std::string &port
+){
+ return sptr(new udp_broadcast_impl(addr, port));
+}
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
new file mode 100644
index 000000000..219ae8720
--- /dev/null
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -0,0 +1,154 @@
+//
+// Copyright 2010 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/udp_zero_copy.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/thread.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+using namespace uhd::transport;
+
+/***********************************************************************
+ * Smart buffer implementation for udp zerocopy none
+ *
+ * This smart buffer implemention houses a const buffer.
+ * When the smart buffer is deleted, the buffer is freed.
+ * The memory in the const buffer is allocated with new [],
+ * and so the destructor frees the buffer with delete [].
+ **********************************************************************/
+class smart_buffer_impl : public smart_buffer{
+public:
+ smart_buffer_impl(const boost::asio::const_buffer &buff){
+ _buff = buff;
+ }
+
+ ~smart_buffer_impl(void){
+ delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff);
+ }
+
+ const boost::asio::const_buffer &get(void) const{
+ return _buff;
+ }
+
+private:
+ boost::asio::const_buffer _buff;
+};
+
+/***********************************************************************
+ * UDP zero copy implementation class
+ *
+ * This is the portable zero copy implementation for systems
+ * where a faster, platform specific solution is not available.
+ *
+ * It uses boost asio udp sockets and the standard recv() class,
+ * and in-fact, is not actually doing a zero-copy implementation.
+ **********************************************************************/
+class udp_zero_copy_impl : public udp_zero_copy{
+public:
+ //structors
+ udp_zero_copy_impl(const std::string &addr, const std::string &port);
+ ~udp_zero_copy_impl(void);
+
+ //send/recv
+ size_t send(const boost::asio::const_buffer &buff);
+ smart_buffer::sptr recv(void);
+
+private:
+ boost::asio::ip::udp::socket *_socket;
+ boost::asio::io_service _io_service;
+
+ size_t get_recv_buff_size(void);
+ void set_recv_buff_size(size_t);
+};
+
+udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){
+ //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl;
+
+ // resolve the address
+ boost::asio::ip::udp::resolver resolver(_io_service);
+ boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port);
+ boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query);
+
+ // Create, open, and connect the socket
+ _socket = new boost::asio::ip::udp::socket(_io_service);
+ _socket->open(boost::asio::ip::udp::v4());
+ _socket->connect(receiver_endpoint);
+
+ // set the rx socket buffer size:
+ // pick a huge size, and deal with whatever we get
+ set_recv_buff_size(size_t(54321e3)); //some big number!
+ size_t current_buff_size = get_recv_buff_size();
+ std::cout << boost::format(
+ "Current rx socket buffer size: %d\n"
+ ) % current_buff_size;
+ if (current_buff_size < size_t(.1e6)) std::cout << boost::format(
+ "Adjust max rx socket buffer size (linux only):\n"
+ " sysctl -w net.core.rmem_max=VALUE\n"
+ );
+}
+
+udp_zero_copy_impl::~udp_zero_copy_impl(void){
+ delete _socket;
+}
+
+size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){
+ return _socket->send(boost::asio::buffer(buff));
+}
+
+smart_buffer::sptr udp_zero_copy_impl::recv(void){
+ size_t available = 0;
+
+ //implement timeout through polling and sleeping
+ boost::asio::deadline_timer timer(_socket->get_io_service());
+ timer.expires_from_now(boost::posix_time::milliseconds(50));
+ while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1));
+ }
+
+ //allocate memory and create buffer
+ boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)];
+ boost::asio::mutable_buffer buff(buff_mem, available);
+
+ //receive only if data is available
+ if (available){
+ _socket->receive(boost::asio::buffer(buff));
+ }
+
+ //create a new smart buffer to house the data
+ return smart_buffer::sptr(new smart_buffer_impl(buff));
+}
+
+size_t udp_zero_copy_impl::get_recv_buff_size(void){
+ boost::asio::socket_base::receive_buffer_size option;
+ _socket->get_option(option);
+ return option.value();
+}
+
+void udp_zero_copy_impl::set_recv_buff_size(size_t new_size){
+ boost::asio::socket_base::receive_buffer_size option(new_size);
+ _socket->set_option(option);
+}
+
+/***********************************************************************
+ * UDP zero copy make function
+ **********************************************************************/
+udp_zero_copy::sptr udp_zero_copy::make(
+ const std::string &addr, const std::string &port
+){
+ return sptr(new udp_zero_copy_impl(addr, port));
+}
diff --git a/host/lib/transport/vrt.cpp b/host/lib/transport/vrt.cpp
new file mode 100644
index 000000000..a06b5bf21
--- /dev/null
+++ b/host/lib/transport/vrt.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright 2010 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/vrt.hpp>
+#include <boost/asio.hpp> //endianness conversion
+#include <stdexcept>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+void vrt::pack(
+ const tx_metadata_t &metadata, //input
+ boost::uint32_t *header_buff, //output
+ size_t &num_header_words32, //output
+ size_t num_payload_words32, //input
+ size_t &num_packet_words32, //output
+ size_t packet_count //input
+){
+ boost::uint32_t vrt_hdr_flags = 0;
+ num_header_words32 = 1;
+
+ //load the vrt header and flags
+ if(metadata.has_stream_id){
+ vrt_hdr_flags |= (0x1 << 28); //IF Data packet with Stream Identifier
+ header_buff[num_header_words32++] = htonl(metadata.stream_id);
+ }
+
+ if(metadata.has_time_spec){
+ vrt_hdr_flags |= (0x3 << 22) | (0x1 << 20); //TSI: Other, TSF: Sample Count Timestamp
+ header_buff[num_header_words32++] = htonl(metadata.time_spec.secs);
+ header_buff[num_header_words32++] = htonl(metadata.time_spec.ticks);
+ header_buff[num_header_words32++] = 0; //unused part of fractional seconds
+ }
+
+ vrt_hdr_flags |= (metadata.start_of_burst)? (0x1 << 25) : 0;
+ vrt_hdr_flags |= (metadata.end_of_burst)? (0x1 << 24) : 0;
+
+ num_packet_words32 = num_header_words32 + num_payload_words32;
+
+ //fill in complete header word
+ header_buff[0] = htonl(vrt_hdr_flags |
+ ((packet_count & 0xf) << 16) |
+ (num_packet_words32 & 0xffff)
+ );
+}
+
+void vrt::unpack(
+ rx_metadata_t &metadata, //output
+ const boost::uint32_t *header_buff, //input
+ size_t &num_header_words32, //output
+ size_t &num_payload_words32, //output
+ size_t num_packet_words32, //input
+ size_t &packet_count //output
+){
+ //clear the metadata
+ metadata = rx_metadata_t();
+
+ //extract vrt header
+ boost::uint32_t vrt_hdr_word = ntohl(header_buff[0]);
+ size_t packet_words32 = vrt_hdr_word & 0xffff;
+ packet_count = (vrt_hdr_word >> 16) & 0xf;
+
+ //failure cases
+ if (packet_words32 == 0 or num_packet_words32 < packet_words32)
+ throw std::runtime_error("bad vrt header or packet fragment");
+ if (vrt_hdr_word & (0x7 << 29))
+ throw std::runtime_error("unsupported vrt packet type");
+
+ //parse the header flags
+ num_header_words32 = 1;
+
+ if (vrt_hdr_word & (0x1 << 28)){ //stream id
+ metadata.has_stream_id = true;
+ metadata.stream_id = ntohl(header_buff[num_header_words32++]);
+ }
+
+ if (vrt_hdr_word & (0x1 << 27)){ //class id (we dont use)
+ num_header_words32 += 2;
+ }
+
+ if (vrt_hdr_word & (0x3 << 22)){ //integer time
+ metadata.has_time_spec = true;
+ metadata.time_spec.secs = ntohl(header_buff[num_header_words32++]);
+ }
+
+ if (vrt_hdr_word & (0x3 << 20)){ //fractional time
+ metadata.has_time_spec = true;
+ metadata.time_spec.ticks = ntohl(header_buff[num_header_words32++]);
+ num_header_words32++; //unused part of fractional seconds
+ }
+
+ size_t num_trailer_words32 = (vrt_hdr_word & (0x1 << 26))? 1 : 0;
+
+ num_payload_words32 = packet_words32 - num_header_words32 - num_trailer_words32;
+}