Statistics
| Branch: | Tag: | Revision:

root / host / lib / usrp / mboard_eeprom.cpp @ 6ca39ad9

History | View | Annotate | Download (17.4 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 <uhd/usrp/mboard_eeprom.hpp>
19
#include <uhd/types/mac_addr.hpp>
20
#include <uhd/utils/byteswap.hpp>
21
#include <boost/asio/ip/address_v4.hpp>
22
#include <boost/assign/list_of.hpp>
23
#include <boost/lexical_cast.hpp>
24
#include <boost/foreach.hpp>
25
#include <algorithm>
26
#include <iostream>
27
#include <cstddef>
28

    
29
using namespace uhd;
30
using namespace uhd::usrp;
31

    
32
/***********************************************************************
33
 * Constants
34
 **********************************************************************/
35
static const size_t SERIAL_LEN = 9;
36
static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN;
37

    
38
/***********************************************************************
39
 * Utility functions
40
 **********************************************************************/
41

    
42
//! A wrapper around std::copy that takes ranges instead of iterators.
43
template<typename RangeSrc, typename RangeDst> inline
44
void byte_copy(const RangeSrc &src, RangeDst &dst){
45
    std::copy(boost::begin(src), boost::end(src), boost::begin(dst));
46
}
47

    
48
//! create a string from a byte vector, return empty if invalid ascii
49
static const std::string bytes_to_string(const byte_vector_t &bytes){
50
    std::string out;
51
    BOOST_FOREACH(boost::uint8_t byte, bytes){
52
        if (byte < 32 or byte > 127) return out;
53
        out += byte;
54
    }
55
    return out;
56
}
57

    
58
//! create a byte vector from a string, null terminate unless max length
59
static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){
60
    byte_vector_t bytes;
61
    for (size_t i = 0; i < std::min(string.size(), max_length); i++){
62
        bytes.push_back(string[i]);
63
    }
64
    if (bytes.size() < max_length - 1) bytes.push_back('\0');
65
    return bytes;
66
}
67

    
68
//! convert a string to a byte vector to write to eeprom
69
static byte_vector_t string_to_uint16_bytes(const std::string &num_str){
70
    const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str);
71
    const byte_vector_t lsb_msb = boost::assign::list_of
72
        (boost::uint8_t(num >> 0))(boost::uint8_t(num >> 8));
73
    return lsb_msb;
74
}
75

    
76
//! convert a byte vector read from eeprom to a string
77
static std::string uint16_bytes_to_string(const byte_vector_t &bytes){
78
    const boost::uint16_t num = (boost::uint16_t(bytes.at(0)) << 0) | (boost::uint16_t(bytes.at(1)) << 8);
79
    return (num == 0 or num == 0xffff)? "" : boost::lexical_cast<std::string>(num);
80
}
81

    
82
/***********************************************************************
83
 * Implementation of N100 load/store
84
 **********************************************************************/
85
static const boost::uint8_t N100_EEPROM_ADDR = 0x50;
86

    
87
struct n100_eeprom_map{
88
    boost::uint16_t hardware;
89
    boost::uint8_t mac_addr[6];
90
    boost::uint32_t subnet;
91
    boost::uint32_t ip_addr;
92
    boost::uint16_t _pad0;
93
    boost::uint16_t revision;
94
    boost::uint16_t product;
95
    unsigned char _pad1;
96
    unsigned char gpsdo;
97
    unsigned char serial[SERIAL_LEN];
98
    unsigned char name[NAME_MAX_LEN];
99
    boost::uint32_t gateway;
100
};
101

    
102
enum n200_gpsdo_type{
103
    N200_GPSDO_NONE = 0,
104
    N200_GPSDO_INTERNAL = 1,
105
    N200_GPSDO_ONBOARD = 2
106
};
107

    
108
static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
109
    //extract the hardware number
110
    mb_eeprom["hardware"] = uint16_bytes_to_string(
111
        iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2)
112
    );
113

    
114
    //extract the revision number
115
    mb_eeprom["revision"] = uint16_bytes_to_string(
116
        iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2)
117
    );
118

    
119
    //extract the product code
120
    mb_eeprom["product"] = uint16_bytes_to_string(
121
        iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2)
122
    );
123

    
124
    //extract the addresses
125
    mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom(
126
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6
127
    )).to_string();
128

    
129
    boost::asio::ip::address_v4::bytes_type ip_addr_bytes;
130
    byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes);
131
    mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string();
132

    
133
    byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes);
134
    mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string();
135

    
136
    byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes);
137
    mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string();
138

    
139
    //gpsdo capabilities
140
    boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0);
141
    switch(n200_gpsdo_type(gpsdo_byte)){
142
    case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break;
143
    case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break;
144
    default: mb_eeprom["gpsdo"] = "none";
145
    }
146

    
147
    //extract the serial
148
    mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
149
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN
150
    ));
151

    
152
    //extract the name
153
    mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
154
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN
155
    ));
156

    
157
    //Empty serial correction: use the mac address to determine serial.
158
    //Older usrp2 models don't have a serial burned into EEPROM.
159
    //The lower mac address bits will function as the serial number.
160
    if (mb_eeprom["serial"].empty()){
161
        byte_vector_t mac_addr_bytes = mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes();
162
        unsigned serial = mac_addr_bytes.at(5) | (unsigned(mac_addr_bytes.at(4) & 0x0f) << 8);
163
        mb_eeprom["serial"] = boost::lexical_cast<std::string>(serial);
164
    }
165
}
166

    
167
static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
168
    //parse the revision number
169
    if (mb_eeprom.has_key("hardware")) iface.write_eeprom(
170
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware),
171
        string_to_uint16_bytes(mb_eeprom["hardware"])
172
    );
173

    
174
    //parse the revision number
175
    if (mb_eeprom.has_key("revision")) iface.write_eeprom(
176
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision),
177
        string_to_uint16_bytes(mb_eeprom["revision"])
178
    );
179

    
180
    //parse the product code
181
    if (mb_eeprom.has_key("product")) iface.write_eeprom(
182
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product),
183
        string_to_uint16_bytes(mb_eeprom["product"])
184
    );
185

    
186
    //store the addresses
187
    if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom(
188
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr),
189
        mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes()
190
    );
191

    
192
    if (mb_eeprom.has_key("ip-addr")){
193
        byte_vector_t ip_addr_bytes(4);
194
        byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes);
195
        iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes);
196
    }
197

    
198
    if (mb_eeprom.has_key("subnet")){
199
        byte_vector_t ip_addr_bytes(4);
200
        byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes);
201
        iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes);
202
    }
203

    
204
    if (mb_eeprom.has_key("gateway")){
205
        byte_vector_t ip_addr_bytes(4);
206
        byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes);
207
        iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes);
208
    }
209

    
210
    //gpsdo capabilities
211
    if (mb_eeprom.has_key("gpsdo")){
212
        boost::uint8_t gpsdo_byte = N200_GPSDO_NONE;
213
        if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL;
214
        if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD;
215
        iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte));
216
    }
217

    
218
    //store the serial
219
    if (mb_eeprom.has_key("serial")) iface.write_eeprom(
220
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial),
221
        string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
222
    );
223

    
224
    //store the name
225
    if (mb_eeprom.has_key("name")) iface.write_eeprom(
226
        N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name),
227
        string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
228
    );
229
}
230

    
231
/***********************************************************************
232
 * Implementation of B000 load/store
233
 **********************************************************************/
234
static const boost::uint8_t B000_EEPROM_ADDR = 0x50;
235
static const size_t B000_SERIAL_LEN = 8;
236

    
237
//use char array so we dont need to attribute packed
238
struct b000_eeprom_map{
239
    unsigned char _r[221];
240
    unsigned char mcr[4];
241
    unsigned char name[NAME_MAX_LEN];
242
    unsigned char serial[B000_SERIAL_LEN];
243
};
244

    
245
static void load_b000(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
246
    //extract the serial
247
    mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
248
        B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial), B000_SERIAL_LEN
249
    ));
250

    
251
    //extract the name
252
    mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
253
        B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name), NAME_MAX_LEN
254
    ));
255

    
256
    //extract master clock rate as a 32-bit uint in Hz
257
    boost::uint32_t master_clock_rate;
258
    const byte_vector_t rate_bytes = iface.read_eeprom(
259
        B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), sizeof(master_clock_rate)
260
    );
261
    std::copy(
262
        rate_bytes.begin(), rate_bytes.end(), //input
263
        reinterpret_cast<boost::uint8_t *>(&master_clock_rate) //output
264
    );
265
    master_clock_rate = ntohl(master_clock_rate);
266
    if (master_clock_rate > 1e6 and master_clock_rate < 1e9){
267
        mb_eeprom["mcr"] = boost::lexical_cast<std::string>(master_clock_rate);
268
    }
269
    else mb_eeprom["mcr"] = "";
270
}
271

    
272
static void store_b000(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
273
    //store the serial
274
    if (mb_eeprom.has_key("serial")) iface.write_eeprom(
275
        B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial),
276
        string_to_bytes(mb_eeprom["serial"], B000_SERIAL_LEN)
277
    );
278

    
279
    //store the name
280
    if (mb_eeprom.has_key("name")) iface.write_eeprom(
281
        B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name),
282
        string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
283
    );
284

    
285
    //store the master clock rate as a 32-bit uint in Hz
286
    if (mb_eeprom.has_key("mcr")){
287
        boost::uint32_t master_clock_rate = boost::uint32_t(boost::lexical_cast<double>(mb_eeprom["mcr"]));
288
        master_clock_rate = htonl(master_clock_rate);
289
        const byte_vector_t rate_bytes(
290
            reinterpret_cast<const boost::uint8_t *>(&master_clock_rate),
291
            reinterpret_cast<const boost::uint8_t *>(&master_clock_rate) + sizeof(master_clock_rate)
292
        );
293
        iface.write_eeprom(
294
            B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), rate_bytes
295
        );
296
    }
297
}
298

    
299
/***********************************************************************
300
 * Implementation of B100 load/store
301
 **********************************************************************/
302
static const boost::uint8_t B100_EEPROM_ADDR = 0x50;
303

    
304
//use char array so we dont need to attribute packed
305
struct b100_eeprom_map{
306
    unsigned char _r[220];
307
    unsigned char revision[2];
308
    unsigned char product[2];
309
    unsigned char name[NAME_MAX_LEN];
310
    unsigned char serial[SERIAL_LEN];
311
};
312

    
313
static void load_b100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
314
    //extract the revision number
315
    mb_eeprom["revision"] = uint16_bytes_to_string(
316
        iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision), 2)
317
    );
318

    
319
    //extract the product code
320
    mb_eeprom["product"] = uint16_bytes_to_string(
321
        iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product), 2)
322
    );
323

    
324
    //extract the serial
325
    mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom(
326
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), SERIAL_LEN
327
    ));
328

    
329
    //extract the name
330
    mb_eeprom["name"] = bytes_to_string(iface.read_eeprom(
331
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), NAME_MAX_LEN
332
    ));
333
}
334

    
335
static void store_b100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
336
    //parse the revision number
337
    if (mb_eeprom.has_key("revision")) iface.write_eeprom(
338
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision),
339
        string_to_uint16_bytes(mb_eeprom["revision"])
340
    );
341

    
342
    //parse the product code
343
    if (mb_eeprom.has_key("product")) iface.write_eeprom(
344
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product),
345
        string_to_uint16_bytes(mb_eeprom["product"])
346
    );
347

    
348
    //store the serial
349
    if (mb_eeprom.has_key("serial")) iface.write_eeprom(
350
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial),
351
        string_to_bytes(mb_eeprom["serial"], SERIAL_LEN)
352
    );
353

    
354
    //store the name
355
    if (mb_eeprom.has_key("name")) iface.write_eeprom(
356
        B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name),
357
        string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN)
358
    );
359
}
360

    
361
/***********************************************************************
362
 * Implementation of E100 load/store
363
 **********************************************************************/
364
static const boost::uint8_t E100_EEPROM_ADDR = 0x51;
365

    
366
struct e100_eeprom_map{
367
    boost::uint16_t vendor;
368
    boost::uint16_t device;
369
    unsigned char revision;
370
    unsigned char content;
371
    unsigned char model[8];
372
    unsigned char env_var[16];
373
    unsigned char env_setting[64];
374
    unsigned char serial[10];
375
    unsigned char name[NAME_MAX_LEN];
376
};
377

    
378
template <typename T> static const byte_vector_t to_bytes(const T &item){
379
    return byte_vector_t(
380
        reinterpret_cast<const byte_vector_t::value_type *>(&item),
381
        reinterpret_cast<const byte_vector_t::value_type *>(&item)+sizeof(item)
382
    );
383
}
384

    
385
#define sizeof_member(struct_name, member_name) \
386
    sizeof(reinterpret_cast<struct_name*>(NULL)->member_name)
387

    
388
static void load_e100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
389
    const size_t num_bytes = offsetof(e100_eeprom_map, model);
390
    byte_vector_t map_bytes = iface.read_eeprom(E100_EEPROM_ADDR, 0, num_bytes);
391
    e100_eeprom_map map; std::memcpy(&map, &map_bytes[0], map_bytes.size());
392

    
393
    mb_eeprom["vendor"] = boost::lexical_cast<std::string>(uhd::ntohx(map.vendor));
394
    mb_eeprom["device"] = boost::lexical_cast<std::string>(uhd::ntohx(map.device));
395
    mb_eeprom["revision"] = boost::lexical_cast<std::string>(unsigned(map.revision));
396
    mb_eeprom["content"] = boost::lexical_cast<std::string>(unsigned(map.content));
397

    
398
    #define load_e100_string_xx(key) mb_eeprom[#key] = bytes_to_string(iface.read_eeprom( \
399
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), sizeof_member(e100_eeprom_map, key) \
400
    ));
401

    
402
    load_e100_string_xx(model);
403
    load_e100_string_xx(env_var);
404
    load_e100_string_xx(env_setting);
405
    load_e100_string_xx(serial);
406
    load_e100_string_xx(name);
407
}
408

    
409
static void store_e100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){
410

    
411
    if (mb_eeprom.has_key("vendor")) iface.write_eeprom(
412
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, vendor),
413
        to_bytes(uhd::htonx(boost::lexical_cast<boost::uint16_t>(mb_eeprom["vendor"])))
414
    );
415

    
416
    if (mb_eeprom.has_key("device")) iface.write_eeprom(
417
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, device),
418
        to_bytes(uhd::htonx(boost::lexical_cast<boost::uint16_t>(mb_eeprom["device"])))
419
    );
420

    
421
    if (mb_eeprom.has_key("revision")) iface.write_eeprom(
422
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, revision),
423
        byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["revision"]))
424
    );
425

    
426
    if (mb_eeprom.has_key("content")) iface.write_eeprom(
427
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, content),
428
        byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["content"]))
429
    );
430

    
431
    #define store_e100_string_xx(key) if (mb_eeprom.has_key(#key)) iface.write_eeprom( \
432
        E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), \
433
        string_to_bytes(mb_eeprom[#key], sizeof_member(e100_eeprom_map, key)) \
434
    );
435

    
436
    store_e100_string_xx(model);
437
    store_e100_string_xx(env_var);
438
    store_e100_string_xx(env_setting);
439
    store_e100_string_xx(serial);
440
    store_e100_string_xx(name);
441
}
442

    
443
/***********************************************************************
444
 * Implementation of mboard eeprom
445
 **********************************************************************/
446
mboard_eeprom_t::mboard_eeprom_t(void){
447
    /* NOP */
448
}
449

    
450
mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){
451
    switch(map){
452
    case MAP_N100: load_n100(*this, iface); break;
453
    case MAP_B000: load_b000(*this, iface); break;
454
    case MAP_B100: load_b100(*this, iface); break;
455
    case MAP_E100: load_e100(*this, iface); break;
456
    }
457
}
458

    
459
void mboard_eeprom_t::commit(i2c_iface &iface, map_type map) const{
460
    switch(map){
461
    case MAP_N100: store_n100(*this, iface); break;
462
    case MAP_B000: store_b000(*this, iface); break;
463
    case MAP_B100: store_b100(*this, iface); break;
464
    case MAP_E100: store_e100(*this, iface); break;
465
    }
466
}