//
// Copyright 2013-2014 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 "../../../firmware/usrp3/n230/n230_eeprom.h"
#include
#include
#include
#include
#include
#include //used for htonl and ntohl
#include "n230_eeprom_manager.hpp"
namespace uhd { namespace usrp { namespace n230 {
const double n230_eeprom_manager::UDP_TIMEOUT_IN_SEC = 2.0;
n230_eeprom_manager::n230_eeprom_manager(const std::string& addr):
_seq_num(0)
{
_udp_xport = transport::udp_simple::make_connected(
addr, BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT));
read_mb_eeprom();
}
static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len)
{
std::string out;
for (size_t i = 0; i < max_len; i++) {
if (bytes[i] < 32 or bytes[i] > 127) return out;
out += bytes[i];
}
return out;
}
static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer)
{
byte_vector_t bytes;
const size_t len = std::min(string.size(), max_len);
for (size_t i = 0; i < len; i++){
buffer[i] = string[i];
}
if (len < max_len - 1) buffer[len] = '\0';
}
const mboard_eeprom_t& n230_eeprom_manager::read_mb_eeprom()
{
boost::mutex::scoped_lock lock(_mutex);
//Read EEPROM from device
_transact(N230_FLASH_COMM_CMD_READ_NV_DATA);
const n230_eeprom_map_t* map_ptr = reinterpret_cast(_response.data);
const n230_eeprom_map_t& map = *map_ptr;
uint16_t ver_major = uhd::htonx(map.data_version_major);
uint16_t ver_minor = uhd::htonx(map.data_version_minor);
_mb_eeprom["product"] = boost::lexical_cast(
uhd::htonx(map.hw_product));
_mb_eeprom["revision"] = boost::lexical_cast(
uhd::htonx(map.hw_revision));
//The revision_compat field does not exist in version 1.0
//EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set
//revision_compat = revision
if (ver_major == 1 and ver_minor == 0) {
_mb_eeprom["revision_compat"] = _mb_eeprom["revision"];
} else {
_mb_eeprom["revision_compat"] = boost::lexical_cast(
uhd::htonx(map.hw_revision_compat));
}
_mb_eeprom["serial"] = _bytes_to_string(
map.serial, N230_EEPROM_SERIAL_LEN);
//Extract ethernet info
_mb_eeprom["gateway"] = boost::asio::ip::address_v4(
uhd::htonx(map.gateway)).to_string();
for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) {
const std::string n(1, i+'0');
_mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(
uhd::htonx(map.eth_info[i].ip_addr)).to_string();
_mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(
uhd::htonx(map.eth_info[i].subnet)).to_string();
byte_vector_t mac_addr(map.eth_info[i].mac_addr, map.eth_info[i].mac_addr + 6);
_mb_eeprom["mac-addr"+n] = mac_addr_t::from_bytes(mac_addr).to_string();
}
_mb_eeprom["name"] = _bytes_to_string(
map.user_name, N230_EEPROM_NAME_LEN);
return _mb_eeprom;
}
void n230_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom)
{
boost::mutex::scoped_lock lock(_mutex);
_mb_eeprom = eeprom;
n230_eeprom_map_t* map_ptr = reinterpret_cast(_request.data);
memset(map_ptr, 0xff, sizeof(n230_eeprom_map_t)); //Initialize to erased state
//Read EEPROM from device
_transact(N230_FLASH_COMM_CMD_READ_NV_DATA);
memcpy(map_ptr, _response.data, sizeof(n230_eeprom_map_t));
n230_eeprom_map_t& map = *map_ptr;
// Automatic version upgrade handling
uint16_t old_ver_major = uhd::htonx(map.data_version_major);
uint16_t old_ver_minor = uhd::htonx(map.data_version_minor);
//The revision_compat field does not exist for version 1.0 so force write it
//EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set
//revision_compat = revision for the upgrade
bool force_write_version_compat = (old_ver_major == 1 and old_ver_minor == 0);
map.data_version_major = uhd::htonx(N230_EEPROM_VER_MAJOR);
map.data_version_minor = uhd::htonx(N230_EEPROM_VER_MINOR);
if (_mb_eeprom.has_key("product")) {
map.hw_product = uhd::htonx(
boost::lexical_cast(_mb_eeprom["product"]));
}
if (_mb_eeprom.has_key("revision")) {
map.hw_revision = uhd::htonx(
boost::lexical_cast(_mb_eeprom["revision"]));
}
if (_mb_eeprom.has_key("revision_compat")) {
map.hw_revision_compat = uhd::htonx(
boost::lexical_cast(_mb_eeprom["revision_compat"]));
} else if (force_write_version_compat) {
map.hw_revision_compat = map.hw_revision;
}
if (_mb_eeprom.has_key("serial")) {
_string_to_bytes(_mb_eeprom["serial"], N230_EEPROM_SERIAL_LEN, map.serial);
}
//Push ethernet info
if (_mb_eeprom.has_key("gateway")){
map.gateway = uhd::htonx(
boost::asio::ip::address_v4::from_string(_mb_eeprom["gateway"]).to_ulong());
}
for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) {
const std::string n(1, i+'0');
if (_mb_eeprom.has_key("ip-addr"+n)){
map.eth_info[i].ip_addr = uhd::htonx(
boost::asio::ip::address_v4::from_string(_mb_eeprom["ip-addr"+n]).to_ulong());
}
if (_mb_eeprom.has_key("subnet"+n)){
map.eth_info[i].subnet = uhd::htonx(
boost::asio::ip::address_v4::from_string(_mb_eeprom["subnet"+n]).to_ulong());
}
if (_mb_eeprom.has_key("mac-addr"+n)) {
byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"+n]).to_bytes();
std::copy(mac_addr.begin(), mac_addr.end(), map.eth_info[i].mac_addr);
}
}
//store the name
if (_mb_eeprom.has_key("name")) {
_string_to_bytes(_mb_eeprom["name"], N230_EEPROM_NAME_LEN, map.user_name);
}
//Write EEPROM to device
_transact(N230_FLASH_COMM_CMD_WRITE_NV_DATA);
}
void n230_eeprom_manager::_transact(const boost::uint32_t command)
{
//Load request struct
_request.flags = uhd::htonx(N230_FLASH_COMM_FLAGS_ACK | command);
_request.seq = uhd::htonx(_seq_num++);
//Send request
_flush_xport();
_udp_xport->send(boost::asio::buffer(&_request, sizeof(_request)));
//Recv reply
const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&_response, sizeof(_response)), UDP_TIMEOUT_IN_SEC);
if (nbytes == 0) throw uhd::io_error("n230_eeprom_manager::_transact failure");
//Sanity checks
const size_t flags = uhd::ntohx(_response.flags);
UHD_ASSERT_THROW(nbytes == sizeof(_response));
UHD_ASSERT_THROW(_response.seq == _request.seq);
UHD_ASSERT_THROW(flags & command);
}
void n230_eeprom_manager::_flush_xport()
{
char buff[sizeof(n230_flash_prog_t)] = {};
while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) {
/*NOP*/
}
}
}}}; //namespace