diff options
Diffstat (limited to 'host/lib/usrp/usrp_e')
| -rw-r--r-- | host/lib/usrp/usrp_e/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e/dboard_iface.cpp | 5 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e/dsp_impl.cpp | 24 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e/io_impl.cpp | 198 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e/usrp_e_impl.hpp | 13 | ||||
| -rw-r--r-- | host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp | 215 | 
6 files changed, 318 insertions, 138 deletions
| diff --git a/host/lib/usrp/usrp_e/CMakeLists.txt b/host/lib/usrp/usrp_e/CMakeLists.txt index da759d931..bab868f90 100644 --- a/host/lib/usrp/usrp_e/CMakeLists.txt +++ b/host/lib/usrp/usrp_e/CMakeLists.txt @@ -54,6 +54,7 @@ IF(ENABLE_USRP_E)          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_impl.hpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_iface.hpp +        ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp          ${CMAKE_SOURCE_DIR}/lib/usrp/usrp_e/usrp_e_regs.hpp      )  ELSE(ENABLE_USRP_E) diff --git a/host/lib/usrp/usrp_e/dboard_iface.cpp b/host/lib/usrp/usrp_e/dboard_iface.cpp index 1bd177f60..6898df8df 100644 --- a/host/lib/usrp/usrp_e/dboard_iface.cpp +++ b/host/lib/usrp/usrp_e/dboard_iface.cpp @@ -91,6 +91,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t);  private:      usrp_e_iface::sptr _iface; @@ -140,6 +141,10 @@ void usrp_e_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      }  } +double usrp_e_dboard_iface::get_codec_rate(unit_t){ +    return _clock->get_fpga_clock_rate(); +} +  /***********************************************************************   * GPIO   **********************************************************************/ diff --git a/host/lib/usrp/usrp_e/dsp_impl.cpp b/host/lib/usrp/usrp_e/dsp_impl.cpp index c133eafae..9312bb603 100644 --- a/host/lib/usrp/usrp_e/dsp_impl.cpp +++ b/host/lib/usrp/usrp_e/dsp_impl.cpp @@ -44,7 +44,9 @@ void usrp_e_impl::rx_ddc_init(void){  /***********************************************************************   * RX DDC Get   **********************************************************************/ -void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){ +void usrp_e_impl::rx_ddc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp-e ddc0"); @@ -58,6 +60,10 @@ void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){          val = _ddc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = MASTER_CLOCK_RATE;          return; @@ -73,7 +79,9 @@ void usrp_e_impl::rx_ddc_get(const wax::obj &key, wax::obj &val){  /***********************************************************************   * RX DDC Set   **********************************************************************/ -void usrp_e_impl::rx_ddc_set(const wax::obj &key, const wax::obj &val){ +void usrp_e_impl::rx_ddc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ @@ -119,7 +127,9 @@ void usrp_e_impl::tx_duc_init(void){  /***********************************************************************   * TX DUC Get   **********************************************************************/ -void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){ +void usrp_e_impl::tx_duc_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_NAME:          val = std::string("usrp-e duc0"); @@ -133,6 +143,10 @@ void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){          val = _duc_freq;          return; +    case DSP_PROP_FREQ_SHIFT_NAMES: +        val = prop_names_t(1, ""); +        return; +      case DSP_PROP_CODEC_RATE:          val = MASTER_CLOCK_RATE;          return; @@ -148,7 +162,9 @@ void usrp_e_impl::tx_duc_get(const wax::obj &key, wax::obj &val){  /***********************************************************************   * TX DUC Set   **********************************************************************/ -void usrp_e_impl::tx_duc_set(const wax::obj &key, const wax::obj &val){ +void usrp_e_impl::tx_duc_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); +      switch(key.as<dsp_prop_t>()){      case DSP_PROP_FREQ_SHIFT:{ diff --git a/host/lib/usrp/usrp_e/io_impl.cpp b/host/lib/usrp/usrp_e/io_impl.cpp index 31ea4c6c0..d89a7db07 100644 --- a/host/lib/usrp/usrp_e/io_impl.cpp +++ b/host/lib/usrp/usrp_e/io_impl.cpp @@ -22,8 +22,6 @@  #include <uhd/transport/bounded_buffer.hpp>  #include "../../transport/vrt_packet_handler.hpp"  #include <boost/bind.hpp> -#include <fcntl.h> //read, write -#include <poll.h>  #include <boost/format.hpp>  #include <boost/thread.hpp>  #include <iostream> @@ -32,88 +30,14 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport; +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface); +  /***********************************************************************   * Constants   **********************************************************************/ -static const size_t MAX_BUFF_SIZE = 2048; -static const bool usrp_e_io_impl_verbose = false;  static const size_t tx_async_report_sid = 1;  static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET; - -/*********************************************************************** - * Data Transport (phony zero-copy with read/write) - **********************************************************************/ -class data_transport: -    public transport::phony_zero_copy_recv_if, -    public transport::phony_zero_copy_send_if -{ -public: -    data_transport(int fd): -        transport::phony_zero_copy_recv_if(MAX_BUFF_SIZE), -        transport::phony_zero_copy_send_if(MAX_BUFF_SIZE), -        _fd(fd) -    { -        /* NOP */ -    } - -    size_t get_num_recv_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -    size_t get_num_send_frames(void) const{ -        return 100; //FIXME no idea! -        //this will be an important number when packet ring gets implemented -    } - -private: -    int _fd; -    ssize_t send(const boost::asio::const_buffer &buff){ -        return write(_fd, -            boost::asio::buffer_cast<const void *>(buff), -            boost::asio::buffer_size(buff) -        ); -    } -    ssize_t recv(const boost::asio::mutable_buffer &buff){ -        //std::cout << boost::format( -        //    "calling read on fd %d, buff size is %d" -        //) % _fd % boost::asio::buffer_size(buff) << std::endl; - -        //setup and call poll on the file descriptor -        //return 0 and do not read when poll times out -        pollfd pfd; -        pfd.fd = _fd; -        pfd.events = POLLIN; -        ssize_t poll_ret = poll(&pfd, 1, 100/*ms*/); -        if (poll_ret <= 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): poll() returned non-positive value: %d\n" -                "    -> return 0 for timeout" -            ) % poll_ret << std::endl; -            return 0; //timeout -        } - -        //perform the blocking read(...) -        ssize_t read_ret = read(_fd, -            boost::asio::buffer_cast<void *>(buff), -            boost::asio::buffer_size(buff) -        ); -        if (read_ret < 0){ -            if (usrp_e_io_impl_verbose) std::cerr << boost::format( -                "usrp-e io impl recv(): read() returned small value: %d\n" -                "    -> return -1 for error" -            ) % read_ret << std::endl; -            return -1; -        } - -        //std::cout << "len " << int(read_ret) << std::endl; -        //for (size_t i = 0; i < 9; i++){ -        //    std::cout << boost::format("    0x%08x") % boost::asio::buffer_cast<boost::uint32_t *>(buff)[i] << std::endl; -        //} - -        return read_ret; -    } -}; +static const bool recv_debug = false;  /***********************************************************************   * io impl details (internal to this file) @@ -126,11 +50,11 @@ struct usrp_e_impl::io_impl{      //state management for the vrt packet handler code      vrt_packet_handler::recv_state packet_handler_recv_state;      vrt_packet_handler::send_state packet_handler_send_state; -    data_transport transport; +    zero_copy_if::sptr data_xport;      bool continuous_streaming; -    io_impl(int fd): -        transport(fd), -        recv_pirate_booty(recv_booty_type::make(transport.get_num_recv_frames())), +    io_impl(usrp_e_iface::sptr iface): +        data_xport(usrp_e_make_mmap_zero_copy(iface)), +        recv_pirate_booty(recv_booty_type::make(data_xport->get_num_recv_frames())),          async_msg_fifo(bounded_buffer<async_metadata_t>::make(100/*messages deep*/))      {          /* NOP */ @@ -142,10 +66,10 @@ struct usrp_e_impl::io_impl{          recv_pirate_crew.join_all();      } -    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, size_t timeout_ms){ +    bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs, double timeout){          UHD_ASSERT_THROW(buffs.size() == 1);          boost::this_thread::disable_interruption di; //disable because the wait can throw -        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), boost::posix_time::milliseconds(timeout_ms)); +        return recv_pirate_booty->pop_with_timed_wait(buffs.front(), timeout);      }      //a pirate's life is the life for me! @@ -167,12 +91,19 @@ void usrp_e_impl::io_impl::recv_pirate_loop(  ){      set_thread_priority_safe();      recv_pirate_crew_raiding = true; -    //size_t next_packet_seq = 0;      while(recv_pirate_crew_raiding){ -        managed_recv_buffer::sptr buff = this->transport.get_recv_buff(); +        managed_recv_buffer::sptr buff = this->data_xport->get_recv_buff();          if (not buff.get()) continue; //ignore timeout/error buffers +        if (recv_debug){ +            std::cout << "len " << buff->size() << std::endl; +            for (size_t i = 0; i < 9; i++){ +                std::cout << boost::format("    0x%08x") % buff->cast<const boost::uint32_t *>()[i] << std::endl; +            } +            std::cout << std::endl << std::endl; +        } +          try{              //extract the vrt header packet info              vrt::if_packet_info_t if_packet_info; @@ -198,12 +129,12 @@ void usrp_e_impl::io_impl::recv_pirate_loop(                  continue;              } +            //same number of frames as the data transport -> always immediate +            recv_pirate_booty->push_with_wait(buff); +          }catch(const std::exception &e){              std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl;          } - -        //usrp-e back-pressures on receive: push with wait -        recv_pirate_booty->push_with_wait(buff);      }  } @@ -211,6 +142,15 @@ void usrp_e_impl::io_impl::recv_pirate_loop(   * Helper Functions   **********************************************************************/  void usrp_e_impl::io_init(void){ +    //setup otw types +    _send_otw_type.width = 16; +    _send_otw_type.shift = 0; +    _send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _recv_otw_type.width = 16; +    _recv_otw_type.shift = 0; +    _recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; +      //setup rx data path      _iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet());      _iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1); @@ -228,7 +168,7 @@ void usrp_e_impl::io_init(void){      _iface->poke32(UE_REG_CTRL_TX_REPORT_SID, tx_async_report_sid);      _iface->poke32(UE_REG_CTRL_TX_POLICY, UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET); -    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface->get_file_descriptor())); +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface));      //spawn a pirate, yarrr!      _io_impl->recv_pirate_crew.create_thread(boost::bind( @@ -257,34 +197,38 @@ void usrp_e_impl::handle_overrun(size_t){   * Data Send   **********************************************************************/  bool get_send_buffs( -    data_transport *trans, +    zero_copy_if::sptr trans, double timeout,      vrt_packet_handler::managed_send_buffs_t &buffs  ){      UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_send_buff(); -    return buffs[0].get(); +    buffs[0] = trans->get_send_buff(timeout); +    return buffs[0].get() != NULL; +} + +#if 0 +size_t usrp_e_impl::get_max_send_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_send_frame_size() - hdr_size; +    return bpp/_send_otw_type.get_sample_size();  } +#endif  size_t usrp_e_impl::send( -    const std::vector<const void *> &buffs, -    size_t num_samps, -    const tx_metadata_t &metadata, -    const io_type_t &io_type, -    send_mode_t send_mode +    const std::vector<const void *> &buffs, size_t num_samps, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout  ){ -    otw_type_t send_otw_type; -    send_otw_type.width = 16; -    send_otw_type.shift = 0; -    send_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::send(          _io_impl->packet_handler_send_state,       //last state of the send handler          buffs, num_samps,                          //buffer to fill          metadata, send_mode,                       //samples metadata -        io_type, send_otw_type,                    //input and output types to convert +        io_type, _send_otw_type,                   //input and output types to convert          MASTER_CLOCK_RATE,                         //master clock tick rate          uhd::transport::vrt::if_hdr_pack_le, -        boost::bind(&get_send_buffs, &_io_impl->transport, _1), +        boost::bind(&get_send_buffs, _io_impl->data_xport, timeout, _1),          get_max_send_samps_per_packet()      );  } @@ -292,36 +236,31 @@ size_t usrp_e_impl::send(  /***********************************************************************   * Data Recv   **********************************************************************/ -bool get_recv_buffs( -    data_transport *trans, -    vrt_packet_handler::managed_recv_buffs_t &buffs -){ -    UHD_ASSERT_THROW(buffs.size() == 1); -    buffs[0] = trans->get_recv_buff(); -    return buffs[0].get(); +#if 0 +size_t usrp_e_impl::get_max_recv_samps_per_packet(void) const{ +    static const size_t hdr_size = 0 +        + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +        + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +        - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +    ; +    size_t bpp = _io_impl->data_xport->get_recv_frame_size() - hdr_size; +    return bpp/_recv_otw_type.get_sample_size();  } +#endif  size_t usrp_e_impl::recv( -    const std::vector<void *> &buffs, -    size_t num_samps, -    rx_metadata_t &metadata, -    const io_type_t &io_type, -    recv_mode_t recv_mode, -    size_t timeout_ms +    const std::vector<void *> &buffs, size_t num_samps, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout  ){ -    otw_type_t recv_otw_type; -    recv_otw_type.width = 16; -    recv_otw_type.shift = 0; -    recv_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; -      return vrt_packet_handler::recv(          _io_impl->packet_handler_recv_state,       //last state of the recv handler          buffs, num_samps,                          //buffer to fill          metadata, recv_mode,                       //samples metadata -        io_type, recv_otw_type,                    //input and output types to convert +        io_type, _recv_otw_type,                   //input and output types to convert          MASTER_CLOCK_RATE,                         //master clock tick rate          uhd::transport::vrt::if_hdr_unpack_le, -        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout_ms), +        boost::bind(&usrp_e_impl::io_impl::get_recv_buffs, _io_impl.get(), _1, timeout),          boost::bind(&usrp_e_impl::handle_overrun, this, _1)      );  } @@ -330,11 +269,8 @@ size_t usrp_e_impl::recv(   * Async Recv   **********************************************************************/  bool usrp_e_impl::recv_async_msg( -    async_metadata_t &async_metadata, -    size_t timeout_ms +    async_metadata_t &async_metadata, double timeout  ){      boost::this_thread::disable_interruption di; //disable because the wait can throw -    return _io_impl->async_msg_fifo->pop_with_timed_wait( -        async_metadata, boost::posix_time::milliseconds(timeout_ms) -    ); +    return _io_impl->async_msg_fifo->pop_with_timed_wait(async_metadata, timeout);  } diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.hpp b/host/lib/usrp/usrp_e/usrp_e_impl.hpp index 2457e27cc..9799cd645 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.hpp @@ -22,6 +22,7 @@  #include <uhd/utils/pimpl.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/types/otw_type.hpp>  #include <uhd/types/clock_config.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/usrp/dboard_manager.hpp> @@ -82,11 +83,16 @@ public:      ~usrp_e_impl(void);      //the io interface -    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t); -    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, size_t); -    bool recv_async_msg(uhd::async_metadata_t &, size_t); +    size_t send(const std::vector<const void *> &, size_t, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t, double); +    size_t recv(const std::vector<void *> &, size_t, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t, double); +    bool recv_async_msg(uhd::async_metadata_t &, double); +#if 0 +    size_t get_max_send_samps_per_packet(void) const; +    size_t get_max_recv_samps_per_packet(void) const; +#else      size_t get_max_send_samps_per_packet(void) const{return 503;}      size_t get_max_recv_samps_per_packet(void) const{return 503;} +#endif  private:      //interface to ioctls and file descriptor @@ -97,6 +103,7 @@ private:      //handle io stuff      UHD_PIMPL_DECL(io_impl) _io_impl; +    uhd::otw_type_t _send_otw_type, _recv_otw_type;      void io_init(void);      void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd);      void handle_overrun(size_t); diff --git a/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp new file mode 100644 index 000000000..274bb043e --- /dev/null +++ b/host/lib/usrp/usrp_e/usrp_e_mmap_zero_copy.cpp @@ -0,0 +1,215 @@ +// +// 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 "usrp_e_iface.hpp" +#include <uhd/transport/zero_copy.hpp> +#include <uhd/utils/assert.hpp> +#include <linux/usrp_e.h> +#include <sys/mman.h> //mmap +#include <unistd.h> //getpagesize +#include <poll.h> //poll +#include <boost/bind.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::transport; + +static const bool fp_verbose = false; //fast-path verbose +static const bool sp_verbose = false; //slow-path verbose +static const size_t poll_breakout = 10; //how many poll timeouts constitute a full timeout + +/*********************************************************************** + * The zero copy interface implementation + **********************************************************************/ +class usrp_e_mmap_zero_copy_impl : public zero_copy_if, public boost::enable_shared_from_this<usrp_e_mmap_zero_copy_impl> { +public: +    usrp_e_mmap_zero_copy_impl(usrp_e_iface::sptr iface): +        _fd(iface->get_file_descriptor()), _recv_index(0), _send_index(0) +    { +        //get system sizes +        iface->ioctl(USRP_E_GET_RB_INFO, &_rb_size); +        size_t page_size = getpagesize(); +        _frame_size = page_size/2; + +        //calculate the memory size +        _map_size = +            (_rb_size.num_pages_rx_flags + _rb_size.num_pages_tx_flags) * page_size + +            (_rb_size.num_rx_frames + _rb_size.num_tx_frames) * _frame_size; + +        //print sizes summary +        if (sp_verbose){ +            std::cout << "page_size:          " << page_size                   << std::endl; +            std::cout << "frame_size:         " << _frame_size                 << std::endl; +            std::cout << "num_pages_rx_flags: " << _rb_size.num_pages_rx_flags << std::endl; +            std::cout << "num_rx_frames:      " << _rb_size.num_rx_frames      << std::endl; +            std::cout << "num_pages_tx_flags: " << _rb_size.num_pages_tx_flags << std::endl; +            std::cout << "num_tx_frames:      " << _rb_size.num_tx_frames      << std::endl; +            std::cout << "map_size:           " << _map_size                   << std::endl; +        } + +        //call mmap to get the memory +        _mapped_mem = ::mmap( +            NULL, _map_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0 +        ); +        UHD_ASSERT_THROW(_mapped_mem != MAP_FAILED); + +        //calculate the memory offsets for info and buffers +        size_t recv_info_off = 0; +        size_t recv_buff_off = recv_info_off + (_rb_size.num_pages_rx_flags * page_size); +        size_t send_info_off = recv_buff_off + (_rb_size.num_rx_frames * _frame_size); +        size_t send_buff_off = send_info_off + (_rb_size.num_pages_tx_flags * page_size); + +        //print offset summary +        if (sp_verbose){ +            std::cout << "recv_info_off: " << recv_info_off << std::endl; +            std::cout << "recv_buff_off: " << recv_buff_off << std::endl; +            std::cout << "send_info_off: " << send_info_off << std::endl; +            std::cout << "send_buff_off: " << send_buff_off << std::endl; +        } + +        //set the internal pointers for info and buffers +        typedef ring_buffer_info (*rbi_pta)[]; +        char *rb_ptr = reinterpret_cast<char *>(_mapped_mem); +        _recv_info = reinterpret_cast<rbi_pta>(rb_ptr + recv_info_off); +        _recv_buff = rb_ptr + recv_buff_off; +        _send_info = reinterpret_cast<rbi_pta>(rb_ptr + send_info_off); +        _send_buff = rb_ptr + send_buff_off; +    } + +    ~usrp_e_mmap_zero_copy_impl(void){ +        if (sp_verbose) std::cout << "cleanup: munmap" << std::endl; +        ::munmap(_mapped_mem, _map_size); +    } + +    managed_recv_buffer::sptr get_recv_buff(double timeout){ +        if (fp_verbose) std::cout << "get_recv_buff: " << _recv_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_recv_info) + _recv_index; +        void *mem = _recv_buff + _frame_size*_recv_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_USER)){ +            for (size_t i = 0; i < poll_breakout; i++){ +                pollfd pfd; +                pfd.fd = _fd; +                pfd.events = POLLIN; +                ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3/poll_breakout)); +                if (fp_verbose) std::cout << "  POLLIN: " << poll_ret << std::endl; +                if (poll_ret > 0) goto found_user_frame; //good poll, continue on +            } +            return managed_recv_buffer::sptr(); //timed-out for real +        } found_user_frame: + +        //the process has claimed the frame +        info->flags = RB_USER_PROCESS; + +        //increment the index for the next call +        if (++_recv_index == size_t(_rb_size.num_rx_frames)) _recv_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_recv_buff: " << info->len << std::endl; +        return managed_recv_buffer::make_safe( +            boost::asio::const_buffer(mem, info->len), +            boost::bind(&usrp_e_mmap_zero_copy_impl::release, shared_from_this(), info) +        ); +    } + +    size_t get_num_recv_frames(void) const{ +        return _rb_size.num_rx_frames; +    } + +    size_t get_recv_frame_size(void) const{ +        return _frame_size; +    } + +    managed_send_buffer::sptr get_send_buff(double timeout){ +        if (fp_verbose) std::cout << "get_send_buff: " << _send_index << std::endl; + +        //grab pointers to the info and buffer +        ring_buffer_info *info = (*_send_info) + _send_index; +        void *mem = _send_buff + _frame_size*_send_index; + +        //poll/wait for a ready frame +        if (not (info->flags & RB_KERNEL)){ +            pollfd pfd; +            pfd.fd = _fd; +            pfd.events = POLLOUT; +            ssize_t poll_ret = ::poll(&pfd, 1, size_t(timeout*1e3)); +            if (fp_verbose) std::cout << "  POLLOUT: " << poll_ret << std::endl; +            if (poll_ret <= 0) return managed_send_buffer::sptr(); +        } + +        //increment the index for the next call +        if (++_send_index == size_t(_rb_size.num_tx_frames)) _send_index = 0; + +        //return the managed buffer for this frame +        if (fp_verbose) std::cout << "  make_send_buff: " << _frame_size << std::endl; +        return managed_send_buffer::make_safe( +            boost::asio::mutable_buffer(mem, _frame_size), +            boost::bind(&usrp_e_mmap_zero_copy_impl::commit, shared_from_this(), info, _1) +        ); +    } + +    size_t get_num_send_frames(void) const{ +        return _rb_size.num_tx_frames; +    } + +    size_t get_send_frame_size(void) const{ +        return _frame_size; +    } + +private: + +    void release(ring_buffer_info *info){ +        if (fp_verbose) std::cout << "recv buff: release" << std::endl; +        info->flags = RB_KERNEL; +    } + +    void commit(ring_buffer_info *info, size_t len){ +        if (fp_verbose) std::cout << "send buff: commit " << len << std::endl; +        info->len = len; +        info->flags = RB_USER; +        if (::write(_fd, NULL, 0) < 0){ +            std::cerr << UHD_THROW_SITE_INFO("write error") << std::endl; +        } +    } + +    int _fd; + +    //the mapped memory itself +    void *_mapped_mem; + +    //mapped memory sizes +    usrp_e_ring_buffer_size_t _rb_size; +    size_t _frame_size, _map_size; + +    //pointers to sections in the mapped memory +    ring_buffer_info (*_recv_info)[], (*_send_info)[]; +    char *_recv_buff, *_send_buff; + +    //indexes into sub-sections of mapped memory +    size_t _recv_index, _send_index; +}; + +/*********************************************************************** + * The zero copy interface make function + **********************************************************************/ +zero_copy_if::sptr usrp_e_make_mmap_zero_copy(usrp_e_iface::sptr iface){ +    return zero_copy_if::sptr(new usrp_e_mmap_zero_copy_impl(iface)); +} | 
