aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/usrp1/io_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/usrp1/io_impl.cpp')
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp378
1 files changed, 378 insertions, 0 deletions
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
new file mode 100644
index 000000000..8fb639c4a
--- /dev/null
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -0,0 +1,378 @@
+//
+// Copyright 2010-2011 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 "../../transport/vrt_packet_handler.hpp"
+#include "usrp_commands.h"
+#include "usrp1_impl.hpp"
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+static const size_t alignment_padding = 512;
+
+/***********************************************************************
+ * Helper struct to associate an offset with a buffer
+ **********************************************************************/
+struct offset_send_buffer{
+ offset_send_buffer(void){
+ /* NOP */
+ }
+
+ offset_send_buffer(managed_send_buffer::sptr buff, size_t offset = 0):
+ buff(buff), offset(offset)
+ {
+ /* NOP */
+ }
+
+ //member variables
+ managed_send_buffer::sptr buff;
+ size_t offset; /* in bytes */
+};
+
+/***********************************************************************
+ * Reusable managed send buffer to handle aligned commits
+ **********************************************************************/
+class offset_managed_send_buffer : public managed_send_buffer{
+public:
+ typedef boost::function<void(offset_send_buffer&, offset_send_buffer&, size_t)> commit_cb_type;
+ offset_managed_send_buffer(const commit_cb_type &commit_cb):
+ _expired(true), _commit_cb(commit_cb)
+ {
+ /* NOP */
+ }
+
+ bool expired(void){return _expired;}
+
+ void commit(size_t size){
+ if (_expired) return;
+ this->_commit_cb(_curr_buff, _next_buff, size);
+ _expired = true;
+ }
+
+ sptr get_new(
+ offset_send_buffer &curr_buff,
+ offset_send_buffer &next_buff
+ ){
+ _expired = false;
+ _curr_buff = curr_buff;
+ _next_buff = next_buff;
+ return sptr(this, &offset_managed_send_buffer::fake_deleter);
+ }
+
+private:
+ static void fake_deleter(void *){
+ //dont do anything and assume the bastard committed it
+ //static_cast<offset_managed_send_buffer *>(obj)->commit(0);
+ }
+
+ void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;}
+ size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;}
+
+ bool _expired;
+ offset_send_buffer _curr_buff, _next_buff;
+ commit_cb_type _commit_cb;
+};
+
+/***********************************************************************
+ * IO Implementation Details
+ **********************************************************************/
+struct usrp1_impl::io_impl{
+ io_impl(zero_copy_if::sptr data_transport):
+ data_transport(data_transport),
+ get_recv_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_recv_buffs, this, _1)),
+ get_send_buffs_fcn(boost::bind(&usrp1_impl::io_impl::get_send_buffs, this, _1)),
+ underflow_poll_samp_count(0),
+ overflow_poll_samp_count(0),
+ curr_buff(offset_send_buffer(data_transport->get_send_buff())),
+ omsb(boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, _1, _2, _3))
+ {
+ /* NOP */
+ }
+
+ ~io_impl(void){
+ UHD_SAFE_CALL(flush_send_buff();)
+ }
+
+ zero_copy_if::sptr data_transport;
+
+ //timeouts set on calls to recv/send (passed into get buffs methods)
+ double recv_timeout, send_timeout;
+
+ //bound callbacks for get buffs (bound once here, not in fast-path)
+ vrt_packet_handler::get_recv_buffs_t get_recv_buffs_fcn;
+ vrt_packet_handler::get_send_buffs_t get_send_buffs_fcn;
+
+ //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;
+
+ //state management for overflow and underflow
+ size_t underflow_poll_samp_count;
+ size_t overflow_poll_samp_count;
+
+ //wrapper around the actual send buffer interface
+ //all of this to ensure only aligned lengths are committed
+ //NOTE: you must commit before getting a new buffer
+ //since the vrt packet handler obeys this, we are ok
+ offset_send_buffer curr_buff;
+ offset_managed_send_buffer omsb;
+ void commit_send_buff(offset_send_buffer&, offset_send_buffer&, size_t);
+ void flush_send_buff(void);
+ bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &);
+ bool get_recv_buffs(vrt_packet_handler::managed_recv_buffs_t &buffs){
+ UHD_ASSERT_THROW(buffs.size() == 1);
+ buffs[0] = data_transport->get_recv_buff(recv_timeout);
+ return buffs[0].get() != NULL;
+ }
+};
+
+/*!
+ * Perform an actual commit on the send buffer:
+ * Copy the remainder of alignment to the next buffer.
+ * Commit the current buffer at multiples of alignment.
+ */
+void usrp1_impl::io_impl::commit_send_buff(
+ offset_send_buffer &curr,
+ offset_send_buffer &next,
+ size_t num_bytes
+){
+ //total number of bytes now in the current buffer
+ size_t bytes_in_curr_buffer = curr.offset + num_bytes;
+
+ //calculate how many to commit and remainder
+ size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding;
+ size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining;
+
+ //copy the remainder into the next buffer
+ std::memcpy(
+ next.buff->cast<char *>() + next.offset,
+ curr.buff->cast<char *>() + num_bytes_to_commit,
+ num_bytes_remaining
+ );
+
+ //update the offset into the next buffer
+ next.offset += num_bytes_remaining;
+
+ //commit the current buffer
+ curr.buff->commit(num_bytes_to_commit);
+
+ //store the next buffer for the next call
+ curr_buff = next;
+}
+
+/*!
+ * Flush the current buffer by padding out to alignment and committing.
+ */
+void usrp1_impl::io_impl::flush_send_buff(void){
+ //calculate the number of bytes to alignment
+ size_t bytes_to_pad = (-1*curr_buff.offset)%alignment_padding;
+
+ //send at least alignment_padding to guarantee zeros are sent
+ if (bytes_to_pad == 0) bytes_to_pad = alignment_padding;
+
+ //get the buffer, clear, and commit (really current buffer)
+ vrt_packet_handler::managed_send_buffs_t buffs(1);
+ if (this->get_send_buffs(buffs)){
+ std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad);
+ buffs[0]->commit(bytes_to_pad);
+ }
+}
+
+/*!
+ * Get a managed send buffer with the alignment padding:
+ * Always grab the next send buffer so we can timeout here.
+ */
+bool usrp1_impl::io_impl::get_send_buffs(
+ vrt_packet_handler::managed_send_buffs_t &buffs
+){
+ UHD_ASSERT_THROW(omsb.expired() and buffs.size() == 1);
+
+ //try to get a new managed buffer with timeout
+ offset_send_buffer next_buff(data_transport->get_send_buff(send_timeout));
+ if (not next_buff.buff.get()) return false; /* propagate timeout here */
+
+ //make a new managed buffer with the offset buffs
+ buffs[0] = omsb.get_new(curr_buff, next_buff);
+
+ return true;
+}
+
+/***********************************************************************
+ * Initialize internals within this file
+ **********************************************************************/
+void usrp1_impl::io_init(void){
+ _rx_otw_type.width = 16;
+ _rx_otw_type.shift = 0;
+ _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN;
+
+ _tx_otw_type.width = 16;
+ _tx_otw_type.shift = 0;
+ _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN;
+
+ _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport));
+
+ _soft_time_ctrl = soft_time_ctrl::make(
+ boost::bind(&usrp1_impl::rx_stream_on_off, this, _1)
+ );
+
+ rx_stream_on_off(false);
+ _io_impl->flush_send_buff();
+}
+
+void usrp1_impl::rx_stream_on_off(bool enb){
+ return _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, enb, 0, 0, 0);
+ //drain any junk in the receive transport after stop streaming command
+ while(not enb and _data_transport->get_recv_buff().get() != NULL){
+ /* NOP */
+ }
+}
+
+/***********************************************************************
+ * Data send + helper functions
+ **********************************************************************/
+static void usrp1_bs_vrt_packer(
+ boost::uint32_t *,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.num_header_words32 = 0;
+ if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32;
+}
+
+size_t usrp1_impl::get_max_send_samps_per_packet(void) const {
+ return (_data_transport->get_send_frame_size() - alignment_padding)
+ / _tx_otw_type.get_sample_size()
+ / _tx_subdev_spec.size()
+ ;
+}
+
+size_t usrp1_impl::send(
+ const send_buffs_type &buffs, size_t num_samps,
+ const tx_metadata_t &metadata, const io_type_t &io_type,
+ send_mode_t send_mode, double timeout
+){
+ if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps;
+
+ _io_impl->send_timeout = timeout;
+ size_t num_samps_sent = 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, _tx_otw_type, //input and output types to convert
+ _clock_ctrl->get_master_clock_freq(), //master clock tick rate
+ &usrp1_bs_vrt_packer,
+ _io_impl->get_send_buffs_fcn,
+ get_max_send_samps_per_packet(),
+ 0, //vrt header offset
+ _tx_subdev_spec.size() //num channels
+ );
+
+ //handle eob flag (commit the buffer, disable the DACs)
+ //check num samps sent to avoid flush on incomplete/timeout
+ if (metadata.end_of_burst and num_samps_sent == num_samps){
+ _io_impl->flush_send_buff();
+ }
+
+ //handle the polling for underflow conditions
+ _io_impl->underflow_poll_samp_count += num_samps_sent;
+ if (_io_impl->underflow_poll_samp_count >= _tx_samps_per_poll_interval){
+ _io_impl->underflow_poll_samp_count = 0; //reset count
+ boost::uint8_t underflow = 0;
+ ssize_t ret = _ctrl_transport->usrp_control_read(
+ VRQ_GET_STATUS, 0, GS_TX_UNDERRUN,
+ &underflow, sizeof(underflow)
+ );
+ if (ret < 0) std::cerr << "USRP: underflow check failed" << std::endl;
+ else if (underflow) std::cerr << "U" << std::flush;
+ }
+
+ return num_samps_sent;
+}
+
+/***********************************************************************
+ * Data recv + helper functions
+ **********************************************************************/
+static void usrp1_bs_vrt_unpacker(
+ const boost::uint32_t *,
+ vrt::if_packet_info_t &if_packet_info
+){
+ if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA;
+ if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32;
+ if_packet_info.num_header_words32 = 0;
+ if_packet_info.packet_count = 0;
+ if_packet_info.sob = false;
+ if_packet_info.eob = false;
+ if_packet_info.has_sid = false;
+ if_packet_info.has_cid = false;
+ if_packet_info.has_tsi = false;
+ if_packet_info.has_tsf = false;
+ if_packet_info.has_tlr = false;
+}
+
+size_t usrp1_impl::get_max_recv_samps_per_packet(void) const {
+ return _data_transport->get_recv_frame_size()
+ / _rx_otw_type.get_sample_size()
+ / _rx_subdev_spec.size()
+ ;
+}
+
+size_t usrp1_impl::recv(
+ const recv_buffs_type &buffs, size_t num_samps,
+ rx_metadata_t &metadata, const io_type_t &io_type,
+ recv_mode_t recv_mode, double timeout
+){
+ _io_impl->recv_timeout = timeout;
+ size_t num_samps_recvd = 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, _rx_otw_type, //input and output types to convert
+ _clock_ctrl->get_master_clock_freq(), //master clock tick rate
+ &usrp1_bs_vrt_unpacker,
+ _io_impl->get_recv_buffs_fcn,
+ &vrt_packet_handler::handle_overflow_nop,
+ 0, //vrt header offset
+ _rx_subdev_spec.size() //num channels
+ );
+
+ _soft_time_ctrl->recv_post(metadata, num_samps_recvd);
+
+ //handle the polling for overflow conditions
+ _io_impl->overflow_poll_samp_count += num_samps_recvd;
+ if (_io_impl->overflow_poll_samp_count >= _rx_samps_per_poll_interval){
+ _io_impl->overflow_poll_samp_count = 0; //reset count
+ boost::uint8_t overflow = 0;
+ ssize_t ret = _ctrl_transport->usrp_control_read(
+ VRQ_GET_STATUS, 0, GS_RX_OVERRUN,
+ &overflow, sizeof(overflow)
+ );
+ if (ret < 0) std::cerr << "USRP: overflow check failed" << std::endl;
+ else if (overflow) std::cerr << "O" << std::flush;
+ }
+
+ return num_samps_recvd;
+}