Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / usrp2 / usrp2_iface.cpp @ 23f8854b

History | View | Annotate | Download (17.9 KB)

1
//
2
// Copyright 2010-2012 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_regs.hpp"
19
#include "usrp2_impl.hpp"
20
#include "fw_common.h"
21
#include "usrp2_iface.hpp"
22
#include <uhd/exception.hpp>
23
#include <uhd/utils/msg.hpp>
24
#include <uhd/utils/tasks.hpp>
25
#include <uhd/utils/images.hpp>
26
#include <uhd/utils/safe_call.hpp>
27
#include <uhd/types/dict.hpp>
28
#include <boost/thread.hpp>
29
#include <boost/foreach.hpp>
30
#include <boost/asio.hpp> //used for htonl and ntohl
31
#include <boost/assign/list_of.hpp>
32
#include <boost/format.hpp>
33
#include <boost/bind.hpp>
34
#include <boost/tokenizer.hpp>
35
#include <boost/functional/hash.hpp>
36
#include <boost/filesystem.hpp>
37
#include <algorithm>
38
#include <iostream>
39

    
40
using namespace uhd;
41
using namespace uhd::usrp;
42
using namespace uhd::transport;
43
namespace fs = boost::filesystem;
44

    
45
static const double CTRL_RECV_TIMEOUT = 1.0;
46
static const size_t CTRL_RECV_RETRIES = 3;
47

    
48
//custom timeout error for retry logic to catch/retry
49
struct timeout_error : uhd::runtime_error
50
{
51
    timeout_error(const std::string &what):
52
        uhd::runtime_error(what)
53
    {
54
        //NOP
55
    }
56
};
57

    
58
static const boost::uint32_t MIN_PROTO_COMPAT_SPI = 7;
59
static const boost::uint32_t MIN_PROTO_COMPAT_I2C = 7;
60
// The register compat number must reflect the protocol compatibility
61
// and the compatibility of the register mapping (more likely to change).
62
static const boost::uint32_t MIN_PROTO_COMPAT_REG = 10;
63
static const boost::uint32_t MIN_PROTO_COMPAT_UART = 7;
64

    
65
//Define get_gpid() to get a globally unique identifier for this process.
66
//The gpid is implemented as a hash of the pid and a unique machine identifier.
67
#ifdef UHD_PLATFORM_WIN32
68
#include <Windows.h>
69
static inline size_t get_gpid(void){
70
    //extract volume serial number
71
    char szVolName[MAX_PATH+1], szFileSysName[MAX_PATH+1];
72
    DWORD dwSerialNumber, dwMaxComponentLen, dwFileSysFlags;
73
    GetVolumeInformation("C:\\", szVolName, MAX_PATH,
74
        &dwSerialNumber, &dwMaxComponentLen,
75
        &dwFileSysFlags, szFileSysName, sizeof(szFileSysName));
76

    
77
    size_t hash = 0;
78
    boost::hash_combine(hash, GetCurrentProcessId());
79
    boost::hash_combine(hash, dwSerialNumber);
80
    return hash;
81
}
82
#else
83
#include <unistd.h>
84
static inline size_t get_gpid(void){
85
    size_t hash = 0;
86
    boost::hash_combine(hash, getpid());
87
    boost::hash_combine(hash, gethostid());
88
    return hash;
89
}
90
#endif
91

    
92
class usrp2_iface_impl : public usrp2_iface{
93
public:
94
/***********************************************************************
95
 * Structors
96
 **********************************************************************/
97
    usrp2_iface_impl(udp_simple::sptr ctrl_transport):
98
        _ctrl_transport(ctrl_transport),
99
        _ctrl_seq_num(0),
100
        _protocol_compat(0) //initialized below...
101
    {
102
        //Obtain the firmware's compat number.
103
        //Save the response compat number for communication.
104
        //TODO can choose to reject certain older compat numbers
105
        usrp2_ctrl_data_t ctrl_data = usrp2_ctrl_data_t();
106
        ctrl_data.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO);
107
        ctrl_data = ctrl_send_and_recv(ctrl_data, 0, ~0);
108
        if (ntohl(ctrl_data.id) != USRP2_CTRL_ID_WAZZUP_DUDE)
109
            throw uhd::runtime_error("firmware not responding");
110
        _protocol_compat = ntohl(ctrl_data.proto_ver);
111

    
112
        mb_eeprom = mboard_eeprom_t(*this, USRP2_EEPROM_MAP_KEY);
113
    }
114

    
115
    ~usrp2_iface_impl(void){UHD_SAFE_CALL(
116
        this->lock_device(false);
117
    )}
118

    
119
/***********************************************************************
120
 * Device locking
121
 **********************************************************************/
122

    
123
    void lock_device(bool lock){
124
        if (lock){
125
            this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid()));
126
            _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this));
127
        }
128
        else{
129
            _lock_task.reset(); //shutdown the task
130
            this->pokefw(U2_FW_REG_LOCK_TIME, 0); //unlock
131
        }
132
    }
133

    
134
    bool is_device_locked(void){
135
        //never assume lock with fpga image mismatch
136
        if ((this->peek32(U2_REG_COMPAT_NUM_RB) >> 16) != USRP2_FPGA_COMPAT_NUM) return false;
137

    
138
        boost::uint32_t lock_time = this->peekfw(U2_FW_REG_LOCK_TIME);
139
        boost::uint32_t lock_gpid = this->peekfw(U2_FW_REG_LOCK_GPID);
140

    
141
        //may not be the right tick rate, but this is ok for locking purposes
142
        const boost::uint32_t lock_timeout_time = boost::uint32_t(3*100e6);
143

    
144
        //if the difference is larger, assume not locked anymore
145
        if ((lock_time & 1) == 0) return false; //bit0 says unlocked
146
        const boost::uint32_t time_diff = this->get_curr_time() - lock_time;
147
        if (time_diff >= lock_timeout_time) return false;
148

    
149
        //otherwise only lock if the device hash is different that ours
150
        return lock_gpid != boost::uint32_t(get_gpid());
151
    }
152

    
153
    void lock_task(void){
154
        //re-lock in task
155
        this->pokefw(U2_FW_REG_LOCK_TIME, this->get_curr_time());
156
        //sleep for a bit
157
        boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
158
    }
159

    
160
    boost::uint32_t get_curr_time(void){
161
        return this->peek32(U2_REG_TIME64_LO_RB_IMM) | 1; //bit 1 says locked
162
    }
163

    
164
/***********************************************************************
165
 * Peek and Poke
166
 **********************************************************************/
167
    void poke32(wb_addr_type addr, boost::uint32_t data){
168
        this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FPGA_POKE32>(addr, data);
169
    }
170

    
171
    boost::uint32_t peek32(wb_addr_type addr){
172
        return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FPGA_PEEK32>(addr);
173
    }
174

    
175
    void poke16(wb_addr_type addr, boost::uint16_t data){
176
        this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_POKE16>(addr, data);
177
    }
178

    
179
    boost::uint16_t peek16(wb_addr_type addr){
180
        return this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_PEEK16>(addr);
181
    }
182

    
183
    void pokefw(wb_addr_type addr, boost::uint32_t data)
184
    {
185
        this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(addr, data);
186
    }
187

    
188
    boost::uint32_t peekfw(wb_addr_type addr)
189
    {
190
        return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(addr);
191
    }
192

    
193
    template <class T, usrp2_reg_action_t action>
194
    T get_reg(wb_addr_type addr, T data = 0){
195
        //setup the out data
196
        usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
197
        out_data.id = htonl(USRP2_CTRL_ID_GET_THIS_REGISTER_FOR_ME_BRO);
198
        out_data.data.reg_args.addr = htonl(addr);
199
        out_data.data.reg_args.data = htonl(boost::uint32_t(data));
200
        out_data.data.reg_args.action = action;
201

    
202
        //send and recv
203
        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_REG);
204
        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_GOT_REGISTER_SO_BAD_DUDE);
205
        return T(ntohl(in_data.data.reg_args.data));
206
    }
207

    
208
/***********************************************************************
209
 * SPI
210
 **********************************************************************/
211
    boost::uint32_t transact_spi(
212
        int which_slave,
213
        const spi_config_t &config,
214
        boost::uint32_t data,
215
        size_t num_bits,
216
        bool readback
217
    ){
218
        static const uhd::dict<spi_config_t::edge_t, int> spi_edge_to_otw = boost::assign::map_list_of
219
            (spi_config_t::EDGE_RISE, USRP2_CLK_EDGE_RISE)
220
            (spi_config_t::EDGE_FALL, USRP2_CLK_EDGE_FALL)
221
        ;
222

    
223
        //setup the out data
224
        usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
225
        out_data.id = htonl(USRP2_CTRL_ID_TRANSACT_ME_SOME_SPI_BRO);
226
        out_data.data.spi_args.dev = htonl(which_slave);
227
        out_data.data.spi_args.miso_edge = spi_edge_to_otw[config.miso_edge];
228
        out_data.data.spi_args.mosi_edge = spi_edge_to_otw[config.mosi_edge];
229
        out_data.data.spi_args.readback = (readback)? 1 : 0;
230
        out_data.data.spi_args.num_bits = num_bits;
231
        out_data.data.spi_args.data = htonl(data);
232

    
233
        //send and recv
234
        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_SPI);
235
        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE);
236

    
237
        return ntohl(in_data.data.spi_args.data);
238
    }
239

    
240
/***********************************************************************
241
 * I2C
242
 **********************************************************************/
243
    void write_i2c(boost::uint8_t addr, const byte_vector_t &buf){
244
        //setup the out data
245
        usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
246
        out_data.id = htonl(USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO);
247
        out_data.data.i2c_args.addr = addr;
248
        out_data.data.i2c_args.bytes = buf.size();
249

    
250
        //limitation of i2c transaction size
251
        UHD_ASSERT_THROW(buf.size() <= sizeof(out_data.data.i2c_args.data));
252

    
253
        //copy in the data
254
        std::copy(buf.begin(), buf.end(), out_data.data.i2c_args.data);
255

    
256
        //send and recv
257
        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_I2C);
258
        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE);
259
    }
260

    
261
    byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes){
262
        //setup the out data
263
        usrp2_ctrl_data_t out_data = usrp2_ctrl_data_t();
264
        out_data.id = htonl(USRP2_CTRL_ID_DO_AN_I2C_READ_FOR_ME_BRO);
265
        out_data.data.i2c_args.addr = addr;
266
        out_data.data.i2c_args.bytes = num_bytes;
267

    
268
        //limitation of i2c transaction size
269
        UHD_ASSERT_THROW(num_bytes <= sizeof(out_data.data.i2c_args.data));
270

    
271
        //send and recv
272
        usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_I2C);
273
        UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE);
274
        UHD_ASSERT_THROW(in_data.data.i2c_args.addr = num_bytes);
275

    
276
        //copy out the data
277
        byte_vector_t result(num_bytes);
278
        std::copy(in_data.data.i2c_args.data, in_data.data.i2c_args.data + num_bytes, result.begin());
279
        return result;
280
    }
281

    
282
/***********************************************************************
283
 * Send/Recv over control
284
 **********************************************************************/
285
    usrp2_ctrl_data_t ctrl_send_and_recv(
286
        const usrp2_ctrl_data_t &out_data,
287
        boost::uint32_t lo = USRP2_FW_COMPAT_NUM,
288
        boost::uint32_t hi = USRP2_FW_COMPAT_NUM
289
    ){
290
        boost::mutex::scoped_lock lock(_ctrl_mutex);
291

    
292
        for (size_t i = 0; i < CTRL_RECV_RETRIES; i++){
293
            try{
294
                return ctrl_send_and_recv_internal(out_data, lo, hi, CTRL_RECV_TIMEOUT/CTRL_RECV_RETRIES);
295
            }
296
            catch(const timeout_error &e){
297
                UHD_MSG(error)
298
                    << "Control packet attempt " << i
299
                    << ", sequence number " << _ctrl_seq_num
300
                    << ":\n" << e.what() << std::endl;
301
            }
302
        }
303
        throw uhd::runtime_error("link dead: timeout waiting for control packet ACK");
304
    }
305

    
306
    usrp2_ctrl_data_t ctrl_send_and_recv_internal(
307
        const usrp2_ctrl_data_t &out_data,
308
        boost::uint32_t lo, boost::uint32_t hi,
309
        const double timeout
310
    ){
311
        //fill in the seq number and send
312
        usrp2_ctrl_data_t out_copy = out_data;
313
        out_copy.proto_ver = htonl(_protocol_compat);
314
        out_copy.seq = htonl(++_ctrl_seq_num);
315
        _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t)));
316

    
317
        //loop until we get the packet or timeout
318
        boost::uint8_t usrp2_ctrl_data_in_mem[udp_simple::mtu]; //allocate max bytes for recv
319
        const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem);
320
        while(true){
321
            size_t len = _ctrl_transport->recv(boost::asio::buffer(usrp2_ctrl_data_in_mem), timeout);
322
            boost::uint32_t compat = ntohl(ctrl_data_in->proto_ver);
323
            if(len >= sizeof(boost::uint32_t) and (hi < compat or lo > compat)){
324
                throw uhd::runtime_error(str(boost::format(
325
                    "\nPlease update the firmware and FPGA images for your device.\n"
326
                    "See the application notes for USRP2/N-Series for instructions.\n"
327
                    "Expected protocol compatibility number %s, but got %d:\n"
328
                    "The firmware build is not compatible with the host code build.\n"
329
                    "%s\n"
330
                ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi))
331
                  % compat % this->images_warn_help_message()));
332
            }
333
            if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(ctrl_data_in->seq) == _ctrl_seq_num){
334
                return *ctrl_data_in;
335
            }
336
            if (len == 0) break; //timeout
337
            //didnt get seq or bad packet, continue looking...
338
        }
339
        throw timeout_error("no control response, possible packet loss");
340
    }
341

    
342
    rev_type get_rev(void){
343
        std::string hw = mb_eeprom["hardware"];
344
        if (hw.empty()) return USRP_NXXX;
345
        switch (boost::lexical_cast<boost::uint16_t>(hw)){
346
        case 0x0300:
347
        case 0x0301: return USRP2_REV3;
348
        case 0x0400: return USRP2_REV4;
349
        case 0x0A00: return USRP_N200;
350
        case 0x0A01: return USRP_N210;
351
        case 0x0A10: return USRP_N200_R4;
352
        case 0x0A11: return USRP_N210_R4;
353
        }
354
        return USRP_NXXX; //unknown type
355
    }
356

    
357
    const std::string get_cname(void){
358
        switch(this->get_rev()){
359
        case USRP2_REV3: return "USRP2 r3";
360
        case USRP2_REV4: return "USRP2 r4";
361
        case USRP_N200: return "N200";
362
        case USRP_N210: return "N210";
363
        case USRP_N200_R4: return "N200r4";
364
        case USRP_N210_R4: return "N210r4";
365
        case USRP_NXXX: return "N???";
366
        }
367
        UHD_THROW_INVALID_CODE_PATH();
368
    }
369

    
370
    const std::string get_fw_version_string(void){
371
        boost::uint32_t minor = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_VER_MINOR);
372
        return str(boost::format("%u.%u") % _protocol_compat % minor);
373
    }
374

    
375
    std::string images_warn_help_message(void){
376
        //determine the images names
377
        std::string fw_image, fpga_image;
378
        switch(this->get_rev()){
379
        case USRP2_REV3:   fpga_image = "usrp2_fpga.bin";        fw_image = "usrp2_fw.bin";     break;
380
        case USRP2_REV4:   fpga_image = "usrp2_fpga.bin";        fw_image = "usrp2_fw.bin";     break;
381
        case USRP_N200:    fpga_image = "usrp_n200_r2_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break;
382
        case USRP_N210:    fpga_image = "usrp_n210_r2_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break;
383
        case USRP_N200_R4: fpga_image = "usrp_n200_r4_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break;
384
        case USRP_N210_R4: fpga_image = "usrp_n210_r4_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break;
385
        default: break;
386
        }
387
        if (fw_image.empty() or fpga_image.empty()) return "";
388

    
389
        //does your platform use sudo?
390
        std::string sudo;
391
        #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_MACOS)
392
            sudo = "sudo ";
393
        #endif
394

    
395

    
396
        //look up the real FS path to the images
397
        std::string fw_image_path, fpga_image_path;
398
        try{
399
            fw_image_path = uhd::find_image_path(fw_image);
400
            fpga_image_path = uhd::find_image_path(fpga_image);
401
        }
402
        catch(const std::exception &){
403
            return str(boost::format("Could not find %s and %s in your images path!\n%s") % fw_image % fpga_image % print_images_error());
404
        }
405

    
406
        //escape char for multi-line cmd + newline + indent?
407
        #ifdef UHD_PLATFORM_WIN32
408
            const std::string ml = "^\n    ";
409
        #else
410
            const std::string ml = "\\\n    ";
411
        #endif
412

    
413
        //create the images downloader and burner commands
414
        const std::string images_downloader_cmd = str(boost::format("%s\"%s\"") % sudo % find_images_downloader());
415
        if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){
416
            const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.py").string();
417
            const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
418
            return str(boost::format("%s\n%s") % print_images_error() % card_burner_cmd);
419
        }
420
        else{
421
            const std::string addr = _ctrl_transport->get_recv_addr();
422
            const std::string net_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_net_burner.py").string();
423
            const std::string net_burner_cmd = str(boost::format("\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\" %s--addr=\"%s\"") % net_burner % ml % fpga_image_path % ml % fw_image_path % ml % addr);
424
            return str(boost::format("%s\n%s") % print_images_error() % net_burner_cmd);
425
        }
426
    }
427

    
428
private:
429
    //this lovely lady makes it all possible
430
    udp_simple::sptr _ctrl_transport;
431

    
432
    //used in send/recv
433
    boost::mutex _ctrl_mutex;
434
    boost::uint32_t _ctrl_seq_num;
435
    boost::uint32_t _protocol_compat;
436

    
437
    //lock thread stuff
438
    task::sptr _lock_task;
439
};
440

    
441
/***********************************************************************
442
 * Public make function for usrp2 interface
443
 **********************************************************************/
444
usrp2_iface::sptr usrp2_iface::make(udp_simple::sptr ctrl_transport){
445
    return usrp2_iface::sptr(new usrp2_iface_impl(ctrl_transport));
446
}
447