diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-09-26 18:22:25 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-09-29 10:50:56 -0700 |
commit | 83dde40090e0bbd91c304602cc0e3c365f7878bb (patch) | |
tree | c404968e3d6d35795f331ade6ad3c176f3c1bc8b /host/lib | |
parent | 59736a5bf512db83a6bd7250e14b13c7464770fc (diff) | |
download | uhd-83dde40090e0bbd91c304602cc0e3c365f7878bb.tar.gz uhd-83dde40090e0bbd91c304602cc0e3c365f7878bb.tar.bz2 uhd-83dde40090e0bbd91c304602cc0e3c365f7878bb.zip |
uhd: Changed mboard_eeprom_t interface, refactored MB EEPROM code
- uhd::usrp::mboard_eeprom_t is now simply a map. Its commit() method
has no utility being a public API call, because the user never gets
access to the appropriate I2C object (Minor API breakage)
- The central mboard_eeprom.cpp file was broken up and put into many
smaller compilation units in every device's implementation folder.
- Renamed some of the constants (e.g. B000_* -> USRP1_*, N100_* ->
N200_*)
- Removed the N000_* EEPROM code, because, well, you know, there's no
such device
Diffstat (limited to 'host/lib')
33 files changed, 1013 insertions, 906 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f769417d9..44b1b7c43 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -27,7 +27,6 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gps_ctrl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/fe_connection.cpp diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 66129458c..93177fce3 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -29,6 +29,7 @@ IF(ENABLE_B100) ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mb_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usb_zero_copy_wrapper.cpp ) ENDIF(ENABLE_B100) diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 27af43c48..110c5ff87 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -108,7 +108,8 @@ static device_addrs_t b100_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, B100_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = + b100_impl::get_mb_eeprom(fx2_ctrl); device_addr_t new_addr; new_addr["type"] = "b100"; new_addr["name"] = mb_eeprom["name"]; @@ -283,7 +284,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, B100_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = this->get_mb_eeprom(_fx2_ctrl); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .add_coerced_subscriber(boost::bind(&b100_impl::set_mb_eeprom, this, _1)); @@ -561,10 +562,6 @@ double b100_impl::update_rx_codec_gain(const double gain){ return _codec_ctrl->get_rx_pga_gain('A'); } -void b100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, B100_EEPROM_MAP_KEY); -} - void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ if (type == "rx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_RX_A); if (type == "tx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_A); diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index ba4b3cd90..4faf69b64 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -53,7 +53,6 @@ static const uint32_t B100_TX_ASYNC_SID = 10; static const uint32_t B100_CTRL_MSG_SID = 20; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; -static const std::string B100_EEPROM_MAP_KEY = "B100"; static const size_t B100_MAX_RATE_USB2 = 32000000; // bytes/s #define I2C_ADDR_TX_A (I2C_DEV_EEPROM | 0x4) @@ -105,6 +104,8 @@ public: uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); + static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr); + private: //controllers fifo_ctrl_excelsior::sptr _fifo_ctrl; diff --git a/host/lib/usrp/b100/mb_eeprom.cpp b/host/lib/usrp/b100/mb_eeprom.cpp new file mode 100644 index 000000000..be7137b7b --- /dev/null +++ b/host/lib/usrp/b100/mb_eeprom.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "b100_impl.hpp" +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> + +namespace { + const uint8_t B100_EEPROM_ADDR = 0x50; + + //use char array so we dont need to attribute packed + struct b100_eeprom_map{ + unsigned char _r[220]; + unsigned char revision[2]; + unsigned char product[2]; + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; + }; +} + +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; + +mboard_eeprom_t b100_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface) +{ + mboard_eeprom_t mb_eeprom; + + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface->read_eeprom( + B100_EEPROM_ADDR, + offsetof(b100_eeprom_map, revision), + 2 + ) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface->read_eeprom( + B100_EEPROM_ADDR, + offsetof(b100_eeprom_map, product), + 2 + ) + ); + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface->read_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface->read_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), NAME_MAX_LEN + )); + + return mb_eeprom; +} + +void b100_impl::set_mb_eeprom( + const uhd::usrp::mboard_eeprom_t &mb_eeprom +) { + auto &iface = _fx2_ctrl; + + //parse the revision number + if (mb_eeprom.has_key("revision")) iface->write_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface->write_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the serial + if (mb_eeprom.has_key("serial")) iface->write_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface->write_eeprom( + B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} + diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index 4b9e2de55..f5c6e28b2 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -30,5 +30,6 @@ IF(ENABLE_B200) ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_uart.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_cores.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/b200_mb_eeprom.cpp ) ENDIF(ENABLE_B200) diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp index af79a59fc..4d0f8d937 100644 --- a/host/lib/usrp/b200/b200_image_loader.cpp +++ b/host/lib/usrp/b200/b200_image_loader.cpp @@ -46,8 +46,8 @@ static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t& if(dev_handles.size() > 0){ for(usb_device_handle::sptr dev_handle: dev_handles){ if(dev_handle->firmware_loaded()){ - iface = b200_iface::make(usb_control::make(dev_handle,0)); - eeprom = mboard_eeprom_t(*iface, "B200"); + iface = b200_iface::make(usb_control::make(dev_handle, 0)); + eeprom = b200_impl::get_mb_eeprom(iface); if(user_specified){ if(image_loader_args.args.has_key("serial") and eeprom.get("serial") != image_loader_args.args.get("serial")){ @@ -73,8 +73,10 @@ static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t& std::string err_msg = "Could not resolve given args to a single B2XX device.\n" "Applicable devices:\n"; - for(usb_device_handle::sptr dev_handle: applicable_dev_handles){ - eeprom = mboard_eeprom_t(*b200_iface::make(usb_control::make(dev_handle,0)), "B200"); + for (usb_device_handle::sptr dev_handle: applicable_dev_handles) { + eeprom = b200_impl::get_mb_eeprom( + b200_iface::make(usb_control::make(dev_handle, 0)) + ); err_msg += str(boost::format(" * %s (serial=%s)\n") % B2XX_STR_NAMES.get(get_b200_product(dev_handle, mb_eeprom), "B2XX") % mb_eeprom.get("serial")); @@ -88,7 +90,7 @@ static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t& // No applicable devices found, return empty sptr so we can exit iface.reset(); - mb_eeprom = mboard_eeprom_t(); + mb_eeprom = eeprom; return iface; } diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index a513e1336..134cba208 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -220,7 +220,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed b200_iface::sptr iface = b200_iface::make(control); - const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*iface, "B200"); + const mboard_eeprom_t mb_eeprom = b200_impl::get_mb_eeprom(iface); device_addr_t new_addr; new_addr["type"] = "b200"; @@ -362,7 +362,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_iface, "B200"); + const mboard_eeprom_t mb_eeprom = get_mb_eeprom(_iface); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .add_coerced_subscriber(boost::bind(&b200_impl::set_mb_eeprom, this, _1)); @@ -983,11 +983,6 @@ void b200_impl::check_fpga_compat(void) % compat_major % compat_minor)); } -void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) -{ - mb_eeprom.commit(*_iface, "B200"); -} - /*********************************************************************** * Reference time and clock **********************************************************************/ diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 1c756c56d..a8288df0f 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -124,6 +124,8 @@ public: // uhd::value_error. void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = ""); + static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr); + private: b200_product_t _product; size_t _revision; diff --git a/host/lib/usrp/b200/b200_mb_eeprom.cpp b/host/lib/usrp/b200/b200_mb_eeprom.cpp new file mode 100644 index 000000000..e56e6f2ec --- /dev/null +++ b/host/lib/usrp/b200/b200_mb_eeprom.cpp @@ -0,0 +1,82 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "b200_impl.hpp" +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> + +namespace { + /* On the B200, this field indicates the slave address. From the FX3, this + * address is always 0. */ + static const uint8_t B200_EEPROM_SLAVE_ADDR = 0x04; + + //use char array so we dont need to attribute packed + struct b200_eeprom_map{ + unsigned char _r[220]; + unsigned char revision[2]; + unsigned char product[2]; + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; + }; +} + +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; + +mboard_eeprom_t b200_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface) +{ + mboard_eeprom_t mb_eeprom; + + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface->read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), 2) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface->read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), 2) + ); + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface->read_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface->read_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), NAME_MAX_LEN + )); + + return mb_eeprom; +} + +void b200_impl::set_mb_eeprom(const mboard_eeprom_t &mb_eeprom) +{ + //parse the revision number + if (mb_eeprom.has_key("revision")) _iface->write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) _iface->write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the serial + if (mb_eeprom.has_key("serial")) _iface->write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) _iface->write_eeprom( + B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} + diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt index da77b85dc..5bf7206d0 100644 --- a/host/lib/usrp/e100/CMakeLists.txt +++ b/host/lib/usrp/e100/CMakeLists.txt @@ -34,5 +34,6 @@ IF(ENABLE_E100) ${CMAKE_CURRENT_SOURCE_DIR}/e100_mmap_zero_copy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/fpga_downloader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mb_eeprom.cpp ) ENDIF(ENABLE_E100) diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index ddb21fc35..7ee9dccbd 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -70,7 +70,8 @@ device_addrs_t e100_find(const device_addr_t &hint){ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); try{ i2c_iface::sptr i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*i2c_iface, E100_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = get_mb_eeprom(i2c_iface); + new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = mb_eeprom["serial"]; } @@ -108,7 +109,7 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){ //read the eeprom so we can determine the hardware uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = e100_impl::get_mb_eeprom(dev_i2c_iface); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -132,8 +133,9 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _ignore_cal_file = device_addr.has_key("ignore-cal-file"); _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); - const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr); + const mboard_eeprom_t mb_eeprom = get_mb_eeprom(_dev_i2c_iface); + const std::string default_fpga_file_name = + get_default_e1x0_fpga_image(device_addr); const std::string model = device_addr["model"]; std::string e100_fpga_image; try{ @@ -496,10 +498,6 @@ double e100_impl::update_rx_codec_gain(const double gain){ return _codec_ctrl->get_rx_pga_gain('A'); } -void e100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); -} - void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ if (type == "rx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_RX_DB); if (type == "tx") db_eeprom.store(*_fpga_i2c_ctrl, I2C_ADDR_TX_DB); diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 5f8277dda..04278d757 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -55,7 +55,6 @@ static const uint32_t E100_RX_SID_BASE = 30; static const uint32_t E100_TX_ASYNC_SID = 10; static const uint32_t E100_CTRL_MSG_SID = 20; static const double E100_DEFAULT_CLOCK_RATE = 64e6; -static const std::string E100_EEPROM_MAP_KEY = "E100"; //! load an fpga image from a bin file into the usrp-e fpga extern void e100_load_fpga(const std::string &bin_file); @@ -117,6 +116,7 @@ private: std::vector<boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; double update_rx_codec_gain(const double); //sets A and B at once + uhd::usrp::mboard_eeprom_t get_mb_eeprom(); void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void set_db_eeprom(const std::string &, const uhd::usrp::dboard_eeprom_t &); void update_tick_rate(const double rate); diff --git a/host/lib/usrp/e100/mb_eeprom.cpp b/host/lib/usrp/e100/mb_eeprom.cpp new file mode 100644 index 000000000..926688d41 --- /dev/null +++ b/host/lib/usrp/e100/mb_eeprom.cpp @@ -0,0 +1,103 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "e100_impl.hpp" +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> + +namespace { + + const uint8_t E100_EEPROM_ADDR = 0x51; + + struct e100_eeprom_map{ + uint16_t vendor; + uint16_t device; + unsigned char revision; + unsigned char content; + unsigned char model[8]; + unsigned char env_var[16]; + unsigned char env_setting[64]; + unsigned char serial[10]; + unsigned char name[NAME_MAX_LEN]; + }; + + template <typename T> static const byte_vector_t to_bytes(const T &item){ + return byte_vector_t( + reinterpret_cast<const byte_vector_t::value_type *>(&item), + reinterpret_cast<const byte_vector_t::value_type *>(&item)+sizeof(item) + ); + } +} + +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; + +mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr i2c) +{ + auto &iface = i2c; + uhd::usrp::mboard_eeprom_t mb_eeprom; + +#define sizeof_member(struct_name, member_name) \ + sizeof(reinterpret_cast<struct_name*>(0)->member_name) + + const size_t num_bytes = offsetof(e100_eeprom_map, model); + byte_vector_t map_bytes = iface->read_eeprom(E100_EEPROM_ADDR, 0, num_bytes); + e100_eeprom_map map; std::memcpy(&map, &map_bytes[0], map_bytes.size()); + + mb_eeprom["vendor"] = std::to_string(uhd::ntohx(map.vendor)); + mb_eeprom["device"] = std::to_string(uhd::ntohx(map.device)); + mb_eeprom["revision"] = std::to_string(unsigned(map.revision)); + mb_eeprom["content"] = std::to_string(unsigned(map.content)); + + #define load_e100_string_xx(key) mb_eeprom[#key] = bytes_to_string(iface->read_eeprom( \ + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), sizeof_member(e100_eeprom_map, key) \ + )); + + load_e100_string_xx(model); + load_e100_string_xx(env_var); + load_e100_string_xx(env_setting); + load_e100_string_xx(serial); + load_e100_string_xx(name); + + return mb_eeprom; +} + + +void e100_impl::set_mb_eeprom(const mboard_eeprom_t &mb_eeprom) +{ + auto &iface = _dev_i2c_iface; + + if (mb_eeprom.has_key("vendor")) iface->write_eeprom( + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, vendor), + to_bytes(uhd::htonx(boost::lexical_cast<uint16_t>(mb_eeprom["vendor"]))) + ); + + if (mb_eeprom.has_key("device")) iface->write_eeprom( + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, device), + to_bytes(uhd::htonx(boost::lexical_cast<uint16_t>(mb_eeprom["device"]))) + ); + + if (mb_eeprom.has_key("revision")) iface->write_eeprom( + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, revision), + byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["revision"])) + ); + + if (mb_eeprom.has_key("content")) iface->write_eeprom( + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, content), + byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["content"])) + ); + + #define store_e100_string_xx(key) if (mb_eeprom.has_key(#key)) iface->write_eeprom( \ + E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), \ + string_to_bytes(mb_eeprom[#key], sizeof_member(e100_eeprom_map, key)) \ + ); + + store_e100_string_xx(model); + store_e100_string_xx(env_var); + store_e100_string_xx(env_setting); + store_e100_string_xx(serial); + store_e100_string_xx(name); +} diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp deleted file mode 100644 index f7222426d..000000000 --- a/host/lib/usrp/mboard_eeprom.cpp +++ /dev/null @@ -1,682 +0,0 @@ -// -// Copyright 2010-2013,2015 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 <http://www.gnu.org/licenses/>. -// - -#include <uhd/usrp/mboard_eeprom.hpp> -#include <uhd/types/byte_vector.hpp> -#include <uhd/types/mac_addr.hpp> -#include <uhd/utils/byteswap.hpp> -#include <boost/asio/ip/address_v4.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/lexical_cast.hpp> -#include <algorithm> -#include <iostream> -#include <cstddef> - -using namespace uhd; -using namespace uhd::usrp; - -/*********************************************************************** - * Constants - **********************************************************************/ -static const size_t SERIAL_LEN = 9; -static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN; - -/*********************************************************************** - * Utility functions - **********************************************************************/ - -//! convert a string to a byte vector to write to eeprom -static byte_vector_t string_to_uint16_bytes(const std::string &num_str){ - const uint16_t num = boost::lexical_cast<uint16_t>(num_str); - const byte_vector_t lsb_msb = boost::assign::list_of - (uint8_t(num >> 0))(uint8_t(num >> 8)); - return lsb_msb; -} - -//! convert a byte vector read from eeprom to a string -static std::string uint16_bytes_to_string(const byte_vector_t &bytes){ - const uint16_t num = (uint16_t(bytes.at(0)) << 0) | (uint16_t(bytes.at(1)) << 8); - return (num == 0 or num == 0xffff)? "" : std::to_string(num); -} - -/*********************************************************************** - * Implementation of N100 load/store - **********************************************************************/ -static const uint8_t N100_EEPROM_ADDR = 0x50; - -struct n100_eeprom_map{ - uint16_t hardware; - uint8_t mac_addr[6]; - uint32_t subnet; - uint32_t ip_addr; - uint16_t _pad0; - uint16_t revision; - uint16_t product; - unsigned char _pad1; - unsigned char gpsdo; - unsigned char serial[SERIAL_LEN]; - unsigned char name[NAME_MAX_LEN]; - uint32_t gateway; -}; - -enum n200_gpsdo_type{ - N200_GPSDO_NONE = 0, - N200_GPSDO_INTERNAL = 1, - N200_GPSDO_ONBOARD = 2 -}; - -static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //extract the hardware number - mb_eeprom["hardware"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2) - ); - - //extract the revision number - mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2) - ); - - //extract the product code - mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2) - ); - - //extract the addresses - mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6 - )).to_string(); - - boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes); - mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes); - mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes); - mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - - //gpsdo capabilities - uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0); - switch(n200_gpsdo_type(gpsdo_byte)){ - case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; - case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; - default: mb_eeprom["gpsdo"] = "none"; - } - - //extract the serial - mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN - )); - - //extract the name - mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN - )); - - //Empty serial correction: use the mac address to determine serial. - //Older usrp2 models don't have a serial burned into EEPROM. - //The lower mac address bits will function as the serial number. - if (mb_eeprom["serial"].empty()){ - byte_vector_t mac_addr_bytes = mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes(); - unsigned serial = mac_addr_bytes.at(5) | (unsigned(mac_addr_bytes.at(4) & 0x0f) << 8); - mb_eeprom["serial"] = std::to_string(serial); - } -} - -static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //parse the revision number - if (mb_eeprom.has_key("hardware")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), - string_to_uint16_bytes(mb_eeprom["hardware"]) - ); - - //parse the revision number - if (mb_eeprom.has_key("revision")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), - string_to_uint16_bytes(mb_eeprom["revision"]) - ); - - //parse the product code - if (mb_eeprom.has_key("product")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), - string_to_uint16_bytes(mb_eeprom["product"]) - ); - - //store the addresses - if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), - mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() - ); - - if (mb_eeprom.has_key("ip-addr")){ - byte_vector_t ip_addr_bytes(4); - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes); - } - - if (mb_eeprom.has_key("subnet")){ - byte_vector_t ip_addr_bytes(4); - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes); - } - - if (mb_eeprom.has_key("gateway")){ - byte_vector_t ip_addr_bytes(4); - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes); - } - - //gpsdo capabilities - if (mb_eeprom.has_key("gpsdo")){ - uint8_t gpsdo_byte = N200_GPSDO_NONE; - if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; - if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; - iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); - } - - //store the serial - if (mb_eeprom.has_key("serial")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), - string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) - ); - - //store the name - if (mb_eeprom.has_key("name")) iface.write_eeprom( - N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), - string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) - ); -} - -/*********************************************************************** - * Implementation of X300 load/store - **********************************************************************/ -static const uint8_t X300_EEPROM_ADDR = 0x50; - -struct x300_eeprom_map -{ - //indentifying numbers - unsigned char revision[2]; - unsigned char product[2]; - unsigned char revision_compat[2]; - uint8_t _pad0[2]; - - //all the mac addrs - uint8_t mac_addr0[6]; - uint8_t _pad1[2]; - uint8_t mac_addr1[6]; - uint8_t _pad2[2]; - - //all the IP addrs - uint32_t gateway; - uint32_t subnet[4]; - uint32_t ip_addr[4]; - uint8_t _pad3[16]; - - //names and serials - unsigned char name[NAME_MAX_LEN]; - unsigned char serial[SERIAL_LEN]; -}; - -static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface) -{ - byte_vector_t bytes = iface.read_eeprom(X300_EEPROM_ADDR, 0, sizeof (struct x300_eeprom_map)); - - if (bytes.size() == 0) - { - return; - } - - //extract the revision number - mb_eeprom["revision"] = uint16_bytes_to_string( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, revision), - bytes.begin() + (offsetof(x300_eeprom_map, revision)+2)) - ); - - //extract the revision compat number - mb_eeprom["revision_compat"] = uint16_bytes_to_string( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, revision_compat), - bytes.begin() + (offsetof(x300_eeprom_map, revision_compat)+2)) - ); - - //extract the product code - mb_eeprom["product"] = uint16_bytes_to_string( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, product), - bytes.begin() + (offsetof(x300_eeprom_map, product)+2)) - ); - - //extract the mac addresses - mb_eeprom["mac-addr0"] = mac_addr_t::from_bytes( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, mac_addr0), - bytes.begin() + (offsetof(x300_eeprom_map, mac_addr0)+6)) - ).to_string(); - mb_eeprom["mac-addr1"] = mac_addr_t::from_bytes( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, mac_addr1), - bytes.begin() + (offsetof(x300_eeprom_map, mac_addr1)+6)) - ).to_string(); - - //extract the ip addresses - boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, gateway), - bytes.begin() + (offsetof(x300_eeprom_map, gateway)+4)), - ip_addr_bytes - ); - mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - for (size_t i = 0; i < 4; i++) - { - const std::string n(1, i+'0'); - byte_copy( - byte_vector_t( - bytes.begin() + (offsetof(x300_eeprom_map, ip_addr)+(i*4)), - bytes.begin() + (offsetof(x300_eeprom_map, ip_addr)+(i*4)+4)), - ip_addr_bytes - ); - mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - - byte_copy( - byte_vector_t( - bytes.begin() + (offsetof(x300_eeprom_map, subnet)+(i*4)), - bytes.begin() + (offsetof(x300_eeprom_map, subnet)+(i*4)+4)), - ip_addr_bytes - ); - mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); - } - - //extract the serial - mb_eeprom["serial"] = bytes_to_string( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, serial), - bytes.begin() + (offsetof(x300_eeprom_map, serial)+SERIAL_LEN)) - ); - - //extract the name - mb_eeprom["name"] = bytes_to_string( - byte_vector_t( - bytes.begin() + offsetof(x300_eeprom_map, name), - bytes.begin() + (offsetof(x300_eeprom_map, name)+NAME_MAX_LEN)) - ); -} - -static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface) -{ - //parse the revision number - if (mb_eeprom.has_key("revision")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), - string_to_uint16_bytes(mb_eeprom["revision"]) - ); - - //parse the revision compat number - if (mb_eeprom.has_key("revision_compat")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), - string_to_uint16_bytes(mb_eeprom["revision_compat"]) - ); - - //parse the product code - if (mb_eeprom.has_key("product")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), - string_to_uint16_bytes(mb_eeprom["product"]) - ); - - //store the mac addresses - if (mb_eeprom.has_key("mac-addr0")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), - mac_addr_t::from_string(mb_eeprom["mac-addr0"]).to_bytes() - ); - if (mb_eeprom.has_key("mac-addr1")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), - mac_addr_t::from_string(mb_eeprom["mac-addr1"]).to_bytes() - ); - - //store the ip addresses - byte_vector_t ip_addr_bytes(4); - if (mb_eeprom.has_key("gateway")){ - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), ip_addr_bytes); - } - for (size_t i = 0; i < 4; i++) - { - const std::string n(1, i+'0'); - if (mb_eeprom.has_key("ip-addr"+n)){ - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"+n]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), ip_addr_bytes); - } - - if (mb_eeprom.has_key("subnet"+n)){ - byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"+n]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), ip_addr_bytes); - } - } - - //store the serial - if (mb_eeprom.has_key("serial")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), - string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) - ); - - //store the name - if (mb_eeprom.has_key("name")) iface.write_eeprom( - X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), - string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) - ); -} - -/*********************************************************************** - * Implementation of B000 load/store - **********************************************************************/ -static const uint8_t B000_EEPROM_ADDR = 0x50; -static const size_t B000_SERIAL_LEN = 8; - -//use char array so we dont need to attribute packed -struct b000_eeprom_map{ - unsigned char _r[221]; - unsigned char mcr[4]; - unsigned char name[NAME_MAX_LEN]; - unsigned char serial[B000_SERIAL_LEN]; -}; - -static void load_b000(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //extract the serial - mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial), B000_SERIAL_LEN - )); - - //extract the name - mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name), NAME_MAX_LEN - )); - - //extract master clock rate as a 32-bit uint in Hz - uint32_t master_clock_rate; - const byte_vector_t rate_bytes = iface.read_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), sizeof(master_clock_rate) - ); - std::copy( - rate_bytes.begin(), rate_bytes.end(), //input - reinterpret_cast<uint8_t *>(&master_clock_rate) //output - ); - master_clock_rate = ntohl(master_clock_rate); - if (master_clock_rate > 1e6 and master_clock_rate < 1e9){ - mb_eeprom["mcr"] = std::to_string(master_clock_rate); - } - else mb_eeprom["mcr"] = ""; -} - -static void store_b000(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //store the serial - if (mb_eeprom.has_key("serial")) iface.write_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, serial), - string_to_bytes(mb_eeprom["serial"], B000_SERIAL_LEN) - ); - - //store the name - if (mb_eeprom.has_key("name")) iface.write_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, name), - string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) - ); - - //store the master clock rate as a 32-bit uint in Hz - if (mb_eeprom.has_key("mcr")){ - uint32_t master_clock_rate = uint32_t(std::stod(mb_eeprom["mcr"])); - master_clock_rate = htonl(master_clock_rate); - const byte_vector_t rate_bytes( - reinterpret_cast<const uint8_t *>(&master_clock_rate), - reinterpret_cast<const uint8_t *>(&master_clock_rate) + sizeof(master_clock_rate) - ); - iface.write_eeprom( - B000_EEPROM_ADDR, offsetof(b000_eeprom_map, mcr), rate_bytes - ); - } -} - -/*********************************************************************** - * Implementation of B100 load/store - **********************************************************************/ -static const uint8_t B100_EEPROM_ADDR = 0x50; - -//use char array so we dont need to attribute packed -struct b100_eeprom_map{ - unsigned char _r[220]; - unsigned char revision[2]; - unsigned char product[2]; - unsigned char name[NAME_MAX_LEN]; - unsigned char serial[SERIAL_LEN]; -}; - -static void load_b100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //extract the revision number - mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision), 2) - ); - - //extract the product code - mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product), 2) - ); - - //extract the serial - mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), SERIAL_LEN - )); - - //extract the name - mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), NAME_MAX_LEN - )); -} - -static void store_b100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //parse the revision number - if (mb_eeprom.has_key("revision")) iface.write_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, revision), - string_to_uint16_bytes(mb_eeprom["revision"]) - ); - - //parse the product code - if (mb_eeprom.has_key("product")) iface.write_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, product), - string_to_uint16_bytes(mb_eeprom["product"]) - ); - - //store the serial - if (mb_eeprom.has_key("serial")) iface.write_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, serial), - string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) - ); - - //store the name - if (mb_eeprom.has_key("name")) iface.write_eeprom( - B100_EEPROM_ADDR, offsetof(b100_eeprom_map, name), - string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) - ); -} - -/*********************************************************************** - * Implementation of B200 load/store - **********************************************************************/ -/* On the B200, this field indicates the slave address. From the FX3, this - * address is always 0. */ -static const uint8_t B200_EEPROM_SLAVE_ADDR = 0x04; - -//use char array so we dont need to attribute packed -struct b200_eeprom_map{ - unsigned char _r[220]; - unsigned char revision[2]; - unsigned char product[2]; - unsigned char name[NAME_MAX_LEN]; - unsigned char serial[SERIAL_LEN]; -}; - -static void load_b200(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //extract the revision number - mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), 2) - ); - - //extract the product code - mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), 2) - ); - - //extract the serial - mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), SERIAL_LEN - )); - - //extract the name - mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), NAME_MAX_LEN - )); -} - -static void store_b200(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - //parse the revision number - if (mb_eeprom.has_key("revision")) iface.write_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, revision), - string_to_uint16_bytes(mb_eeprom["revision"]) - ); - - //parse the product code - if (mb_eeprom.has_key("product")) iface.write_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, product), - string_to_uint16_bytes(mb_eeprom["product"]) - ); - - //store the serial - if (mb_eeprom.has_key("serial")) iface.write_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, serial), - string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) - ); - - //store the name - if (mb_eeprom.has_key("name")) iface.write_eeprom( - B200_EEPROM_SLAVE_ADDR, offsetof(b200_eeprom_map, name), - string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) - ); -} -/*********************************************************************** - * Implementation of E100 load/store - **********************************************************************/ -static const uint8_t E100_EEPROM_ADDR = 0x51; - -struct e100_eeprom_map{ - uint16_t vendor; - uint16_t device; - unsigned char revision; - unsigned char content; - unsigned char model[8]; - unsigned char env_var[16]; - unsigned char env_setting[64]; - unsigned char serial[10]; - unsigned char name[NAME_MAX_LEN]; -}; - -template <typename T> static const byte_vector_t to_bytes(const T &item){ - return byte_vector_t( - reinterpret_cast<const byte_vector_t::value_type *>(&item), - reinterpret_cast<const byte_vector_t::value_type *>(&item)+sizeof(item) - ); -} - -#define sizeof_member(struct_name, member_name) \ - sizeof(reinterpret_cast<struct_name*>(0)->member_name) - -static void load_e100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - const size_t num_bytes = offsetof(e100_eeprom_map, model); - byte_vector_t map_bytes = iface.read_eeprom(E100_EEPROM_ADDR, 0, num_bytes); - e100_eeprom_map map; std::memcpy(&map, &map_bytes[0], map_bytes.size()); - - mb_eeprom["vendor"] = std::to_string(uhd::ntohx(map.vendor)); - mb_eeprom["device"] = std::to_string(uhd::ntohx(map.device)); - mb_eeprom["revision"] = std::to_string(unsigned(map.revision)); - mb_eeprom["content"] = std::to_string(unsigned(map.content)); - - #define load_e100_string_xx(key) mb_eeprom[#key] = bytes_to_string(iface.read_eeprom( \ - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), sizeof_member(e100_eeprom_map, key) \ - )); - - load_e100_string_xx(model); - load_e100_string_xx(env_var); - load_e100_string_xx(env_setting); - load_e100_string_xx(serial); - load_e100_string_xx(name); -} - -static void store_e100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ - - if (mb_eeprom.has_key("vendor")) iface.write_eeprom( - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, vendor), - to_bytes(uhd::htonx(boost::lexical_cast<uint16_t>(mb_eeprom["vendor"]))) - ); - - if (mb_eeprom.has_key("device")) iface.write_eeprom( - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, device), - to_bytes(uhd::htonx(boost::lexical_cast<uint16_t>(mb_eeprom["device"]))) - ); - - if (mb_eeprom.has_key("revision")) iface.write_eeprom( - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, revision), - byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["revision"])) - ); - - if (mb_eeprom.has_key("content")) iface.write_eeprom( - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, content), - byte_vector_t(1, boost::lexical_cast<unsigned>(mb_eeprom["content"])) - ); - - #define store_e100_string_xx(key) if (mb_eeprom.has_key(#key)) iface.write_eeprom( \ - E100_EEPROM_ADDR, offsetof(e100_eeprom_map, key), \ - string_to_bytes(mb_eeprom[#key], sizeof_member(e100_eeprom_map, key)) \ - ); - - store_e100_string_xx(model); - store_e100_string_xx(env_var); - store_e100_string_xx(env_setting); - store_e100_string_xx(serial); - store_e100_string_xx(name); -} - -/*********************************************************************** - * Implementation of mboard eeprom - **********************************************************************/ -mboard_eeprom_t::mboard_eeprom_t(void){ - /* NOP */ -} - -mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ - if (which == "N100") load_n100(*this, iface); - if (which == "X300") load_x300(*this, iface); - if (which == "B000") load_b000(*this, iface); - if (which == "B100") load_b100(*this, iface); - if (which == "B200") load_b200(*this, iface); - if (which == "E100") load_e100(*this, iface); -} - -void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ - if (which == "N100") store_n100(*this, iface); - if (which == "X300") store_x300(*this, iface); - if (which == "B000") store_b000(*this, iface); - if (which == "B100") store_b100(*this, iface); - if (which == "B200") store_b200(*this, iface); - if (which == "E100") store_e100(*this, iface); -} diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 6924ba3b0..e212e924a 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -27,6 +27,7 @@ IF(ENABLE_USRP1) ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mb_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/soft_time_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.cpp diff --git a/host/lib/usrp/usrp1/mb_eeprom.cpp b/host/lib/usrp/usrp1/mb_eeprom.cpp new file mode 100644 index 000000000..ba05b6fd0 --- /dev/null +++ b/host/lib/usrp/usrp1/mb_eeprom.cpp @@ -0,0 +1,90 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "usrp1_impl.hpp" +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> + +namespace { + const uint8_t USRP1_EEPROM_ADDR = 0x50; + const size_t USRP1_SERIAL_LEN = 8; + + //use char array so we dont need to attribute packed + struct usrp1_eeprom_map{ + unsigned char _r[221]; + unsigned char mcr[4]; + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[USRP1_SERIAL_LEN]; + }; +} + +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; + +mboard_eeprom_t usrp1_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface) +{ + mboard_eeprom_t mb_eeprom; + + //extract the serial + mb_eeprom["serial"] = uhd::bytes_to_string(iface->read_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, serial), USRP1_SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = uhd::bytes_to_string(iface->read_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, name), NAME_MAX_LEN + )); + + //extract master clock rate as a 32-bit uint in Hz + uint32_t master_clock_rate; + const byte_vector_t rate_bytes = iface->read_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, mcr), sizeof(master_clock_rate) + ); + std::copy( + rate_bytes.begin(), rate_bytes.end(), //input + reinterpret_cast<uint8_t *>(&master_clock_rate) //output + ); + master_clock_rate = ntohl(master_clock_rate); + if (master_clock_rate > 1e6 and master_clock_rate < 1e9){ + mb_eeprom["mcr"] = std::to_string(master_clock_rate); + } + else mb_eeprom["mcr"] = ""; + + return mb_eeprom; +} + +void usrp1_impl::set_mb_eeprom(const mboard_eeprom_t &mb_eeprom) +{ + auto &iface = _fx2_ctrl; + + //store the serial + if (mb_eeprom.has_key("serial")) iface->write_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], USRP1_SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface->write_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); + + //store the master clock rate as a 32-bit uint in Hz + if (mb_eeprom.has_key("mcr")){ + uint32_t master_clock_rate = uint32_t(std::stod(mb_eeprom["mcr"])); + master_clock_rate = htonl(master_clock_rate); + const byte_vector_t rate_bytes( + reinterpret_cast<const uint8_t *>(&master_clock_rate), + reinterpret_cast<const uint8_t *>(&master_clock_rate) + + sizeof(master_clock_rate) + ); + iface->write_eeprom( + USRP1_EEPROM_ADDR, offsetof(usrp1_eeprom_map, mcr), rate_bytes + ); + } +} + diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 92b7f5331..17009c6a9 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -111,7 +111,8 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom(*fx2_ctrl, USRP1_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = + usrp1_impl::get_mb_eeprom(fx2_ctrl); device_addr_t new_addr; new_addr["type"] = "usrp1"; new_addr["name"] = mb_eeprom["name"]; @@ -218,7 +219,8 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); + //const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); + const mboard_eeprom_t mb_eeprom = this->get_mb_eeprom(_fx2_ctrl); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .add_coerced_subscriber(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1)); @@ -452,10 +454,6 @@ bool usrp1_impl::has_tx_halfband(void){ /*********************************************************************** * Properties callback methods below **********************************************************************/ -void usrp1_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); -} - void usrp1_impl::set_db_eeprom(const std::string &db, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ if (type == "rx") db_eeprom.store(*_fx2_ctrl, (db == "A")? (I2C_ADDR_RX_A) : (I2C_ADDR_RX_B)); if (type == "tx") db_eeprom.store(*_fx2_ctrl, (db == "A")? (I2C_ADDR_TX_A) : (I2C_ADDR_TX_B)); diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index b45d138d1..ca192f907 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -84,6 +84,8 @@ public: uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); + static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr); + private: //controllers uhd::usrp::fx2_ctrl::sptr _fx2_ctrl; diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index edf77a654..a5aaf3eeb 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -28,6 +28,7 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mb_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp diff --git a/host/lib/usrp/usrp2/mb_eeprom.cpp b/host/lib/usrp/usrp2/mb_eeprom.cpp new file mode 100644 index 000000000..5b86633d6 --- /dev/null +++ b/host/lib/usrp/usrp2/mb_eeprom.cpp @@ -0,0 +1,175 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "usrp2_impl.hpp" +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> +#include <uhd/types/mac_addr.hpp> +#include <boost/asio/ip/address_v4.hpp> + +namespace { + const uint8_t N200_EEPROM_ADDR = 0x50; + + struct n200_eeprom_map{ + uint16_t hardware; + uint8_t mac_addr[6]; + uint32_t subnet; + uint32_t ip_addr; + uint16_t _pad0; + uint16_t revision; + uint16_t product; + unsigned char _pad1; + unsigned char gpsdo; + unsigned char serial[SERIAL_LEN]; + unsigned char name[NAME_MAX_LEN]; + uint32_t gateway; + }; + + enum n200_gpsdo_type{ + N200_GPSDO_NONE = 0, + N200_GPSDO_INTERNAL = 1, + N200_GPSDO_ONBOARD = 2 + }; +} + +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; + +mboard_eeprom_t usrp2_impl::get_mb_eeprom(usrp2_iface &iface) +{ + uhd::usrp::mboard_eeprom_t mb_eeprom; + + //extract the hardware number + mb_eeprom["hardware"] = uint16_bytes_to_string( + iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, hardware), 2) + ); + + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, revision), 2) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, product), 2) + ); + + //extract the addresses + mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, mac_addr), 6 + )).to_string(); + + boost::asio::ip::address_v4::bytes_type ip_addr_bytes; + byte_copy(iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, ip_addr), 4), ip_addr_bytes); + mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, subnet), 4), ip_addr_bytes); + mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + //gpsdo capabilities + uint8_t gpsdo_byte = iface.read_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, gpsdo), 1).at(0); + switch(n200_gpsdo_type(gpsdo_byte)){ + case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; + case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; + default: mb_eeprom["gpsdo"] = "none"; + } + + //extract the serial + mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, serial), SERIAL_LEN + )); + + //extract the name + mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, name), NAME_MAX_LEN + )); + + //Empty serial correction: use the mac address to determine serial. + //Older usrp2 models don't have a serial burned into EEPROM. + //The lower mac address bits will function as the serial number. + if (mb_eeprom["serial"].empty()){ + byte_vector_t mac_addr_bytes = mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes(); + unsigned serial = mac_addr_bytes.at(5) | (unsigned(mac_addr_bytes.at(4) & 0x0f) << 8); + mb_eeprom["serial"] = std::to_string(serial); + } + + return mb_eeprom; +} + + +void usrp2_impl::set_mb_eeprom( + const std::string &mb, + const mboard_eeprom_t &mb_eeprom +) { + auto &iface = _mbc[mb].iface; + + //parse the revision number + if (mb_eeprom.has_key("hardware")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, hardware), + string_to_uint16_bytes(mb_eeprom["hardware"]) + ); + + //parse the revision number + if (mb_eeprom.has_key("revision")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the addresses + if (mb_eeprom.has_key("mac-addr")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, mac_addr), + mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() + ); + + if (mb_eeprom.has_key("ip-addr")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, ip_addr), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, subnet), ip_addr_bytes); + } + + if (mb_eeprom.has_key("gateway")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, gateway), ip_addr_bytes); + } + + //gpsdo capabilities + if (mb_eeprom.has_key("gpsdo")){ + uint8_t gpsdo_byte = N200_GPSDO_NONE; + if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; + if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; + iface->write_eeprom(N200_EEPROM_ADDR, offsetof(n200_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); + } + + //store the serial + if (mb_eeprom.has_key("serial")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface->write_eeprom( + N200_EEPROM_ADDR, offsetof(n200_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); +} + diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index ce547bad0..46e4198b0 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -83,7 +83,7 @@ public: throw uhd::runtime_error("firmware not responding"); _protocol_compat = ntohl(ctrl_data.proto_ver); - mb_eeprom = mboard_eeprom_t(*this, USRP2_EEPROM_MAP_KEY); + mb_eeprom = usrp2_impl::get_mb_eeprom(*this); } ~usrp2_iface_impl(void){UHD_SAFE_CALL( diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 573314339..90b949759 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -473,7 +473,8 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) : //////////////////////////////////////////////////////////////// _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(_mbc[mb].iface->mb_eeprom) - .add_coerced_subscriber(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1)); + .add_coerced_subscriber( + boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1)); //////////////////////////////////////////////////////////////// // create clock control objects @@ -792,10 +793,6 @@ usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL( } )} -void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*(_mbc[mb].iface), USRP2_EEPROM_MAP_KEY); -} - void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ if (type == "rx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_RX_DB); if (type == "tx") db_eeprom.store(*_mbc[mb].iface, USRP2_I2C_ADDR_TX_DB); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 087a4f8e9..e3cd5628e 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -55,7 +55,6 @@ static const size_t mimo_clock_sync_delay_cycles = 138; static const size_t USRP2_SRAM_BYTES = size_t(1 << 20); static const uint32_t USRP2_TX_ASYNC_SID = 2; static const uint32_t USRP2_RX_SID_BASE = 3; -static const std::string USRP2_EEPROM_MAP_KEY = "N100"; uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_); @@ -82,6 +81,8 @@ public: uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); + static uhd::usrp::mboard_eeprom_t get_mb_eeprom(usrp2_iface &); + private: struct mb_container_type{ usrp2_iface::sptr iface; diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index 5f8e83e1c..3a48c9900 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -34,6 +34,7 @@ IF(ENABLE_X300) ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_eeprom_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_mb_eeprom.cpp ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c ) diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 1e22f7fb7..f98bdb5b1 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -18,7 +18,7 @@ #include "x300_impl.hpp" #include "x300_lvbitx.hpp" #include "x310_lvbitx.hpp" -#include "x300_mb_eeprom.hpp" +#include "x300_mb_eeprom_iface.hpp" #include "apply_corrections.hpp" #include <boost/algorithm/string.hpp> #include <boost/asio.hpp> @@ -125,7 +125,8 @@ static device_addrs_t x300_find_with_addr(const device_addr_t &hint) i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); x300_mb_eeprom_iface::sptr eeprom_iface = x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c); - const mboard_eeprom_t mb_eeprom(*eeprom_iface, "X300"); + const mboard_eeprom_t mb_eeprom = + x300_impl::get_mb_eeprom(eeprom_iface); if (mb_eeprom.size() == 0 or x300_impl::claim_status(zpu_ctrl) == x300_impl::CLAIMED_BY_OTHER) { // Skip device claimed by another process @@ -233,7 +234,8 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu i2c_core_100_wb32::sptr zpu_i2c = i2c_core_100_wb32::make(zpu_ctrl, I2C1_BASE); x300_mb_eeprom_iface::sptr eeprom_iface = x300_mb_eeprom_iface::make(zpu_ctrl, zpu_i2c); - const mboard_eeprom_t mb_eeprom(*eeprom_iface, "X300"); + const mboard_eeprom_t mb_eeprom = + x300_impl::get_mb_eeprom(eeprom_iface); if (mb_eeprom.size() == 0 or x300_impl::claim_status(zpu_ctrl) == x300_impl::CLAIMED_BY_OTHER) { // Skip device claimed by another process @@ -783,13 +785,21 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) x300_mb_eeprom_iface::sptr eeprom16 = x300_mb_eeprom_iface::make(mb.zpu_ctrl, mb.zpu_i2c); if (dev_addr.has_key("blank_eeprom")) { - UHD_LOGGER_WARNING("X300") << "Obliterating the motherboard EEPROM..." ; + UHD_LOGGER_WARNING("X300") << "Obliterating the motherboard EEPROM..."; eeprom16->write_eeprom(0x50, 0, byte_vector_t(256, 0xff)); } - const mboard_eeprom_t mb_eeprom(*eeprom16, "X300"); + + const mboard_eeprom_t mb_eeprom = get_mb_eeprom(eeprom16); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") + // Initialize the property with a current copy of the EEPROM contents .set(mb_eeprom) - .add_coerced_subscriber(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); + // Whenever this property is written, update the chip + .add_coerced_subscriber( + [this, eeprom16](const mboard_eeprom_t &mb_eeprom){ + this->set_mb_eeprom(eeprom16, mb_eeprom); + } + ) + ; bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); if (recover_mb_eeprom) { @@ -1524,16 +1534,6 @@ bool x300_impl::is_pps_present(mboard_members_t& mb) } /*********************************************************************** - * eeprom - **********************************************************************/ - -void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eeprom) -{ - i2c_iface::sptr eeprom16 = i2c->eeprom16(); - mb_eeprom.commit(*eeprom16, "X300"); -} - -/*********************************************************************** * claimer logic **********************************************************************/ diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 7186e5f4f..a1ff65a5c 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -147,6 +147,9 @@ public: static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port); static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom); + //! Read out the on-board EEPROM, convert to dict, and return + static uhd::usrp::mboard_eeprom_t get_mb_eeprom(uhd::i2c_iface::sptr i2c); + protected: void subdev_to_blockid( const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i, @@ -287,7 +290,11 @@ private: bool wait_for_clk_locked(mboard_members_t& mb, uint32_t which, double timeout); bool is_pps_present(mboard_members_t& mb); - void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); + //! Write the contents of an EEPROM dict to the on-board EEPROM + void set_mb_eeprom( + uhd::i2c_iface::sptr i2c, + const uhd::usrp::mboard_eeprom_t & + ); void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members); diff --git a/host/lib/usrp/x300/x300_mb_eeprom.cpp b/host/lib/usrp/x300/x300_mb_eeprom.cpp index 084685991..da4cf6021 100644 --- a/host/lib/usrp/x300/x300_mb_eeprom.cpp +++ b/host/lib/usrp/x300/x300_mb_eeprom.cpp @@ -1,190 +1,198 @@ // -// Copyright 2013-2016 Ettus Research LLC +// Copyright 2017 Ettus Research (National Instruments Corp.) // -// 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 <http://www.gnu.org/licenses/>. +// SPDX-License-Identifier: GPL-3.0 // -/* - * x300_mb_eeprom_iface - * This interface was created to prevent MB EEPROM corruption while reading - * data during discovery. For devices with firmware version newer than 5.0, - * the EEPROM data is read into firmware memory and available without - * claiming the device. For devices with firmware versions 5.0 and older, - * the code makes sure to claim the device before driving the I2C bus. This - * has the unfortunate side effect of preventing multiple processes from - * discovering the device simultaneously, but is far better than having EEPROM - * corruption. - */ - -#include "x300_mb_eeprom.hpp" -#include "x300_fw_common.h" -#include "x300_regs.hpp" #include "x300_impl.hpp" -#include <uhd/exception.hpp> -#include <uhd/utils/platform.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/byteswap.hpp> -#include <boost/thread.hpp> +#include "eeprom_utils.hpp" +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/serial.hpp> -using namespace uhd; +namespace { + const uint8_t X300_EEPROM_ADDR = 0x50; + + struct x300_eeprom_map + { + //identifying numbers + unsigned char revision[2]; + unsigned char product[2]; + unsigned char revision_compat[2]; + uint8_t _pad0[2]; + + //all the mac addrs + uint8_t mac_addr0[6]; + uint8_t _pad1[2]; + uint8_t mac_addr1[6]; + uint8_t _pad2[2]; + + //all the IP addrs + uint32_t gateway; + uint32_t subnet[4]; + uint32_t ip_addr[4]; + uint8_t _pad3[16]; + + //names and serials + unsigned char name[NAME_MAX_LEN]; + unsigned char serial[SERIAL_LEN]; + }; +} -static const uint32_t X300_FW_SHMEM_IDENT_MIN_VERSION = 0x50001; +using namespace uhd; +using uhd::usrp::mboard_eeprom_t; -class x300_mb_eeprom_iface_impl : public x300_mb_eeprom_iface +mboard_eeprom_t x300_impl::get_mb_eeprom(uhd::i2c_iface::sptr iface) { -public: + byte_vector_t bytes = + iface->read_eeprom(X300_EEPROM_ADDR, 0, sizeof(struct x300_eeprom_map)); - x300_mb_eeprom_iface_impl(wb_iface::sptr wb, i2c_iface::sptr i2c) : _wb(wb), _i2c(i2c) - { - _compat_num = _wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_COMPAT_NUM)); + mboard_eeprom_t mb_eeprom; + if (bytes.empty()) { + return mb_eeprom; } - ~x300_mb_eeprom_iface_impl() + //extract the revision number + mb_eeprom["revision"] = uint16_bytes_to_string( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, revision), + bytes.begin() + (offsetof(x300_eeprom_map, revision)+2)) + ); + + //extract the revision compat number + mb_eeprom["revision_compat"] = uint16_bytes_to_string( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, revision_compat), + bytes.begin() + (offsetof(x300_eeprom_map, revision_compat)+2)) + ); + + //extract the product code + mb_eeprom["product"] = uint16_bytes_to_string( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, product), + bytes.begin() + (offsetof(x300_eeprom_map, product)+2)) + ); + + //extract the mac addresses + mb_eeprom["mac-addr0"] = mac_addr_t::from_bytes( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, mac_addr0), + bytes.begin() + (offsetof(x300_eeprom_map, mac_addr0)+6)) + ).to_string(); + mb_eeprom["mac-addr1"] = mac_addr_t::from_bytes( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, mac_addr1), + bytes.begin() + (offsetof(x300_eeprom_map, mac_addr1)+6)) + ).to_string(); + + //extract the ip addresses + boost::asio::ip::address_v4::bytes_type ip_addr_bytes; + byte_copy( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, gateway), + bytes.begin() + (offsetof(x300_eeprom_map, gateway)+4)), + ip_addr_bytes + ); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + for (size_t i = 0; i < 4; i++) { - /* NOP */ + const std::string n(1, i+'0'); + byte_copy( + byte_vector_t( + bytes.begin() + (offsetof(x300_eeprom_map, ip_addr)+(i*4)), + bytes.begin() + (offsetof(x300_eeprom_map, ip_addr)+(i*4)+4)), + ip_addr_bytes + ); + mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy( + byte_vector_t( + bytes.begin() + (offsetof(x300_eeprom_map, subnet)+(i*4)), + bytes.begin() + (offsetof(x300_eeprom_map, subnet)+(i*4)+4)), + ip_addr_bytes + ); + mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); } - /*! - * Write bytes over the i2c. - * \param addr the address - * \param buf the vector of bytes - */ - void write_i2c( - uint16_t addr, - const byte_vector_t &buf - ) - { - UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); - if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) - { - throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); - } - _i2c->write_i2c(addr, buf); - } + //extract the serial + mb_eeprom["serial"] = bytes_to_string( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, serial), + bytes.begin() + (offsetof(x300_eeprom_map, serial)+SERIAL_LEN)) + ); + + //extract the name + mb_eeprom["name"] = bytes_to_string( + byte_vector_t( + bytes.begin() + offsetof(x300_eeprom_map, name), + bytes.begin() + (offsetof(x300_eeprom_map, name)+NAME_MAX_LEN)) + ); + + return mb_eeprom; +} - /*! - * Read bytes over the i2c. - * \param addr the address - * \param num_bytes number of bytes to read - * \return a vector of bytes - */ - byte_vector_t read_i2c( - uint16_t addr, - size_t num_bytes - ) - { - UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); - byte_vector_t bytes; - if (_compat_num > X300_FW_SHMEM_IDENT_MIN_VERSION) - { - bytes = read_eeprom(addr, 0, num_bytes); - } else { - x300_impl::claim_status_t status = x300_impl::claim_status(_wb); - // Claim device before driving the I2C bus - if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) - { - bytes = _i2c->read_i2c(addr, num_bytes); - if (status != x300_impl::CLAIMED_BY_US) - { - // We didn't originally have the claim, so give it up - x300_impl::release(_wb); - } - } - } - return bytes; - } - /*! - * Write bytes to an eeprom. - * \param addr the address - * \param offset byte offset - * \param buf the vector of bytes - */ - void write_eeprom( - uint16_t addr, - uint16_t offset, - const byte_vector_t &buf - ) +void x300_impl::set_mb_eeprom( + i2c_iface::sptr iface, + const mboard_eeprom_t &mb_eeprom +) { + //parse the revision number + if (mb_eeprom.has_key("revision")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), + string_to_uint16_bytes(mb_eeprom["revision"]) + ); + + //parse the revision compat number + if (mb_eeprom.has_key("revision_compat")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), + string_to_uint16_bytes(mb_eeprom["revision_compat"]) + ); + + //parse the product code + if (mb_eeprom.has_key("product")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), + string_to_uint16_bytes(mb_eeprom["product"]) + ); + + //store the mac addresses + if (mb_eeprom.has_key("mac-addr0")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr0), + mac_addr_t::from_string(mb_eeprom["mac-addr0"]).to_bytes() + ); + if (mb_eeprom.has_key("mac-addr1")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, mac_addr1), + mac_addr_t::from_string(mb_eeprom["mac-addr1"]).to_bytes() + ); + + //store the ip addresses + byte_vector_t ip_addr_bytes(4); + if (mb_eeprom.has_key("gateway")){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, gateway), ip_addr_bytes); + } + for (size_t i = 0; i < 4; i++) { - UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); - if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) - { - throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); + const std::string n(1, i+'0'); + if (mb_eeprom.has_key("ip-addr"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"+n]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, ip_addr)+(i*4), ip_addr_bytes); } - _i2c->write_eeprom(addr, offset, buf); - } - /*! - * Read bytes from an eeprom. - * \param addr the address - * \param offset byte offset - * \param num_bytes number of bytes to read - * \return a vector of bytes - */ - byte_vector_t read_eeprom( - uint16_t addr, - uint16_t offset, - size_t num_bytes - ) - { - UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); - byte_vector_t bytes; - x300_impl::claim_status_t status = x300_impl::claim_status(_wb); - if (_compat_num >= X300_FW_SHMEM_IDENT_MIN_VERSION) - { - // Get MB EEPROM data from firmware memory - if (num_bytes == 0) return bytes; - - size_t bytes_read = 0; - for (size_t word = offset / 4; bytes_read < num_bytes; word++) - { - uint32_t value = byteswap(_wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_IDENT + word))); - for (size_t byte = offset % 4; byte < 4 and bytes_read < num_bytes; byte++) - { - bytes.push_back(uint8_t((value >> (byte * 8)) & 0xff)); - bytes_read++; - } - } - } else { - // Claim device before driving the I2C bus - if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) - { - bytes = _i2c->read_eeprom(addr, offset, num_bytes); - if (status != x300_impl::CLAIMED_BY_US) - { - // We didn't originally have the claim, so give it up - x300_impl::release(_wb); - } - } + if (mb_eeprom.has_key("subnet"+n)){ + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"+n]).to_bytes(), ip_addr_bytes); + iface->write_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, subnet)+(i*4), ip_addr_bytes); } - return bytes; } - -private: - wb_iface::sptr _wb; - i2c_iface::sptr _i2c; - uint32_t _compat_num; -}; - -x300_mb_eeprom_iface::~x300_mb_eeprom_iface(void) -{ - /* NOP */ -} - -x300_mb_eeprom_iface::sptr x300_mb_eeprom_iface::make(wb_iface::sptr wb, i2c_iface::sptr i2c) -{ - return boost::make_shared<x300_mb_eeprom_iface_impl>(wb, i2c->eeprom16()); + //store the serial + if (mb_eeprom.has_key("serial")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, serial), + string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) + ); + + //store the name + if (mb_eeprom.has_key("name")) iface->write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, name), + string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) + ); } diff --git a/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp new file mode 100644 index 000000000..adc766c39 --- /dev/null +++ b/host/lib/usrp/x300/x300_mb_eeprom_iface.cpp @@ -0,0 +1,190 @@ +// +// Copyright 2013-2016 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 <http://www.gnu.org/licenses/>. +// + +/* + * x300_mb_eeprom_iface + * This interface was created to prevent MB EEPROM corruption while reading + * data during discovery. For devices with firmware version newer than 5.0, + * the EEPROM data is read into firmware memory and available without + * claiming the device. For devices with firmware versions 5.0 and older, + * the code makes sure to claim the device before driving the I2C bus. This + * has the unfortunate side effect of preventing multiple processes from + * discovering the device simultaneously, but is far better than having EEPROM + * corruption. + */ + +#include "x300_mb_eeprom_iface.hpp" +#include "x300_fw_common.h" +#include "x300_regs.hpp" +#include "x300_impl.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/platform.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/thread.hpp> + +using namespace uhd; + +static const uint32_t X300_FW_SHMEM_IDENT_MIN_VERSION = 0x50001; + +class x300_mb_eeprom_iface_impl : public x300_mb_eeprom_iface +{ +public: + + x300_mb_eeprom_iface_impl(wb_iface::sptr wb, i2c_iface::sptr i2c) : _wb(wb), _i2c(i2c) + { + _compat_num = _wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_COMPAT_NUM)); + } + + ~x300_mb_eeprom_iface_impl() + { + /* NOP */ + } + + /*! + * Write bytes over the i2c. + * \param addr the address + * \param buf the vector of bytes + */ + void write_i2c( + uint16_t addr, + const byte_vector_t &buf + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) + { + throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); + } + _i2c->write_i2c(addr, buf); + } + + /*! + * Read bytes over the i2c. + * \param addr the address + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + byte_vector_t read_i2c( + uint16_t addr, + size_t num_bytes + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + byte_vector_t bytes; + if (_compat_num > X300_FW_SHMEM_IDENT_MIN_VERSION) + { + bytes = read_eeprom(addr, 0, num_bytes); + } else { + x300_impl::claim_status_t status = x300_impl::claim_status(_wb); + // Claim device before driving the I2C bus + if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) + { + bytes = _i2c->read_i2c(addr, num_bytes); + if (status != x300_impl::CLAIMED_BY_US) + { + // We didn't originally have the claim, so give it up + x300_impl::release(_wb); + } + } + } + return bytes; + } + + /*! + * Write bytes to an eeprom. + * \param addr the address + * \param offset byte offset + * \param buf the vector of bytes + */ + void write_eeprom( + uint16_t addr, + uint16_t offset, + const byte_vector_t &buf + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + if (x300_impl::claim_status(_wb) != x300_impl::CLAIMED_BY_US) + { + throw uhd::io_error("Attempted to write MB EEPROM without claim to device."); + } + _i2c->write_eeprom(addr, offset, buf); + } + + /*! + * Read bytes from an eeprom. + * \param addr the address + * \param offset byte offset + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + byte_vector_t read_eeprom( + uint16_t addr, + uint16_t offset, + size_t num_bytes + ) + { + UHD_ASSERT_THROW(addr == MBOARD_EEPROM_ADDR); + byte_vector_t bytes; + x300_impl::claim_status_t status = x300_impl::claim_status(_wb); + if (_compat_num >= X300_FW_SHMEM_IDENT_MIN_VERSION) + { + // Get MB EEPROM data from firmware memory + if (num_bytes == 0) return bytes; + + size_t bytes_read = 0; + for (size_t word = offset / 4; bytes_read < num_bytes; word++) + { + uint32_t value = byteswap(_wb->peek32(X300_FW_SHMEM_ADDR(X300_FW_SHMEM_IDENT + word))); + for (size_t byte = offset % 4; byte < 4 and bytes_read < num_bytes; byte++) + { + bytes.push_back(uint8_t((value >> (byte * 8)) & 0xff)); + bytes_read++; + } + } + } else { + // Claim device before driving the I2C bus + if (status == x300_impl::CLAIMED_BY_US or x300_impl::try_to_claim(_wb)) + { + bytes = _i2c->read_eeprom(addr, offset, num_bytes); + if (status != x300_impl::CLAIMED_BY_US) + { + // We didn't originally have the claim, so give it up + x300_impl::release(_wb); + } + } + } + return bytes; + } + + +private: + wb_iface::sptr _wb; + i2c_iface::sptr _i2c; + uint32_t _compat_num; +}; + +x300_mb_eeprom_iface::~x300_mb_eeprom_iface(void) +{ + /* NOP */ +} + +x300_mb_eeprom_iface::sptr x300_mb_eeprom_iface::make(wb_iface::sptr wb, i2c_iface::sptr i2c) +{ + return boost::make_shared<x300_mb_eeprom_iface_impl>(wb, i2c->eeprom16()); +} + diff --git a/host/lib/usrp/x300/x300_mb_eeprom.hpp b/host/lib/usrp/x300/x300_mb_eeprom_iface.hpp index 0649855c6..0649855c6 100644 --- a/host/lib/usrp/x300/x300_mb_eeprom.hpp +++ b/host/lib/usrp/x300/x300_mb_eeprom_iface.hpp diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 0b163c35f..a9e3ae89a 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -19,6 +19,8 @@ # This file included, use CMake directory variables ######################################################################## +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + ######################################################################## # Setup defines for process scheduling ######################################################################## @@ -168,6 +170,7 @@ SET_SOURCE_FILES_PROPERTIES( ######################################################################## LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/csv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/eeprom_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gain_group.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ihex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp diff --git a/host/lib/utils/eeprom_utils.cpp b/host/lib/utils/eeprom_utils.cpp new file mode 100644 index 000000000..acf81a1ce --- /dev/null +++ b/host/lib/utils/eeprom_utils.cpp @@ -0,0 +1,22 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include "eeprom_utils.hpp" +#include <boost/lexical_cast.hpp> + +uhd::byte_vector_t string_to_uint16_bytes(const std::string &num_str){ + const uint16_t num = boost::lexical_cast<uint16_t>(num_str); + const std::vector<uint8_t> lsb_msb = { + uint8_t(num >> 0), + uint8_t(num >> 8) + }; + return lsb_msb; +} + +std::string uint16_bytes_to_string(const uhd::byte_vector_t &bytes){ + const uint16_t num = (uint16_t(bytes.at(0)) << 0) | (uint16_t(bytes.at(1)) << 8); + return (num == 0 or num == 0xffff)? "" : std::to_string(num); +} diff --git a/host/lib/utils/eeprom_utils.hpp b/host/lib/utils/eeprom_utils.hpp new file mode 100644 index 000000000..b6ff264fb --- /dev/null +++ b/host/lib/utils/eeprom_utils.hpp @@ -0,0 +1,20 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0 +// + +#include <uhd/types/byte_vector.hpp> +#include <uhd/types/mac_addr.hpp> +#include <boost/asio/ip/address_v4.hpp> +#include <string> +#include <vector> + +static const size_t SERIAL_LEN = 9; +static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN; + +//! convert a string to a byte vector to write to eeprom +uhd::byte_vector_t string_to_uint16_bytes(const std::string &num_str); + +//! convert a byte vector read from eeprom to a string +std::string uint16_bytes_to_string(const uhd::byte_vector_t &bytes); |