// // 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 . // #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" #include #include #include //htonl and ntohl #include using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; namespace asio = boost::asio; /*********************************************************************** * Helper Functions **********************************************************************/ void usrp2_impl::io_init(void){ //setup otw type _otw_type.width = 16; _otw_type.shift = 0; _otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; //initially empty copy buffer _rx_copy_buff = asio::buffer("", 0); //init the expected rx seq number _rx_stream_id_to_packet_seq[0] = 0; //send a small data packet so the usrp2 knows the udp source port managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); boost::uint32_t data = htonl(USRP2_INVALID_VRT_HEADER); memcpy(send_buff->cast(), &data, sizeof(data)); send_buff->done(sizeof(data)); //setup RX DSP regs _iface->poke32(FR_RX_CTRL_NSAMPS_PER_PKT, _max_rx_samples_per_packet); _iface->poke32(FR_RX_CTRL_NCHANNELS, 1); _iface->poke32(FR_RX_CTRL_CLEAR_OVERRUN, 1); //reset _iface->poke32(FR_RX_CTRL_VRT_HEADER, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer | (0x3 << 22) //integer time other | (0x1 << 20) //fractional time sample count ); _iface->poke32(FR_RX_CTRL_VRT_STREAM_ID, 0); _iface->poke32(FR_RX_CTRL_VRT_TRAILER, 0); } /*********************************************************************** * Receive Raw Data **********************************************************************/ void usrp2_impl::recv_raw(rx_metadata_t &metadata){ //do a receive _rx_smart_buff = _data_transport->get_recv_buff(); //unpack the vrt header size_t num_packet_words32 = _rx_smart_buff->size()/sizeof(boost::uint32_t); if (num_packet_words32 == 0){ _rx_copy_buff = boost::asio::buffer("", 0); return; //must exit here after setting the buffer } const boost::uint32_t *vrt_hdr = _rx_smart_buff->cast(); size_t num_header_words32_out, num_payload_words32_out, packet_count_out; try{ vrt::unpack( metadata, //output vrt_hdr, //input num_header_words32_out, //output num_payload_words32_out, //output num_packet_words32, //input packet_count_out, //output get_master_clock_freq() ); }catch(const std::exception &e){ std::cerr << "bad vrt header: " << e.what() << std::endl; _rx_copy_buff = boost::asio::buffer("", 0); return; //must exit here after setting the buffer } //handle the packet count / sequence number size_t expected_packet_count = _rx_stream_id_to_packet_seq[metadata.stream_id]; if (packet_count_out != expected_packet_count){ std::cerr << "S" << (packet_count_out - expected_packet_count)%16; } _rx_stream_id_to_packet_seq[metadata.stream_id] = (packet_count_out+1)%16; //setup the rx buffer to point to the data _rx_copy_buff = asio::buffer( vrt_hdr + num_header_words32_out, num_payload_words32_out*sizeof(boost::uint32_t) ); } /*********************************************************************** * Send Data **********************************************************************/ size_t usrp2_impl::send( const asio::const_buffer &buff, const tx_metadata_t &metadata_, const io_type_t &io_type ){ tx_metadata_t metadata = metadata_; //rw copy to change later transport::managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); boost::uint32_t *tx_mem = send_buff->cast(); size_t num_samps = std::min(std::min( asio::buffer_size(buff)/io_type.size, size_t(_max_tx_samples_per_packet)), send_buff->size()/io_type.size ); //kill the end of burst flag if this is a fragment if (asio::buffer_size(buff)/io_type.size < num_samps) metadata.end_of_burst = false; size_t num_header_words32, num_packet_words32; size_t packet_count = _tx_stream_id_to_packet_seq[metadata.stream_id]++; //pack metadata into a vrt header vrt::pack( metadata, //input tx_mem, //output num_header_words32, //output num_samps, //input num_packet_words32, //output packet_count, //input get_master_clock_freq() ); boost::uint32_t *items = tx_mem + num_header_words32; //offset for data //copy-convert the samples into the send buffer convert_io_type_to_otw_type( asio::buffer_cast(buff), io_type, (void*)items, _otw_type, num_samps ); //send and return number of samples send_buff->done(num_packet_words32*sizeof(boost::uint32_t)); return num_samps; } /*********************************************************************** * Receive Data **********************************************************************/ size_t usrp2_impl::recv( const asio::mutable_buffer &buff, rx_metadata_t &metadata, const io_type_t &io_type ){ //perform a receive if no rx data is waiting to be copied if (asio::buffer_size(_rx_copy_buff) == 0){ _fragment_offset_in_samps = 0; recv_raw(metadata); } //otherwise flag the metadata to show that is is a fragment else{ metadata = rx_metadata_t(); } //extract the number of samples available to copy //and a pointer into the usrp2 received items memory size_t bytes_to_copy = asio::buffer_size(_rx_copy_buff); if (bytes_to_copy == 0) return 0; //nothing to receive size_t num_samps = std::min( asio::buffer_size(buff)/io_type.size, bytes_to_copy/sizeof(boost::uint32_t) ); const boost::uint32_t *items = asio::buffer_cast(_rx_copy_buff); //setup the fragment flags and offset metadata.more_fragments = asio::buffer_size(buff)/io_type.size < num_samps; metadata.fragment_offset = _fragment_offset_in_samps; _fragment_offset_in_samps += num_samps; //set for next time //copy-convert the samples from the recv buffer convert_otw_type_to_io_type( (const void*)items, _otw_type, asio::buffer_cast(buff), io_type, num_samps ); //update the rx copy buffer to reflect the bytes copied _rx_copy_buff = asio::buffer( items + num_samps, bytes_to_copy - num_samps*sizeof(boost::uint32_t) ); return num_samps; }