root / host / lib / usrp / usrp2 / usrp2_impl.cpp @ fa7d4a2a
History | View | Annotate | Download (11.4 KB)
| 1 |
//
|
|---|---|
| 2 |
// Copyright 2010 Ettus Research LLC
|
| 3 |
//
|
| 4 |
// This program is free software: you can redistribute it and/or modify
|
| 5 |
// it under the terms of the GNU General Public License as published by
|
| 6 |
// the Free Software Foundation, either version 3 of the License, or
|
| 7 |
// (at your option) any later version.
|
| 8 |
//
|
| 9 |
// This program is distributed in the hope that it will be useful,
|
| 10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 12 |
// GNU General Public License for more details.
|
| 13 |
//
|
| 14 |
// You should have received a copy of the GNU General Public License
|
| 15 |
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
| 16 |
//
|
| 17 |
|
| 18 |
#include "usrp2_impl.hpp" |
| 19 |
#include <uhd/transport/if_addrs.hpp> |
| 20 |
#include <uhd/transport/udp_zero_copy.hpp> |
| 21 |
#include <uhd/usrp/device_props.hpp> |
| 22 |
#include <uhd/utils/assert.hpp> |
| 23 |
#include <uhd/utils/static.hpp> |
| 24 |
#include <uhd/utils/warning.hpp> |
| 25 |
#include <uhd/utils/algorithm.hpp> |
| 26 |
#include <boost/assign/list_of.hpp> |
| 27 |
#include <boost/format.hpp> |
| 28 |
#include <boost/foreach.hpp> |
| 29 |
#include <boost/lexical_cast.hpp> |
| 30 |
#include <boost/regex.hpp> |
| 31 |
#include <boost/bind.hpp> |
| 32 |
#include <boost/asio.hpp> //htonl and ntohl |
| 33 |
#include <iostream> |
| 34 |
|
| 35 |
using namespace uhd; |
| 36 |
using namespace uhd::usrp; |
| 37 |
using namespace uhd::transport; |
| 38 |
namespace asio = boost::asio;
|
| 39 |
|
| 40 |
/***********************************************************************
|
| 41 |
* Helper Functions
|
| 42 |
**********************************************************************/
|
| 43 |
template <class T> std::string num2str(T num){ |
| 44 |
return boost::lexical_cast<std::string>(num); |
| 45 |
} |
| 46 |
|
| 47 |
//! separate indexed device addresses into a vector of device addresses
|
| 48 |
device_addrs_t sep_indexed_dev_addrs(const device_addr_t &dev_addr){
|
| 49 |
//------------ support old deprecated way and print warning --------
|
| 50 |
if (dev_addr.has_key("addr")){ |
| 51 |
std::vector<std::string> addrs = std::split_string(dev_addr["addr"]); |
| 52 |
if (addrs.size() > 1){ |
| 53 |
device_addr_t fixed_dev_addr = dev_addr; |
| 54 |
fixed_dev_addr.pop("addr");
|
| 55 |
for (size_t i = 0; i < addrs.size(); i++){ |
| 56 |
fixed_dev_addr[str(boost::format("addr%d") % i)] = addrs[i];
|
| 57 |
} |
| 58 |
uhd::warning::post( |
| 59 |
"addr = <space separated list of ip addresses> is deprecated.\n"
|
| 60 |
"To address a multi-device, use multiple <key><index> = <val>.\n"
|
| 61 |
"See the USRP-NXXX application notes. Two device example:\n"
|
| 62 |
" addr0 = 192.168.10.2\n"
|
| 63 |
" addr1 = 192.168.10.3\n"
|
| 64 |
); |
| 65 |
return sep_indexed_dev_addrs(fixed_dev_addr);
|
| 66 |
} |
| 67 |
} |
| 68 |
//------------------------------------------------------------------
|
| 69 |
device_addrs_t dev_addrs; |
| 70 |
BOOST_FOREACH(const std::string &key, dev_addr.keys()){ |
| 71 |
boost::cmatch matches; |
| 72 |
if (not boost::regex_match(key.c_str(), matches, boost::regex("^(\\D+)(\\d*)$"))){ |
| 73 |
throw std::runtime_error("unknown key format: " + key); |
| 74 |
} |
| 75 |
std::string key_part(matches[1].first, matches[1].second); |
| 76 |
std::string num_part(matches[2].first, matches[2].second); |
| 77 |
size_t num = (num_part.empty())? 0 : boost::lexical_cast<size_t>(num_part);
|
| 78 |
dev_addrs.resize(std::max(num+1, dev_addrs.size()));
|
| 79 |
dev_addrs[num][key_part] = dev_addr[key]; |
| 80 |
} |
| 81 |
return dev_addrs;
|
| 82 |
} |
| 83 |
|
| 84 |
//! combine a vector in device addresses into an indexed device address
|
| 85 |
device_addr_t combine_dev_addr_vector(const device_addrs_t &dev_addrs){
|
| 86 |
device_addr_t dev_addr; |
| 87 |
for (size_t i = 0; i < dev_addrs.size(); i++){ |
| 88 |
BOOST_FOREACH(const std::string &key, dev_addrs[i].keys()){ |
| 89 |
dev_addr[str(boost::format("%s%d") % key % i)] = dev_addrs[i][key];
|
| 90 |
} |
| 91 |
} |
| 92 |
return dev_addr;
|
| 93 |
} |
| 94 |
|
| 95 |
/***********************************************************************
|
| 96 |
* Discovery over the udp transport
|
| 97 |
**********************************************************************/
|
| 98 |
static device_addrs_t usrp2_find(const device_addr_t &hint_){ |
| 99 |
//handle the multi-device discovery
|
| 100 |
device_addrs_t hints = sep_indexed_dev_addrs(hint_); |
| 101 |
if (hints.size() > 1){ |
| 102 |
device_addrs_t found_devices; |
| 103 |
BOOST_FOREACH(const device_addr_t &hint_i, hints){
|
| 104 |
device_addrs_t found_devices_i = usrp2_find(hint_i); |
| 105 |
if (found_devices_i.size() != 1) throw std::runtime_error(str(boost::format( |
| 106 |
"Could not resolve device hint \"%s\" to a single device."
|
| 107 |
) % hint_i.to_string())); |
| 108 |
found_devices.push_back(found_devices_i[0]);
|
| 109 |
} |
| 110 |
return device_addrs_t(1, combine_dev_addr_vector(found_devices)); |
| 111 |
} |
| 112 |
|
| 113 |
//initialize the hint for a single device case
|
| 114 |
UHD_ASSERT_THROW(hints.size() <= 1);
|
| 115 |
hints.resize(1); //in case it was empty |
| 116 |
device_addr_t hint = hints[0];
|
| 117 |
device_addrs_t usrp2_addrs; |
| 118 |
|
| 119 |
//return an empty list of addresses when type is set to non-usrp2
|
| 120 |
if (hint.has_key("type") and hint["type"] != "usrp2") return usrp2_addrs; |
| 121 |
|
| 122 |
//if no address was specified, send a broadcast on each interface
|
| 123 |
if (not hint.has_key("addr")){ |
| 124 |
BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
|
| 125 |
//avoid the loopback device
|
| 126 |
if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue; |
| 127 |
|
| 128 |
//create a new hint with this broadcast address
|
| 129 |
device_addr_t new_hint = hint; |
| 130 |
new_hint["addr"] = if_addrs.bcast;
|
| 131 |
|
| 132 |
//call discover with the new hint and append results
|
| 133 |
device_addrs_t new_usrp2_addrs = usrp2_find(new_hint); |
| 134 |
usrp2_addrs.insert(usrp2_addrs.begin(), |
| 135 |
new_usrp2_addrs.begin(), new_usrp2_addrs.end() |
| 136 |
); |
| 137 |
} |
| 138 |
return usrp2_addrs;
|
| 139 |
} |
| 140 |
|
| 141 |
//create a udp transport to communicate
|
| 142 |
std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT); |
| 143 |
udp_simple::sptr udp_transport = udp_simple::make_broadcast( |
| 144 |
hint["addr"], ctrl_port
|
| 145 |
); |
| 146 |
|
| 147 |
//send a hello control packet
|
| 148 |
usrp2_ctrl_data_t ctrl_data_out; |
| 149 |
ctrl_data_out.proto_ver = htonl(USRP2_FW_COMPAT_NUM); |
| 150 |
ctrl_data_out.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO); |
| 151 |
udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out)));
|
| 152 |
|
| 153 |
//loop and recieve until the timeout
|
| 154 |
boost::uint8_t usrp2_ctrl_data_in_mem[udp_simple::mtu]; //allocate max bytes for recv
|
| 155 |
const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem); |
| 156 |
while(true){ |
| 157 |
size_t len = udp_transport->recv(asio::buffer(usrp2_ctrl_data_in_mem)); |
| 158 |
//std::cout << len << "\n";
|
| 159 |
if (len > offsetof(usrp2_ctrl_data_t, data) and ntohl(ctrl_data_in->id) == USRP2_CTRL_ID_WAZZUP_DUDE){ |
| 160 |
//make a boost asio ipv4 with the raw addr in host byte order
|
| 161 |
boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in->data.ip_addr)); |
| 162 |
device_addr_t new_addr; |
| 163 |
new_addr["type"] = "usrp2"; |
| 164 |
new_addr["addr"] = ip_addr.to_string();
|
| 165 |
//Attempt to read the name from the EEPROM and perform filtering.
|
| 166 |
//This operation can throw due to compatibility mismatch.
|
| 167 |
//In this case, the discovered device will be ignored.
|
| 168 |
try{
|
| 169 |
mboard_eeprom_t mb_eeprom = usrp2_iface::make( |
| 170 |
udp_simple::make_connected(new_addr["addr"], num2str(USRP2_UDP_CTRL_PORT))
|
| 171 |
)->mb_eeprom; |
| 172 |
new_addr["name"] = mb_eeprom["name"]; |
| 173 |
new_addr["serial"] = mb_eeprom["serial"]; |
| 174 |
if (
|
| 175 |
(not hint.has_key("name") or hint["name"] == new_addr["name"]) and |
| 176 |
(not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) |
| 177 |
){
|
| 178 |
usrp2_addrs.push_back(new_addr); |
| 179 |
} |
| 180 |
} |
| 181 |
catch(const std::exception &e){ |
| 182 |
uhd::warning::post( |
| 183 |
std::string("Ignoring discovered device\n") |
| 184 |
+ e.what() |
| 185 |
); |
| 186 |
} |
| 187 |
//dont break here, it will exit the while loop
|
| 188 |
//just continue on to the next loop iteration
|
| 189 |
} |
| 190 |
if (len == 0) break; //timeout |
| 191 |
} |
| 192 |
|
| 193 |
return usrp2_addrs;
|
| 194 |
} |
| 195 |
|
| 196 |
/***********************************************************************
|
| 197 |
* Make
|
| 198 |
**********************************************************************/
|
| 199 |
static device::sptr usrp2_make(const device_addr_t &device_addr){ |
| 200 |
sep_indexed_dev_addrs(device_addr); |
| 201 |
//create a ctrl and data transport for each address
|
| 202 |
std::vector<udp_simple::sptr> ctrl_transports; |
| 203 |
std::vector<zero_copy_if::sptr> data_transports; |
| 204 |
const device_addrs_t device_addrs = sep_indexed_dev_addrs(device_addr);
|
| 205 |
|
| 206 |
BOOST_FOREACH(const device_addr_t &dev_addr_i, device_addrs){
|
| 207 |
ctrl_transports.push_back(udp_simple::make_connected( |
| 208 |
dev_addr_i["addr"], num2str(USRP2_UDP_CTRL_PORT)
|
| 209 |
)); |
| 210 |
data_transports.push_back(udp_zero_copy::make( |
| 211 |
dev_addr_i["addr"], num2str(USRP2_UDP_DATA_PORT), device_addr
|
| 212 |
)); |
| 213 |
} |
| 214 |
|
| 215 |
//create the usrp2 implementation guts
|
| 216 |
return device::sptr(
|
| 217 |
new usrp2_impl(ctrl_transports, data_transports, device_addrs)
|
| 218 |
); |
| 219 |
} |
| 220 |
|
| 221 |
UHD_STATIC_BLOCK(register_usrp2_device){
|
| 222 |
device::register_device(&usrp2_find, &usrp2_make); |
| 223 |
} |
| 224 |
|
| 225 |
/***********************************************************************
|
| 226 |
* Structors
|
| 227 |
**********************************************************************/
|
| 228 |
usrp2_impl::usrp2_impl( |
| 229 |
std::vector<udp_simple::sptr> ctrl_transports, |
| 230 |
std::vector<zero_copy_if::sptr> data_transports, |
| 231 |
const device_addrs_t &device_args
|
| 232 |
): |
| 233 |
_data_transports(data_transports) |
| 234 |
{
|
| 235 |
//setup rx otw type
|
| 236 |
_rx_otw_type.width = 16;
|
| 237 |
_rx_otw_type.shift = 0;
|
| 238 |
_rx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; |
| 239 |
|
| 240 |
//setup tx otw type
|
| 241 |
_tx_otw_type.width = 16;
|
| 242 |
_tx_otw_type.shift = 0;
|
| 243 |
_tx_otw_type.byteorder = uhd::otw_type_t::BO_BIG_ENDIAN; |
| 244 |
|
| 245 |
//!!!!! set the otw type here before continuing, its used below
|
| 246 |
|
| 247 |
//create a new mboard handler for each control transport
|
| 248 |
for(size_t i = 0; i < device_args.size(); i++){ |
| 249 |
_mboards.push_back(usrp2_mboard_impl::sptr(new usrp2_mboard_impl(
|
| 250 |
i, ctrl_transports[i], data_transports[i], device_args[i], |
| 251 |
this->get_max_recv_samps_per_packet()
|
| 252 |
))); |
| 253 |
//use an empty name when there is only one mboard
|
| 254 |
std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : ""; |
| 255 |
_mboard_dict[name] = _mboards.back(); |
| 256 |
} |
| 257 |
|
| 258 |
//init the send and recv io
|
| 259 |
io_init(); |
| 260 |
|
| 261 |
} |
| 262 |
|
| 263 |
usrp2_impl::~usrp2_impl(void){
|
| 264 |
/* NOP */
|
| 265 |
} |
| 266 |
|
| 267 |
/***********************************************************************
|
| 268 |
* Device Properties
|
| 269 |
**********************************************************************/
|
| 270 |
void usrp2_impl::get(const wax::obj &key_, wax::obj &val){ |
| 271 |
named_prop_t key = named_prop_t::extract(key_); |
| 272 |
|
| 273 |
//handle the get request conditioned on the key
|
| 274 |
switch(key.as<device_prop_t>()){
|
| 275 |
case DEVICE_PROP_NAME:
|
| 276 |
if (_mboards.size() > 1) val = std::string("USRP2/N Series multi-device"); |
| 277 |
else val = std::string("USRP2/N Series device"); |
| 278 |
return;
|
| 279 |
|
| 280 |
case DEVICE_PROP_MBOARD:
|
| 281 |
val = _mboard_dict[key.name]->get_link(); |
| 282 |
return;
|
| 283 |
|
| 284 |
case DEVICE_PROP_MBOARD_NAMES:
|
| 285 |
val = prop_names_t(_mboard_dict.keys()); |
| 286 |
return;
|
| 287 |
|
| 288 |
default: UHD_THROW_PROP_GET_ERROR();
|
| 289 |
} |
| 290 |
} |
| 291 |
|
| 292 |
void usrp2_impl::set(const wax::obj &, const wax::obj &){ |
| 293 |
UHD_THROW_PROP_SET_ERROR(); |
| 294 |
} |