Statistics
| Branch: | Tag: | Revision:

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
}