diff options
Diffstat (limited to 'host/lib/usrp/usrp1/io_impl.cpp')
-rw-r--r-- | host/lib/usrp/usrp1/io_impl.cpp | 378 |
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; +} |