aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/transport
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/transport')
-rw-r--r--host/lib/transport/CMakeLists.txt58
-rwxr-xr-xhost/lib/transport/gen_convert_types.py190
-rwxr-xr-xhost/lib/transport/gen_vrt.py230
-rw-r--r--host/lib/transport/if_addrs.cpp109
-rw-r--r--host/lib/transport/udp_simple.cpp159
-rw-r--r--host/lib/transport/udp_zero_copy_asio.cpp178
-rw-r--r--host/lib/transport/vrt_packet_handler.hpp389
-rw-r--r--host/lib/transport/zero_copy.cpp139
8 files changed, 1452 insertions, 0 deletions
diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt
new file mode 100644
index 000000000..872865d6c
--- /dev/null
+++ b/host/lib/transport/CMakeLists.txt
@@ -0,0 +1,58 @@
+#
+# 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/>.
+#
+
+#This file will be included by cmake, use absolute paths!
+
+########################################################################
+# Setup defines for interface address discovery
+########################################################################
+MESSAGE(STATUS "Configuring interface address discovery...")
+
+INCLUDE(CheckIncludeFileCXX)
+CHECK_INCLUDE_FILE_CXX(ifaddrs.h HAVE_IFADDRS_H)
+CHECK_INCLUDE_FILE_CXX(winsock2.h HAVE_WINSOCK2_H)
+
+IF(HAVE_IFADDRS_H)
+ MESSAGE(STATUS " Interface address discovery supported through getifaddrs.")
+ ADD_DEFINITIONS(-DHAVE_IFADDRS_H)
+ELSEIF(HAVE_WINSOCK2_H)
+ MESSAGE(STATUS " Interface address discovery supported through SIO_GET_INTERFACE_LIST.")
+ ADD_DEFINITIONS(-DHAVE_WINSOCK2_H)
+ELSE(HAVE_IFADDRS_H)
+ MESSAGE(STATUS " Interface address discovery not supported.")
+ENDIF(HAVE_IFADDRS_H)
+
+########################################################################
+# Append to the list of sources for lib uhd
+########################################################################
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_SOURCE_DIR}/lib/transport/gen_vrt.py
+ ${CMAKE_BINARY_DIR}/lib/transport/vrt.cpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_SOURCE_DIR}/lib/transport/gen_convert_types.py
+ ${CMAKE_BINARY_DIR}/lib/transport/convert_types.cpp
+)
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_SOURCE_DIR}/lib/transport/if_addrs.cpp
+ ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp
+ ${CMAKE_SOURCE_DIR}/lib/transport/udp_zero_copy_asio.cpp
+ ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler.hpp
+ ${CMAKE_SOURCE_DIR}/lib/transport/zero_copy.cpp
+)
diff --git a/host/lib/transport/gen_convert_types.py b/host/lib/transport/gen_convert_types.py
new file mode 100755
index 000000000..af2bcc7cb
--- /dev/null
+++ b/host/lib/transport/gen_convert_types.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+#
+# 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/>.
+#
+
+TMPL_TEXT = """
+#import time
+/***********************************************************************
+ * This file was generated by $file on $time.strftime("%c")
+ **********************************************************************/
+
+\#include <uhd/config.hpp>
+\#include <uhd/transport/convert_types.hpp>
+\#include <uhd/utils/byteswap.hpp>
+\#include <boost/cstdint.hpp>
+\#include <boost/detail/endian.hpp>
+\#include <stdexcept>
+\#include <complex>
+
+//define the endian macros to convert integers
+\#ifdef BOOST_BIG_ENDIAN
+ \#define BE_MACRO(x) x
+ \#define LE_MACRO(x) uhd::byteswap(x)
+ static const bool is_big_endian = true;
+\#else
+ \#define BE_MACRO(x) uhd::byteswap(x)
+ \#define LE_MACRO(x) x
+ static const bool is_big_endian = false;
+\#endif
+
+using namespace uhd;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+typedef std::complex<float> fc32_t;
+typedef std::complex<boost::int16_t> sc16_t;
+typedef boost::uint32_t item32_t;
+
+static const float shorts_per_float = float(32767);
+static const float floats_per_short = float(1.0/shorts_per_float);
+
+/***********************************************************************
+ * Single-sample converters
+ **********************************************************************/
+static UHD_INLINE item32_t sc16_to_item32(sc16_t num){
+ boost::uint16_t real = boost::int16_t(num.real());
+ boost::uint16_t imag = boost::int16_t(num.imag());
+ return (item32_t(real) << 16) | (item32_t(imag) << 0);
+}
+
+static UHD_INLINE sc16_t item32_to_sc16(item32_t item){
+ return sc16_t(
+ boost::uint16_t(item >> 16),
+ boost::uint16_t(item >> 0)
+ );
+}
+
+static UHD_INLINE item32_t fc32_to_item32(fc32_t num){
+ boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float);
+ boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float);
+ return (item32_t(real) << 16) | (item32_t(imag) << 0);
+}
+
+static UHD_INLINE fc32_t item32_to_fc32(item32_t item){
+ return fc32_t(
+ float(boost::int16_t(item >> 16)*floats_per_short),
+ float(boost::int16_t(item >> 0)*floats_per_short)
+ );
+}
+
+/***********************************************************************
+ * Sample-buffer converters
+ **********************************************************************/
+UHD_INLINE boost::uint8_t get_pred(
+ const io_type_t &io_type,
+ const otw_type_t &otw_type
+){
+ boost::uint8_t pred = 0;
+
+ switch(otw_type.byteorder){
+ case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.be_p; break;
+ case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.le_p; break;
+ ##let the compiler determine the native byte order (we could use python sys.byteorder)
+ case otw_type_t::BO_NATIVE: pred |= (is_big_endian)? $ph.be_p : $ph.le_p; break;
+ default: throw std::runtime_error("unhandled byteorder type");
+ }
+
+ switch(otw_type.width){
+ case 16: pred |= $ph.w16_p; break;
+ default: throw std::runtime_error("unhandled bit width");
+ }
+
+ switch(io_type.tid){
+ case io_type_t::COMPLEX_INT16: pred |= $ph.sc16_p; break;
+ case io_type_t::COMPLEX_FLOAT32: pred |= $ph.fc32_p; break;
+ default: throw std::runtime_error("unhandled io type id");
+ }
+
+ return pred;
+}
+
+void transport::convert_io_type_to_otw_type(
+ const void *io_buff, const io_type_t &io_type,
+ void *otw_buff, const otw_type_t &otw_type,
+ size_t num_samps
+){
+ switch(get_pred(io_type, otw_type)){
+ #for $pred in range(2**$ph.nbits)
+ case $pred:
+ #set $out_type = $ph.get_dev_type($pred)
+ #set $in_type = $ph.get_host_type($pred)
+ #set $converter = $in_type+"_to_"+$out_type
+ #set $xe_macro = $ph.get_xe_macro($pred)
+ for (size_t i = 0; i < num_samps; i++){
+ (($(out_type)_t *)otw_buff)[i] = $(xe_macro)($(converter)(((const $(in_type)_t *)io_buff)[i]));
+ }
+ break;
+ #end for
+ }
+}
+
+void transport::convert_otw_type_to_io_type(
+ const void *otw_buff, const otw_type_t &otw_type,
+ void *io_buff, const io_type_t &io_type,
+ size_t num_samps
+){
+ switch(get_pred(io_type, otw_type)){
+ #for $pred in range(4)
+ case $pred:
+ #set $out_type = $ph.get_host_type($pred)
+ #set $in_type = $ph.get_dev_type($pred)
+ #set $converter = $in_type+"_to_"+$out_type
+ #set $xe_macro = $ph.get_xe_macro($pred)
+ for (size_t i = 0; i < num_samps; i++){
+ (($(out_type)_t *)io_buff)[i] = $(converter)($(xe_macro)(((const $(in_type)_t *)otw_buff)[i]));
+ }
+ break;
+ #end for
+ }
+}
+
+"""
+
+def parse_tmpl(_tmpl_text, **kwargs):
+ from Cheetah.Template import Template
+ return str(Template(_tmpl_text, kwargs))
+
+class ph:
+ be_p = 0b00001
+ le_p = 0b00000
+ w16_p = 0b00000
+ sc16_p = 0b00010
+ fc32_p = 0b00000
+
+ nbits = 2 #see above
+
+ @staticmethod
+ def get_xe_macro(pred):
+ if (pred & ph.be_p) == ph.be_p: return 'BE_MACRO'
+ if (pred & ph.le_p) == ph.le_p: return 'LE_MACRO'
+ raise NotImplementedError
+
+ @staticmethod
+ def get_dev_type(pred):
+ if (pred & ph.w16_p) == ph.w16_p: return 'item32'
+ raise NotImplementedError
+
+ @staticmethod
+ def get_host_type(pred):
+ if (pred & ph.sc16_p) == ph.sc16_p: return 'sc16'
+ if (pred & ph.fc32_p) == ph.fc32_p: return 'fc32'
+ raise NotImplementedError
+
+if __name__ == '__main__':
+ import sys
+ open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=__file__, ph=ph))
diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py
new file mode 100755
index 000000000..6cdd6645d
--- /dev/null
+++ b/host/lib/transport/gen_vrt.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python
+#
+# 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/>.
+#
+
+"""
+The vrt packer/unpacker code generator:
+
+This script will generate the pack and unpack routines that convert
+metatdata into vrt headers and vrt headers into metadata.
+
+The generated code infers jump tables to speed-up the parsing time.
+"""
+
+TMPL_TEXT = """
+#import time
+/***********************************************************************
+ * This file was generated by $file on $time.strftime("%c")
+ **********************************************************************/
+
+\#include <uhd/transport/vrt.hpp>
+\#include <uhd/utils/byteswap.hpp>
+\#include <boost/detail/endian.hpp>
+\#include <stdexcept>
+
+//define the endian macros to convert integers
+\#ifdef BOOST_BIG_ENDIAN
+ \#define BE_MACRO(x) (x)
+ \#define LE_MACRO(x) uhd::byteswap(x)
+\#else
+ \#define BE_MACRO(x) uhd::byteswap(x)
+ \#define LE_MACRO(x) (x)
+\#endif
+
+using namespace uhd;
+using namespace uhd::transport;
+
+########################################################################
+#def gen_code($XE_MACRO, $suffix)
+########################################################################
+
+########################################################################
+## setup predicates
+########################################################################
+#set $sid_p = 0b00001
+#set $cid_p = 0b00010
+#set $tsi_p = 0b00100
+#set $tsf_p = 0b01000
+#set $tlr_p = 0b10000
+
+void vrt::pack_$(suffix)(
+ 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
+ double tick_rate //input
+){
+ boost::uint32_t vrt_hdr_flags = 0;
+
+ boost::uint8_t pred = 0;
+ if (metadata.has_stream_id) pred |= $hex($sid_p);
+ if (metadata.has_time_spec) pred |= $hex($tsi_p | $tsf_p);
+
+ switch(pred){
+ #for $pred in range(2**5)
+ case $pred:
+ #set $num_header_words = 1
+ #set $flags = 0
+ ########## Stream ID ##########
+ #if $pred & $sid_p
+ header_buff[$num_header_words] = $(XE_MACRO)(metadata.stream_id);
+ #set $num_header_words += 1
+ #set $flags |= (0x1 << 28);
+ #end if
+ ########## Class ID ##########
+ #if $pred & $cid_p
+ header_buff[$num_header_words] = 0;
+ #set $num_header_words += 1
+ header_buff[$num_header_words] = 0;
+ #set $num_header_words += 1
+ #set $flags |= (0x1 << 27);
+ #end if
+ ########## Integer Time ##########
+ #if $pred & $tsi_p
+ header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.secs);
+ #set $num_header_words += 1
+ #set $flags |= (0x3 << 22);
+ #end if
+ ########## Fractional Time ##########
+ #if $pred & $tsf_p
+ header_buff[$num_header_words] = 0;
+ #set $num_header_words += 1
+ header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.get_ticks(tick_rate));
+ #set $num_header_words += 1
+ #set $flags |= (0x1 << 20);
+ #end if
+ ########## Trailer ##########
+ #if $pred & $tlr_p
+ #set $flags |= (0x1 << 26);
+ #set $num_trailer_words = 1;
+ #else
+ #set $num_trailer_words = 0;
+ #end if
+ ########## Variables ##########
+ num_header_words32 = $num_header_words;
+ num_packet_words32 = $($num_header_words + $num_trailer_words) + num_payload_words32;
+ vrt_hdr_flags = $hex($flags);
+ break;
+ #end for
+ }
+
+ //set the burst flags
+ if (metadata.start_of_burst) vrt_hdr_flags |= $hex(0x1 << 25);
+ if (metadata.end_of_burst) vrt_hdr_flags |= $hex(0x1 << 24);
+
+ //fill in complete header word
+ header_buff[0] = $(XE_MACRO)(boost::uint32_t(0
+ | vrt_hdr_flags
+ | ((packet_count & 0xf) << 16)
+ | (num_packet_words32 & 0xffff)
+ ));
+}
+
+void vrt::unpack_$(suffix)(
+ 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
+ double tick_rate //input
+){
+ //clear the metadata
+ metadata = rx_metadata_t();
+
+ //extract vrt header
+ boost::uint32_t vrt_hdr_word = $(XE_MACRO)(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");
+
+ boost::uint8_t pred = 0;
+ if(vrt_hdr_word & $hex(0x1 << 28)) pred |= $hex($sid_p);
+ if(vrt_hdr_word & $hex(0x1 << 27)) pred |= $hex($cid_p);
+ if(vrt_hdr_word & $hex(0x3 << 22)) pred |= $hex($tsi_p);
+ if(vrt_hdr_word & $hex(0x3 << 20)) pred |= $hex($tsf_p);
+ if(vrt_hdr_word & $hex(0x1 << 26)) pred |= $hex($tlr_p);
+
+ switch(pred){
+ #for $pred in range(2**5)
+ case $pred:
+ #set $set_has_time_spec = False
+ #set $num_header_words = 1
+ ########## Stream ID ##########
+ #if $pred & $sid_p
+ metadata.has_stream_id = true;
+ metadata.stream_id = $(XE_MACRO)(header_buff[$num_header_words]);
+ #set $num_header_words += 1
+ #end if
+ ########## Class ID ##########
+ #if $pred & $cid_p
+ #set $num_header_words += 1
+ #set $num_header_words += 1
+ #end if
+ ########## Integer Time ##########
+ #if $pred & $tsi_p
+ metadata.has_time_spec = true;
+ #set $set_has_time_spec = True
+ metadata.time_spec.secs = $(XE_MACRO)(header_buff[$num_header_words]);
+ #set $num_header_words += 1
+ #end if
+ ########## Fractional Time ##########
+ #if $pred & $tsf_p
+ #if not $set_has_time_spec
+ metadata.has_time_spec = true;
+ #set $set_has_time_spec = True
+ #end if
+ #set $num_header_words += 1
+ metadata.time_spec.set_ticks($(XE_MACRO)(header_buff[$num_header_words]), tick_rate);
+ #set $num_header_words += 1
+ #end if
+ ########## Trailer ##########
+ #if $pred & $tlr_p
+ #set $num_trailer_words = 1;
+ #else
+ #set $num_trailer_words = 0;
+ #end if
+ ########## Variables ##########
+ num_header_words32 = $num_header_words;
+ num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words);
+ break;
+ #end for
+ }
+}
+
+########################################################################
+#end def
+########################################################################
+
+$gen_code("BE_MACRO", "be")
+$gen_code("LE_MACRO", "le")
+"""
+
+def parse_tmpl(_tmpl_text, **kwargs):
+ from Cheetah.Template import Template
+ return str(Template(_tmpl_text, kwargs))
+
+if __name__ == '__main__':
+ import sys
+ open(sys.argv[1], 'w').write(parse_tmpl(TMPL_TEXT, file=__file__))
diff --git a/host/lib/transport/if_addrs.cpp b/host/lib/transport/if_addrs.cpp
new file mode 100644
index 000000000..5c8c8a176
--- /dev/null
+++ b/host/lib/transport/if_addrs.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/if_addrs.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/cstdint.hpp>
+#include <iostream>
+
+uhd::transport::if_addrs_t::if_addrs_t(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Interface address discovery through ifaddrs api
+ **********************************************************************/
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+
+static boost::asio::ip::address_v4 sockaddr_to_ip_addr(sockaddr *addr){
+ return boost::asio::ip::address_v4(ntohl(
+ reinterpret_cast<sockaddr_in*>(addr)->sin_addr.s_addr
+ ));
+}
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ std::vector<if_addrs_t> if_addrs;
+ struct ifaddrs *ifap;
+ if (getifaddrs(&ifap) == 0){
+ for (struct ifaddrs *iter = ifap; iter != NULL; iter = iter->ifa_next){
+ //ensure that the entries are valid
+ if (iter->ifa_addr->sa_family != AF_INET) continue;
+ if (iter->ifa_netmask->sa_family != AF_INET) continue;
+ if (iter->ifa_broadaddr->sa_family != AF_INET) continue;
+
+ //append a new set of interface addresses
+ if_addrs_t if_addr;
+ if_addr.inet = sockaddr_to_ip_addr(iter->ifa_addr).to_string();
+ if_addr.mask = sockaddr_to_ip_addr(iter->ifa_netmask).to_string();
+ if_addr.bcast = sockaddr_to_ip_addr(iter->ifa_broadaddr).to_string();
+ if_addrs.push_back(if_addr);
+ }
+ freeifaddrs(ifap);
+ }
+ return if_addrs;
+}
+
+/***********************************************************************
+ * Interface address discovery through windows api (TODO)
+ **********************************************************************/
+#elif HAVE_WINSOCK2_H
+#include <winsock2.h>
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ std::vector<if_addrs_t> if_addrs;
+ SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
+ if (sd == SOCKET_ERROR) {
+ std::cerr << "Failed to get a socket. Error " << WSAGetLastError() <<
+ std::endl; return if_addrs;
+ }
+
+ INTERFACE_INFO InterfaceList[20];
+ unsigned long nBytesReturned;
+ if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
+ sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) {
+ std::cerr << "Failed calling WSAIoctl: error " << WSAGetLastError() <<
+ std::endl;
+ return if_addrs;
+ }
+
+ int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
+ for (int i = 0; i < nNumInterfaces; ++i) {
+ boost::uint32_t iiAddress = ntohl(reinterpret_cast<sockaddr_in&>(InterfaceList[i].iiAddress).sin_addr.s_addr);
+ boost::uint32_t iiNetmask = ntohl(reinterpret_cast<sockaddr_in&>(InterfaceList[i].iiNetmask).sin_addr.s_addr);
+ boost::uint32_t iiBroadcastAddress = (iiAddress & iiNetmask) | ~iiNetmask;
+
+ if_addrs_t if_addr;
+ if_addr.inet = boost::asio::ip::address_v4(iiAddress).to_string();
+ if_addr.mask = boost::asio::ip::address_v4(iiNetmask).to_string();
+ if_addr.bcast = boost::asio::ip::address_v4(iiBroadcastAddress).to_string();
+ if_addrs.push_back(if_addr);
+ }
+
+ return if_addrs;
+}
+
+/***********************************************************************
+ * Interface address discovery not included
+ **********************************************************************/
+#else /* HAVE_IFADDRS_H */
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+ return std::vector<if_addrs_t>();
+}
+
+#endif /* HAVE_IFADDRS_H */
diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp
new file mode 100644
index 000000000..f339127ad
--- /dev/null
+++ b/host/lib/transport/udp_simple.cpp
@@ -0,0 +1,159 @@
+//
+// 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/asio.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..c3c02707e
--- /dev/null
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -0,0 +1,178 @@
+//
+// 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 <uhd/utils/assert.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/asio.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+using namespace uhd::transport;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3);
+static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv
+static const double RECV_TIMEOUT = 0.1; //100 ms
+
+/***********************************************************************
+ * Zero Copy UDP implementation with ASIO:
+ * This is the portable zero copy implementation for systems
+ * where a faster, platform specific solution is not available.
+ * However, it is not a true zero copy implementation as each
+ * send and recv requires a copy operation to/from userspace.
+ **********************************************************************/
+class udp_zero_copy_impl:
+ public phony_zero_copy_recv_if,
+ public phony_zero_copy_send_if,
+ public udp_zero_copy
+{
+public:
+ typedef boost::shared_ptr<udp_zero_copy_impl> sptr;
+
+ udp_zero_copy_impl(
+ const std::string &addr,
+ const std::string &port
+ ):
+ phony_zero_copy_recv_if(MAX_DGRAM_SIZE),
+ phony_zero_copy_send_if(MAX_DGRAM_SIZE)
+ {
+ //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);
+ _sock_fd = _socket->native();
+ }
+
+ ~udp_zero_copy_impl(void){
+ delete _socket;
+ }
+
+ //get size for internal socket buffer
+ template <typename Opt> size_t get_buff_size(void) const{
+ Opt option;
+ _socket->get_option(option);
+ return option.value();
+ }
+
+ //set size for internal socket buffer
+ template <typename Opt> size_t resize_buff(size_t num_bytes){
+ Opt option(num_bytes);
+ _socket->set_option(option);
+ return get_buff_size<Opt>();
+ }
+
+
+ //The number of frames is approximately the buffer size divided by the max datagram size.
+ //In reality, this is a phony zero-copy interface and the number of frames is infinite.
+ //However, its sensible to advertise a frame count that is approximate to buffer size.
+ //This way, the transport caller will have an idea about how much buffering to create.
+
+ size_t get_num_recv_frames(void) const{
+ return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/MAX_DGRAM_SIZE;
+ }
+
+ size_t get_num_send_frames(void) const{
+ return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/MAX_DGRAM_SIZE;
+ }
+
+private:
+ boost::asio::ip::udp::socket *_socket;
+ boost::asio::io_service _io_service;
+ int _sock_fd;
+
+ size_t recv(const boost::asio::mutable_buffer &buff){
+ //setup timeval for timeout
+ timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = int(RECV_TIMEOUT*1e6);
+
+ //setup rset for timeout
+ fd_set rset;
+ FD_ZERO(&rset);
+ FD_SET(_sock_fd, &rset);
+
+ //call select to perform timed wait
+ if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0;
+
+ return ::recv(
+ _sock_fd,
+ boost::asio::buffer_cast<char *>(buff),
+ boost::asio::buffer_size(buff),
+ 0
+ );
+ }
+
+ size_t send(const boost::asio::const_buffer &buff){
+ return ::send(
+ _sock_fd,
+ boost::asio::buffer_cast<const char *>(buff),
+ boost::asio::buffer_size(buff),
+ 0
+ );
+ }
+};
+
+/***********************************************************************
+ * UDP zero copy make function
+ **********************************************************************/
+template<typename Opt> static void resize_buff_helper(
+ udp_zero_copy_impl::sptr udp_trans,
+ size_t target_size,
+ const std::string &name
+){
+ //resize the buffer if size was provided
+ if (target_size > 0){
+ size_t actual_size = udp_trans->resize_buff<Opt>(target_size);
+ if (target_size != actual_size) std::cout << boost::format(
+ "Target %s sock buff size: %d bytes\n"
+ "Actual %s sock buff size: %d bytes"
+ ) % name % target_size % name % actual_size << std::endl;
+ else std::cout << boost::format(
+ "Current %s sock buff size: %d bytes"
+ ) % name % actual_size << std::endl;
+ }
+
+ //otherwise, ensure that the buffer is at least the minimum size
+ else if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){
+ resize_buff_helper<Opt>(udp_trans, MIN_SOCK_BUFF_SIZE, name);
+ }
+}
+
+udp_zero_copy::sptr udp_zero_copy::make(
+ const std::string &addr,
+ const std::string &port,
+ size_t recv_buff_size,
+ size_t send_buff_size
+){
+ udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port));
+
+ //call the helper to resize send and recv buffers
+ resize_buff_helper<boost::asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv");
+ resize_buff_helper<boost::asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send");
+
+ return udp_trans;
+}
diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp
new file mode 100644
index 000000000..8dfc7b3d0
--- /dev/null
+++ b/host/lib/transport/vrt_packet_handler.hpp
@@ -0,0 +1,389 @@
+//
+// 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/>.
+//
+
+#ifndef INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP
+#define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/device.hpp>
+#include <uhd/types/io_type.hpp>
+#include <uhd/types/otw_type.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/transport/vrt.hpp>
+#include <uhd/transport/convert_types.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <boost/asio/buffer.hpp>
+#include <boost/function.hpp>
+#include <stdexcept>
+#include <iostream>
+
+namespace vrt_packet_handler{
+
+/***********************************************************************
+ * vrt packet handler for recv
+ **********************************************************************/
+ struct recv_state{
+ //init the expected seq number
+ size_t next_packet_seq;
+
+ //state variables to handle fragments
+ uhd::transport::managed_recv_buffer::sptr managed_buff;
+ boost::asio::const_buffer copy_buff;
+ size_t fragment_offset_in_samps;
+
+ recv_state(void){
+ //first expected seq is zero
+ next_packet_seq = 0;
+
+ //initially empty copy buffer
+ copy_buff = boost::asio::buffer("", 0);
+ }
+ };
+
+ typedef boost::function<uhd::transport::managed_recv_buffer::sptr(void)> get_recv_buff_t;
+
+ typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t;
+
+ static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){
+ /* NOP */
+ }
+
+ /*******************************************************************
+ * Unpack a received vrt header and set the copy buffer.
+ * - helper function for vrt_packet_handler::_recv1
+ ******************************************************************/
+ template<typename vrt_unpacker_type>
+ static UHD_INLINE void _recv1_helper(
+ recv_state &state,
+ uhd::rx_metadata_t &metadata,
+ double tick_rate,
+ vrt_unpacker_type vrt_unpacker,
+ size_t vrt_header_offset_words32
+ ){
+ size_t num_packet_words32 = state.managed_buff->size()/sizeof(boost::uint32_t);
+ if (num_packet_words32 <= vrt_header_offset_words32){
+ state.copy_buff = boost::asio::buffer("", 0);
+ return; //must exit here after setting the buffer
+ }
+ const boost::uint32_t *vrt_hdr = state.managed_buff->cast<const boost::uint32_t *>() + vrt_header_offset_words32;
+ size_t num_header_words32_out, num_payload_words32_out, packet_count_out;
+ vrt_unpacker(
+ metadata, //output
+ vrt_hdr, //input
+ num_header_words32_out, //output
+ num_payload_words32_out, //output
+ num_packet_words32, //input
+ packet_count_out, //output
+ tick_rate
+ );
+
+ //handle the packet count / sequence number
+ if (packet_count_out != state.next_packet_seq){
+ std::cerr << "S" << (packet_count_out - state.next_packet_seq)%16;
+ }
+ state.next_packet_seq = (packet_count_out+1)%16;
+
+ //setup the buffer to point to the data
+ state.copy_buff = boost::asio::buffer(
+ vrt_hdr + num_header_words32_out,
+ num_payload_words32_out*sizeof(boost::uint32_t)
+ );
+ }
+
+ /*******************************************************************
+ * Recv data, unpack a vrt header, and copy-convert the data.
+ * - helper function for vrt_packet_handler::recv
+ ******************************************************************/
+ template<typename vrt_unpacker_type>
+ static UHD_INLINE size_t _recv1(
+ recv_state &state,
+ void *recv_mem,
+ size_t total_samps,
+ uhd::rx_metadata_t &metadata,
+ const uhd::io_type_t &io_type,
+ const uhd::otw_type_t &otw_type,
+ double tick_rate,
+ vrt_unpacker_type vrt_unpacker,
+ const get_recv_buff_t &get_recv_buff,
+ //use these two params to handle a layer above vrt
+ size_t vrt_header_offset_words32,
+ const recv_cb_t &recv_cb
+ ){
+ //perform a receive if no rx data is waiting to be copied
+ if (boost::asio::buffer_size(state.copy_buff) == 0){
+ state.fragment_offset_in_samps = 0;
+ state.managed_buff = get_recv_buff();
+ if (state.managed_buff.get() == NULL) return 0;
+ recv_cb(state.managed_buff); //callback before vrt unpack
+ try{
+ _recv1_helper(
+ state, metadata, tick_rate, vrt_unpacker, vrt_header_offset_words32
+ );
+ }catch(const std::exception &e){
+ std::cerr << "Error (recv): " << e.what() << std::endl;
+ return 0;
+ }
+ }
+
+ //extract the number of samples available to copy
+ size_t bytes_per_item = otw_type.get_sample_size();
+ size_t bytes_available = boost::asio::buffer_size(state.copy_buff);
+ size_t num_samps = std::min(total_samps, bytes_available/bytes_per_item);
+
+ //setup the fragment flags and offset
+ metadata.more_fragments = total_samps < num_samps;
+ metadata.fragment_offset = state.fragment_offset_in_samps;
+ state.fragment_offset_in_samps += num_samps; //set for next call
+
+ //copy-convert the samples from the recv buffer
+ uhd::transport::convert_otw_type_to_io_type(
+ boost::asio::buffer_cast<const void*>(state.copy_buff), otw_type,
+ recv_mem, io_type, num_samps
+ );
+
+ //update the rx copy buffer to reflect the bytes copied
+ size_t bytes_copied = num_samps*bytes_per_item;
+ state.copy_buff = boost::asio::buffer(
+ boost::asio::buffer_cast<const boost::uint8_t*>(state.copy_buff) + bytes_copied,
+ bytes_available - bytes_copied
+ );
+
+ return num_samps;
+ }
+
+ /*******************************************************************
+ * Recv vrt packets and copy convert the samples into the buffer.
+ ******************************************************************/
+ template<typename vrt_unpacker_type>
+ static UHD_INLINE size_t recv(
+ recv_state &state,
+ const boost::asio::mutable_buffer &buff,
+ uhd::rx_metadata_t &metadata,
+ uhd::device::recv_mode_t recv_mode,
+ const uhd::io_type_t &io_type,
+ const uhd::otw_type_t &otw_type,
+ double tick_rate,
+ vrt_unpacker_type vrt_unpacker,
+ const get_recv_buff_t &get_recv_buff,
+ //use these two params to handle a layer above vrt
+ size_t vrt_header_offset_words32 = 0,
+ const recv_cb_t& recv_cb = &recv_cb_nop
+ ){
+ metadata = uhd::rx_metadata_t(); //init the metadata
+ const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size;
+
+ switch(recv_mode){
+
+ ////////////////////////////////////////////////////////////////
+ case uhd::device::RECV_MODE_ONE_PACKET:{
+ ////////////////////////////////////////////////////////////////
+ return _recv1(
+ state,
+ boost::asio::buffer_cast<void *>(buff),
+ total_num_samps,
+ metadata,
+ io_type, otw_type,
+ tick_rate,
+ vrt_unpacker,
+ get_recv_buff,
+ vrt_header_offset_words32,
+ recv_cb
+ );
+ }
+
+ ////////////////////////////////////////////////////////////////
+ case uhd::device::RECV_MODE_FULL_BUFF:{
+ ////////////////////////////////////////////////////////////////
+ size_t accum_num_samps = 0;
+ uhd::rx_metadata_t tmp_md;
+ while(accum_num_samps < total_num_samps){
+ size_t num_samps = _recv1(
+ state,
+ boost::asio::buffer_cast<boost::uint8_t *>(buff) + (accum_num_samps*io_type.size),
+ total_num_samps - accum_num_samps,
+ (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept
+ io_type, otw_type,
+ tick_rate,
+ vrt_unpacker,
+ get_recv_buff,
+ vrt_header_offset_words32,
+ recv_cb
+ );
+ if (num_samps == 0) break; //had a recv timeout or error, break loop
+ accum_num_samps += num_samps;
+ }
+ return accum_num_samps;
+ }
+
+ default: throw std::runtime_error("unknown recv mode");
+ }//switch(recv_mode)
+ }
+
+/***********************************************************************
+ * vrt packet handler for send
+ **********************************************************************/
+ struct send_state{
+ //init the expected seq number
+ size_t next_packet_seq;
+
+ send_state(void){
+ next_packet_seq = 0;
+ }
+ };
+
+ typedef boost::function<uhd::transport::managed_send_buffer::sptr(void)> get_send_buff_t;
+
+ typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t;
+
+ static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){
+ /* NOP */
+ }
+
+ /*******************************************************************
+ * Pack a vrt header, copy-convert the data, and send it.
+ * - helper function for vrt_packet_handler::send
+ ******************************************************************/
+ template<typename vrt_packer_type>
+ static UHD_INLINE void _send1(
+ send_state &state,
+ const void *send_mem,
+ size_t num_samps,
+ const uhd::tx_metadata_t &metadata,
+ const uhd::io_type_t &io_type,
+ const uhd::otw_type_t &otw_type,
+ double tick_rate,
+ vrt_packer_type vrt_packer,
+ const get_send_buff_t &get_send_buff,
+ size_t vrt_header_offset_words32,
+ const send_cb_t& send_cb
+ ){
+ //get a new managed send buffer
+ uhd::transport::managed_send_buffer::sptr send_buff = get_send_buff();
+ boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32;
+
+ size_t num_header_words32, num_packet_words32;
+ size_t packet_count = state.next_packet_seq++;
+
+ //pack metadata into a vrt header
+ vrt_packer(
+ metadata, //input
+ tx_mem, //output
+ num_header_words32, //output
+ num_samps, //input
+ num_packet_words32, //output
+ packet_count, //input
+ tick_rate
+ );
+
+ //copy-convert the samples into the send buffer
+ uhd::transport::convert_io_type_to_otw_type(
+ send_mem, io_type,
+ tx_mem + num_header_words32, otw_type,
+ num_samps
+ );
+
+ send_cb(send_buff); //callback after memory filled
+
+ //commit the samples to the zero-copy interface
+ send_buff->commit(num_packet_words32*sizeof(boost::uint32_t));
+ }
+
+ /*******************************************************************
+ * Send vrt packets and copy convert the samples into the buffer.
+ ******************************************************************/
+ template<typename vrt_packer_type>
+ static UHD_INLINE size_t send(
+ send_state &state,
+ const boost::asio::const_buffer &buff,
+ const uhd::tx_metadata_t &metadata,
+ uhd::device::send_mode_t send_mode,
+ const uhd::io_type_t &io_type,
+ const uhd::otw_type_t &otw_type,
+ double tick_rate,
+ vrt_packer_type vrt_packer,
+ const get_send_buff_t &get_send_buff,
+ size_t max_samples_per_packet,
+ //use these two params to handle a layer above vrt
+ size_t vrt_header_offset_words32 = 0,
+ const send_cb_t& send_cb = &send_cb_nop
+ ){
+ const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size;
+ if (total_num_samps <= max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET;
+ switch(send_mode){
+
+ ////////////////////////////////////////////////////////////////
+ case uhd::device::SEND_MODE_ONE_PACKET:{
+ ////////////////////////////////////////////////////////////////
+ size_t num_samps = std::min(total_num_samps, max_samples_per_packet);
+ _send1(
+ state,
+ boost::asio::buffer_cast<const void *>(buff),
+ num_samps,
+ metadata,
+ io_type, otw_type,
+ tick_rate,
+ vrt_packer,
+ get_send_buff,
+ vrt_header_offset_words32,
+ send_cb
+ );
+ return num_samps;
+ }
+
+ ////////////////////////////////////////////////////////////////
+ case uhd::device::SEND_MODE_FULL_BUFF:{
+ ////////////////////////////////////////////////////////////////
+ //calculate constants for fragmentation
+ const size_t num_fragments = (total_num_samps+max_samples_per_packet-1)/max_samples_per_packet;
+ static const size_t first_fragment_index = 0;
+ const size_t final_fragment_index = num_fragments-1;
+
+ //make a rw copy of the metadata to re-flag below
+ uhd::tx_metadata_t md(metadata);
+
+ //loop through the following fragment indexes
+ for (size_t n = first_fragment_index; n <= final_fragment_index; n++){
+
+ //calculate new flags for the fragments
+ md.has_time_spec = metadata.has_time_spec and (n == first_fragment_index);
+ md.start_of_burst = metadata.start_of_burst and (n == first_fragment_index);
+ md.end_of_burst = metadata.end_of_burst and (n == final_fragment_index);
+
+ //send the fragment with the helper function
+ _send1(
+ state,
+ boost::asio::buffer_cast<const boost::uint8_t *>(buff) + (n*max_samples_per_packet*io_type.size),
+ (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet,
+ md,
+ io_type, otw_type,
+ tick_rate,
+ vrt_packer,
+ get_send_buff,
+ vrt_header_offset_words32,
+ send_cb
+ );
+ }
+ return total_num_samps;
+ }
+
+ default: throw std::runtime_error("unknown send mode");
+ }//switch(send_mode)
+ }
+
+} //namespace vrt_packet_handler
+
+#endif /* INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP */
diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp
new file mode 100644
index 000000000..27f41329b
--- /dev/null
+++ b/host/lib/transport/zero_copy.cpp
@@ -0,0 +1,139 @@
+//
+// 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/zero_copy.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+
+using namespace uhd::transport;
+
+/***********************************************************************
+ * The pure-virtual deconstructor needs an implementation to be happy
+ **********************************************************************/
+managed_recv_buffer::~managed_recv_buffer(void){
+ /* NOP */
+}
+
+/***********************************************************************
+ * Phony zero-copy recv interface implementation
+ **********************************************************************/
+
+//! phony zero-copy recv buffer implementation
+class managed_recv_buffer_impl : public managed_recv_buffer{
+public:
+ managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){
+ /* NOP */
+ }
+
+ ~managed_recv_buffer_impl(void){
+ delete [] this->cast<const boost::uint8_t *>();
+ }
+
+private:
+ const boost::asio::const_buffer &get(void) const{
+ return _buff;
+ }
+
+ const boost::asio::const_buffer _buff;
+};
+
+//! phony zero-copy recv interface implementation
+struct phony_zero_copy_recv_if::impl{
+ size_t max_buff_size;
+};
+
+phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+ _impl->max_buff_size = max_buff_size;
+}
+
+phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){
+ /* NOP */
+}
+
+managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){
+ //allocate memory
+ boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size];
+
+ //call recv() with timeout option
+ size_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size));
+
+ //create a new managed buffer to house the data
+ return managed_recv_buffer::sptr(
+ new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes))
+ );
+}
+
+/***********************************************************************
+ * Phony zero-copy send interface implementation
+ **********************************************************************/
+
+//! phony zero-copy send buffer implementation
+class managed_send_buffer_impl : public managed_send_buffer{
+public:
+ typedef boost::function<size_t(const boost::asio::const_buffer &)> send_fcn_t;
+
+ managed_send_buffer_impl(
+ const boost::asio::mutable_buffer &buff,
+ const send_fcn_t &send_fcn
+ ):
+ _buff(buff),
+ _send_fcn(send_fcn)
+ {
+ /* NOP */
+ }
+
+ ~managed_send_buffer_impl(void){
+ /* NOP */
+ }
+
+ void commit(size_t num_bytes){
+ _send_fcn(boost::asio::buffer(_buff, num_bytes));
+ }
+
+private:
+ const boost::asio::mutable_buffer &get(void) const{
+ return _buff;
+ }
+
+ const boost::asio::mutable_buffer _buff;
+ const send_fcn_t _send_fcn;
+};
+
+//! phony zero-copy send interface implementation
+struct phony_zero_copy_send_if::impl{
+ boost::uint8_t *send_mem;
+ managed_send_buffer::sptr send_buff;
+};
+
+phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){
+ _impl = UHD_PIMPL_MAKE(impl, ());
+ _impl->send_mem = new boost::uint8_t[max_buff_size];
+ _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl(
+ boost::asio::buffer(_impl->send_mem, max_buff_size),
+ boost::bind(&phony_zero_copy_send_if::send, this, _1)
+ ));
+}
+
+phony_zero_copy_send_if::~phony_zero_copy_send_if(void){
+ delete [] _impl->send_mem;
+}
+
+managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){
+ return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these
+}