aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/CMakeLists.txt1
-rw-r--r--host/lib/image_loader.cpp87
-rw-r--r--host/lib/usrp/b200/CMakeLists.txt1
-rw-r--r--host/lib/usrp/b200/b200_iface.cpp4
-rw-r--r--host/lib/usrp/b200/b200_iface.hpp2
-rw-r--r--host/lib/usrp/b200/b200_image_loader.cpp125
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp39
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp16
-rw-r--r--host/lib/usrp/e100/e100_impl.cpp34
-rw-r--r--host/lib/usrp/e100/e100_impl.hpp4
-rw-r--r--host/lib/usrp/e100/fpga_downloader.cpp41
-rw-r--r--host/lib/usrp/e300/e300_common.cpp35
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp63
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp8
-rw-r--r--host/lib/usrp/usrp2/CMakeLists.txt15
-rw-r--r--host/lib/usrp/usrp2/n200_image_loader.cpp616
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp10
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.cpp2
-rw-r--r--host/lib/usrp/usrp2/usrp2_impl.hpp3
-rw-r--r--host/lib/usrp/x300/CMakeLists.txt4
-rw-r--r--host/lib/usrp/x300/cdecode.c80
-rw-r--r--host/lib/usrp/x300/cdecode.h36
-rw-r--r--host/lib/usrp/x300/x300_image_loader.cpp402
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp20
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp4
-rw-r--r--host/lib/usrp_clock/octoclock/CMakeLists.txt8
-rw-r--r--host/lib/usrp_clock/octoclock/ihexcvt.cpp250
-rw-r--r--host/lib/usrp_clock/octoclock/ihexcvt.hpp22
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp340
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.hpp2
30 files changed, 2173 insertions, 101 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 3fa8ed22f..3d4ba8a68 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -89,6 +89,7 @@ CONFIGURE_FILE(
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp
${CMAKE_CURRENT_SOURCE_DIR}/device.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp
${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp
${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp
diff --git a/host/lib/image_loader.cpp b/host/lib/image_loader.cpp
new file mode 100644
index 000000000..91dd325dd
--- /dev/null
+++ b/host/lib/image_loader.cpp
@@ -0,0 +1,87 @@
+//
+// Copyright 2014-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 <iostream>
+#include <map>
+#include <utility>
+
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/static.hpp>
+
+namespace fs = boost::filesystem;
+
+typedef std::map<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_map_t;
+typedef std::map<std::string, std::string> string_map_t;
+
+// Nice typedefs for iterating over std::map
+typedef std::pair<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_pair_t;
+typedef std::pair<std::string, std::string> string_pair_t;
+
+UHD_SINGLETON_FCN(loader_fcn_map_t, get_image_loaders);
+UHD_SINGLETON_FCN(string_map_t, get_recovery_strings);
+
+/*
+ * Registration
+ */
+void uhd::image_loader::register_image_loader(const std::string &device_type,
+ const loader_fcn_t &loader_fcn,
+ const std::string &recovery_instructions){
+ UHD_LOGV(always) << "Registering image loader and recovery instructions for "
+ << device_type << std::endl;
+
+ get_image_loaders().insert(loader_fcn_pair_t(device_type, loader_fcn));
+ get_recovery_strings().insert(string_pair_t(device_type, recovery_instructions));
+}
+
+/*
+ * Actual loading
+ */
+bool uhd::image_loader::load(const uhd::image_loader::image_loader_args_t &image_loader_args){
+
+ // If "type=foo" given in args, see if we have an image loader for that
+ if(image_loader_args.args.has_key("type")){
+ std::string type = image_loader_args.args.get("type");
+ if(get_image_loaders().find(type) == get_image_loaders().end()){
+ throw uhd::runtime_error(str(boost::format("There is no image loader registered for given type \"%s\".")
+ % type));
+ }
+ else return get_image_loaders().at(type)(image_loader_args);
+ }
+ else{
+ BOOST_FOREACH(const loader_fcn_pair_t &loader_fcn_pair, get_image_loaders()){
+ if(loader_fcn_pair.second(image_loader_args)) return true;
+ }
+ return false;
+ }
+}
+
+/*
+ * Get recovery instructions for particular device
+ */
+std::string uhd::image_loader::get_recovery_instructions(const std::string &device_type){
+ if(get_recovery_strings().count(device_type) == 0){
+ return "A firmware or FPGA loading process was interrupted by the user. This can leave your device in a non-working state.";
+ }
+ else return get_recovery_strings().at(device_type);
+}
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt
index ce89b5d80..cd8ebcba7 100644
--- a/host/lib/usrp/b200/CMakeLists.txt
+++ b/host/lib/usrp/b200/CMakeLists.txt
@@ -26,6 +26,7 @@ LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)
IF(ENABLE_B200)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp
diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp
index 270d3bb4b..4754a6357 100644
--- a/host/lib/usrp/b200/b200_iface.cpp
+++ b/host/lib/usrp/b200/b200_iface.cpp
@@ -511,7 +511,7 @@ public:
throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str());
}
- boost::uint32_t load_fpga(const std::string filestring) {
+ boost::uint32_t load_fpga(const std::string filestring, bool force) {
boost::uint8_t fx3_state = 0;
boost::uint32_t wait_count;
@@ -522,7 +522,7 @@ public:
hash_type hash = generate_hash(filename);
hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash);
- if (hash == loaded_hash) return 0;
+ if (hash == loaded_hash and !force) return 0;
// Establish default largest possible control request transfer size based on operating USB speed
int transfer_size = VREQ_DEFAULT_SIZE;
diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp
index 1d123439a..0c7ee6b9e 100644
--- a/host/lib/usrp/b200/b200_iface.hpp
+++ b/host/lib/usrp/b200/b200_iface.hpp
@@ -97,7 +97,7 @@ public:
virtual void set_fpga_reset_pin(const bool reset) = 0;
//! load an FPGA image
- virtual boost::uint32_t load_fpga(const std::string filestring) = 0;
+ virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0;
virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0;
diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp
new file mode 100644
index 000000000..87010244c
--- /dev/null
+++ b/host/lib/usrp/b200/b200_image_loader.cpp
@@ -0,0 +1,125 @@
+//
+// Copyright 2014-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 <boost/assign.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "b200_iface.hpp"
+#include "b200_impl.hpp"
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+namespace uhd{
+
+static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t &image_loader_args,
+ mboard_eeprom_t &mb_eeprom,
+ bool user_specified){
+
+ std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args);
+ b200_iface::sptr iface;
+
+ if(dev_handles.size() > 0){
+ BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){
+ if(dev_handle->firmware_loaded()){
+ iface = b200_iface::make(usb_control::make(dev_handle,0));
+ mb_eeprom = mboard_eeprom_t(*iface, "B200");
+ if(user_specified){
+ if(image_loader_args.args.has_key("serial") and
+ mb_eeprom.get("serial") != image_loader_args.args.get("serial")){
+ continue;
+ }
+ if(image_loader_args.args.has_key("name") and
+ mb_eeprom.get("name") != image_loader_args.args.get("name")){
+ continue;
+ }
+ return iface;
+ }
+ else return iface; // Just return first found
+ }
+ }
+ }
+
+ // No applicable devices found, return empty sptr so we can exit
+ iface.reset();
+ mb_eeprom = mboard_eeprom_t();
+ return iface;
+}
+
+static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ if(!image_loader_args.load_fpga)
+ return false;
+
+ bool user_specified = (image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name"));
+
+ // See if a B2x0 with the given args is found
+ mboard_eeprom_t mb_eeprom;
+ b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, user_specified);
+ if(!iface) return false; // No initialized B2x0 found
+
+ std::string fpga_path;
+ if(image_loader_args.fpga_path == ""){
+ /*
+ * Normally, we can auto-generate the FPGA filename from what's in the EEPROM,
+ * but if the applicable value is not in the EEPROM, the user must give a specific
+ * filename for us to use.
+ */
+ std::string product = mb_eeprom.get("product");
+ if(not B2X0_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){
+ if(user_specified){
+ // The user specified a bad device but expects us to know what it is
+ throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename.");
+ }
+ else{
+ return false;
+ }
+ }
+ else{
+ fpga_path = find_image_path(B2X0_FPGA_FILE_NAME.get(get_b200_type(mb_eeprom)));
+ }
+ }
+ else fpga_path = image_loader_args.fpga_path;
+
+ std::cout << boost::format("Unit: USRP %s (%s)")
+ % B2X0_STR_NAMES.get(get_b200_type(mb_eeprom), "B2XX")
+ % mb_eeprom.get("serial")
+ << std::endl;
+
+ iface->load_fpga(fpga_path, true);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_b200_image_loader){
+ std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n"
+ "device, and the firmware/FPGA will be reloaded the next time\n"
+ "UHD uses the device.";
+
+ image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions);
+}
+
+} /* namespace uhd */
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
index 7c672fd46..17086de02 100644
--- a/host/lib/usrp/b200/b200_impl.cpp
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -76,7 +76,7 @@ public:
//! Look up the type of B-Series device we're currently running.
// If the product ID stored in mb_eeprom is invalid, throws a
// uhd::runtime_error.
-static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
+b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
{
if (mb_eeprom["product"].empty()) {
throw uhd::runtime_error("B200: Missing product ID on EEPROM.");
@@ -91,6 +91,21 @@ static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)
return B2X0_PRODUCT_ID[product_id];
}
+std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint)
+{
+ std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;
+
+ if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
+ vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")),
+ uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"))));
+ } else {
+ vid_pid_pair_list = b200_vid_pid_pairs;
+ }
+
+ //find the usrps and load firmware
+ return usb_device_handle::get_device_list(vid_pid_pair_list);
+}
+
static device_addrs_t b200_find(const device_addr_t &hint)
{
device_addrs_t b200_addrs;
@@ -104,28 +119,14 @@ static device_addrs_t b200_find(const device_addr_t &hint)
if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs;
}
- size_t found = 0;
- std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//vid pid pair search list for devices.
-
- if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") {
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")),
- uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid"))));
- } else {
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID));
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID));
- vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID));
- }
-
// Important note:
// The get device list calls are nested inside the for loop.
// This allows the usb guts to decontruct when not in use,
// so that re-enumeration after fw load can occur successfully.
// This requirement is a courtesy of libusb1.0 on windows.
-
- //find the usrps and load firmware
- std::vector<usb_device_handle::sptr> uhd_usb_device_vector = usb_device_handle::get_device_list(vid_pid_pair_list);
-
- BOOST_FOREACH(usb_device_handle::sptr handle, uhd_usb_device_vector) {
+ size_t found = 0;
+ std::vector<usb_device_handle::sptr> b200_device_handles = get_b200_device_handles(hint);
+ BOOST_FOREACH(usb_device_handle::sptr handle, b200_device_handles) {
//extract the firmware path for the b200
std::string b200_fw_image;
try{
@@ -156,7 +157,7 @@ static device_addrs_t b200_find(const device_addr_t &hint)
//search for the device until found or timeout
while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0)
{
- BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid_pid_pair_list))
+ BOOST_FOREACH(usb_device_handle::sptr handle, b200_device_handles)
{
usb_control::sptr control;
try{control = usb_control::make(handle, 0);}
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
index 11c3da0d9..a3c93e22e 100644
--- a/host/lib/usrp/b200/b200_impl.hpp
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -43,6 +43,7 @@
#include <uhd/usrp/gps_ctrl.hpp>
#include <uhd/transport/usb_zero_copy.hpp>
#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/assign.hpp>
#include <boost/weak_ptr.hpp>
#include "recv_packet_demuxer_3000.hpp"
static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8;
@@ -82,9 +83,18 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI
static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040;
static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID);
-/***********************************************************************
- * The B200 Capability Constants
- **********************************************************************/
+/*
+ * VID/PID pairs for all B2xx products
+ */
+static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs =
+ boost::assign::list_of
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID))
+ (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID))
+ ;
+
+b200_type_t get_b200_type(const uhd::usrp::mboard_eeprom_t &mb_eeprom);
+std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint);
//! Implementation guts
class b200_impl : public uhd::device
diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp
index ac419e0e0..6d3c08534 100644
--- a/host/lib/usrp/e100/e100_impl.cpp
+++ b/host/lib/usrp/e100/e100_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2012,2014 Ettus Research LLC
+// Copyright 2010-2012,2014-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
@@ -28,6 +28,7 @@
#include <boost/functional/hash.hpp>
#include <boost/assign/list_of.hpp>
#include <fstream>
+#include <iostream>
#include <ctime>
using namespace uhd;
@@ -45,7 +46,7 @@ namespace fs = boost::filesystem;
/***********************************************************************
* Discovery
**********************************************************************/
-static device_addrs_t e100_find(const device_addr_t &hint){
+device_addrs_t e100_find(const device_addr_t &hint){
device_addrs_t e100_addrs;
//return an empty list of addresses when type is set to non-usrp-e
@@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost
("E110", "usrp_e110_fpga.bin")
;
-/***********************************************************************
- * Structors
- **********************************************************************/
-e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
- _tree = property_tree::make();
- _type = device::USRP;
- _ignore_cal_file = device_addr.has_key("ignore-cal-file");
-
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){
//read the eeprom so we can determine the hardware
- _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);
+ 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);
//determine the model string for this device
const std::string model = device_addr.get("model", mb_eeprom.get("model", ""));
@@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
) % model));
//extract the fpga path and compute hash
- const std::string default_fpga_file_name = model_to_fpga_file_name[model];
+ return model_to_fpga_file_name[model];
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+e100_impl::e100_impl(const uhd::device_addr_t &device_addr){
+ _tree = property_tree::make();
+ _type = device::USRP;
+ _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 std::string model = device_addr["model"];
std::string e100_fpga_image;
try{
e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name));
diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp
index 4efc21427..d00668224 100644
--- a/host/lib/usrp/e100/e100_impl.hpp
+++ b/host/lib/usrp/e100/e100_impl.hpp
@@ -29,6 +29,7 @@
#include "recv_packet_demuxer.hpp"
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
@@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(
e100_codec_ctrl::sptr codec
);
+uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint);
+std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr);
+
/*!
* USRP-E100 implementation guts:
* The implementation details are encapsulated here.
diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp
index c9d77f560..9abde32f7 100644
--- a/host/lib/usrp/e100/fpga_downloader.cpp
+++ b/host/lib/usrp/e100/fpga_downloader.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2010-2011,2014 Ettus Research LLC
+// Copyright 2010-2011,2014-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
@@ -17,8 +17,16 @@
#include <uhd/config.hpp>
#ifdef UHD_DLL_EXPORTS
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
#include <uhd/exception.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e100_impl.hpp"
#else //special case when this file is externally included
#include <stdexcept>
#include <iostream>
@@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){
}
+#ifdef UHD_DLL_EXPORTS
+namespace fs = boost::filesystem;
+
+static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){
+ // Make sure this is an E1x0
+ uhd::device_addrs_t devs = e100_find(uhd::device_addr_t());
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename;
+ if(image_loader_args.fpga_path == ""){
+ fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0]));
+ }
+ else{
+ if(not fs::exists(image_loader_args.fpga_path)){
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ e100_load_fpga(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e100_image_loader){
+ std::string recovery_instructions = "The default FPGA image will be loaded the next time "
+ "UHD uses this device.";
+
+ uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions);
+}
+#endif /* UHD_DLL_EXPORTS */
diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp
index db5b37055..29117e21f 100644
--- a/host/lib/usrp/e300/e300_common.cpp
+++ b/host/lib/usrp/e300/e300_common.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014-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
@@ -14,8 +14,12 @@
// 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/image_loader.hpp>
#include <uhd/utils/msg.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include "e300_impl.hpp"
#include "e300_fifo_config.hpp"
#include "e300_fifo_config.hpp"
@@ -23,6 +27,7 @@
#include <boost/filesystem.hpp>
#include <fstream>
+#include <string>
namespace uhd { namespace usrp { namespace e300 {
@@ -54,6 +59,34 @@ void load_fpga_image(const std::string &path)
UHD_MSG(status) << " done" << std::endl;
}
+static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) {
+ // Make sure this is an E3x0 and we don't want to use anything connected
+ uhd::device_addrs_t devs = e300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ std::string fpga_filename, idle_image; // idle_image never used, just needed for function
+ if(image_loader_args.fpga_path == "") {
+ get_e3x0_fpga_images(devs[0], fpga_filename, idle_image);
+ }
+ else {
+ if(not boost::filesystem::exists(image_loader_args.fpga_path)) {
+ throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.")
+ % image_loader_args.fpga_path));
+ }
+ else fpga_filename = image_loader_args.fpga_path;
+ }
+
+ load_fpga_image(fpga_filename);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_e300_image_loader) {
+ std::string recovery_instructions = "The default FPGA image will be loaded the next "
+ "time UHD uses this device.";
+
+ image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions);
+}
+
}
}}}
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
index de2357100..5e3f4c575 100644
--- a/host/lib/usrp/e300/e300_impl.cpp
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 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
@@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs)
return if_addrs.inet == asio::ip::address_v4::loopback().to_string();
}
-static device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
+device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
{
// handle multi device discovery
device_addrs_t hints = separate_device_addr(multi_dev_hint);
@@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr)
return device::sptr(new e300_impl(device_addr));
}
+// Common code used by e300_impl and e300_image_loader
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr,
+ std::string &fpga_image,
+ std::string &idle_image){
+ const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
+ device_addr["product"]);
+
+ //extract the FPGA path for the e300
+ switch(e300_eeprom_manager::get_mb_type(pid)) {
+ case e300_eeprom_manager::USRP_E310_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E310_FPGA_FILE_NAME));
+ idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::USRP_E300_MB:
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ case e300_eeprom_manager::UNKNOWN:
+ default:
+ UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
+ << std::endl;
+ fpga_image = device_addr.cast<std::string>("fpga",
+ find_image_path(E300_FPGA_FILE_NAME));
+ idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
+ break;
+ }
+}
+
/***********************************************************************
* Structors
**********************************************************************/
@@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
if (_xport_path == AXI) {
_do_not_reload = device_addr.has_key("no_reload_fpga");
if (not _do_not_reload) {
- // Load FPGA image if provided via args
- const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>(
- device_addr["product"]);
-
std::string fpga_image;
-
- //extract the FPGA path for the e300
- switch(e300_eeprom_manager::get_mb_type(pid)) {
- case e300_eeprom_manager::USRP_E310_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E310_FPGA_FILE_NAME));
- _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::USRP_E300_MB:
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- case e300_eeprom_manager::UNKNOWN:
- default:
- UHD_MSG(warning) << "Unknown motherboard type, loading e300 image."
- << std::endl;
- fpga_image = device_addr.cast<std::string>("fpga",
- find_image_path(E300_FPGA_FILE_NAME));
- _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME);
- break;
- }
+ get_e3x0_fpga_images(device_addr,
+ fpga_image,
+ _idle_image);
common::load_fpga_image(fpga_image);
}
}
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
index c530a5d72..3ed133489 100644
--- a/host/lib/usrp/e300/e300_impl.hpp
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 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
@@ -20,6 +20,7 @@
#include <uhd/device.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
@@ -28,6 +29,7 @@
#include <uhd/types/sensors.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
+#include <string>
#include "e300_fifo_config.hpp"
#include "radio_ctrl_core_3000.hpp"
#include "rx_frontend_core_200.hpp"
@@ -98,6 +100,10 @@ static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_C
static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX;
static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX;
+uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint);
+void get_e3x0_fpga_images(const uhd::device_addr_t &device_args,
+ std::string &fpga_image,
+ std::string &idle_image);
/*!
* USRP-E300 implementation guts:
diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt
index c6257c7fe..bd302895b 100644
--- a/host/lib/usrp/usrp2/CMakeLists.txt
+++ b/host/lib/usrp/usrp2/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2012,2014 Ettus Research LLC
+# Copyright 2011-2012,2014-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
@@ -25,18 +25,6 @@
LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF)
IF(ENABLE_USRP2)
- ########################################################################
- # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp
- ########################################################################
- FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH)
- STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH})
-
- SET_SOURCE_FILES_PROPERTIES(
- ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
- PROPERTIES COMPILE_DEFINITIONS
- "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\""
- )
-
LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp
@@ -45,5 +33,6 @@ IF(ENABLE_USRP2)
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp
)
ENDIF(ENABLE_USRP2)
diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp
new file mode 100644
index 000000000..ce956c22c
--- /dev/null
+++ b/host/lib/usrp/usrp2/n200_image_loader.cpp
@@ -0,0 +1,616 @@
+//
+// Copyright 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 <cstring>
+#include <iostream>
+#include <fstream>
+
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/assign.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/algorithm/string/erase.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/types/dict.hpp>
+
+#include "fw_common.h"
+#include "usrp2_iface.hpp"
+#include "usrp2_impl.hpp"
+
+typedef boost::asio::ip::address_v4 ip_v4;
+
+namespace fs = boost::filesystem;
+using namespace boost::algorithm;
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+
+#define N200_FLASH_DATA_PACKET_SIZE 256
+#define N200_UDP_FW_UPDATE_PORT 49154
+#define UDP_TIMEOUT 0.5
+
+#define N200_FW_MAX_SIZE_BYTES 31744
+#define N200_PROD_FW_IMAGE_ADDR 0x00300000
+#define N200_SAFE_FW_IMAGE_ADDR 0x003F0000
+
+#define N200_FPGA_MAX_SIZE_BYTES 1572864
+#define N200_PROD_FPGA_IMAGE_ADDR 0x00180000
+#define N200_SAFE_FPGA_IMAGE_ADDR 0x00000000
+
+/*
+ * Packet codes
+ */
+typedef enum {
+ UNKNOWN = ' ',
+
+ N200_QUERY = 'a',
+ N200_ACK = 'A',
+
+ GET_FLASH_INFO_CMD = 'f',
+ GET_FLASH_INFO_ACK = 'F',
+
+ ERASE_FLASH_CMD = 'e',
+ ERASE_FLASH_ACK = 'E',
+
+ CHECK_ERASING_DONE_CMD = 'd',
+ DONE_ERASING_ACK = 'D',
+ NOT_DONE_ERASING_ACK = 'B',
+
+ WRITE_FLASH_CMD = 'w',
+ WRITE_FLASH_ACK = 'W',
+
+ READ_FLASH_CMD = 'r',
+ READ_FLASH_ACK = 'R',
+
+ RESET_CMD = 's',
+ RESET_ACK = 'S',
+
+ GET_HW_REV_CMD = 'v',
+ GET_HW_REV_ACK = 'V',
+} n200_fw_update_id_t;
+
+/*
+ * Mapping revision numbers to names
+ */
+static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of
+ (0, "n2xx") // Is an N-Series, but the EEPROM value is invalid
+ (0xa, "n200_r3")
+ (0x100a, "n200_r4")
+ (0x10a, "n210_r3")
+ (0x110a, "n210_r4")
+;
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t proto_ver;
+ boost::uint32_t id;
+ boost::uint32_t seq;
+ union {
+ boost::uint32_t ip_addr;
+ boost::uint32_t hw_rev;
+ struct {
+ boost::uint32_t flash_addr;
+ boost::uint32_t length;
+ boost::uint8_t data[256];
+ } flash_args;
+ struct {
+ boost::uint32_t sector_size_bytes;
+ boost::uint32_t memory_size_bytes;
+ } flash_info_args;
+ } data;
+} n200_fw_update_data_t;
+
+/*
+ * N-Series burn session
+ */
+typedef struct {
+ bool fw;
+ bool overwrite_safe;
+ bool reset;
+ uhd::device_addr_t dev_addr;
+ std::string burn_type;
+ std::string filepath;
+ boost::uint8_t data_in[udp_simple::mtu];
+ boost::uint32_t size;
+ boost::uint32_t max_size;
+ boost::uint32_t flash_addr;
+ udp_simple::sptr xport;
+} n200_session_t;
+
+/***********************************************************************
+ * uhd::image_loader functionality
+ **********************************************************************/
+
+static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){
+ #ifdef UHD_PLATFORM_WIN32
+ std::string usrp2_card_burner_gui = "\"";
+ const std::string nl = " ^\n ";
+ #else
+ std::string usrp2_card_burner_gui = "sudo \"";
+ const std::string nl = " \\\n ";
+ #endif
+
+ usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py");
+ usrp2_card_burner_gui += "\"";
+
+ if(image_loader_args.load_firmware){
+ usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"")
+ % nl
+ % ((image_loader_args.firmware_path == "")
+ ? find_image_path("usrp2_fw.bin")
+ : image_loader_args.firmware_path));
+ }
+ if(image_loader_args.load_fpga){
+ usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"")
+ % nl
+ % ((image_loader_args.fpga_path == "")
+ ? find_image_path("usrp2_fpga.bin")
+ : image_loader_args.fpga_path));
+ }
+
+ throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n"
+ "Instead, plug the device's SD card into your machine and run this command:\n\n"
+ "%s"
+ ) % usrp2_card_burner_gui));
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport,
+ n200_fw_update_id_t pkt_code,
+ n200_fw_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM);
+ pkt_out->id = htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in,
+ n200_fw_update_id_t pkt_code,
+ size_t len){
+ return (len > offsetof(n200_fw_update_data_t, data) and
+ ntohl(pkt_in->id) == pkt_code);
+}
+
+static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){
+ bool user_specified = image_loader_args.args.has_key("addr") or
+ image_loader_args.args.has_key("serial") or
+ image_loader_args.args.has_key("name");
+
+ uhd::device_addrs_t found = usrp2_find(image_loader_args.args);
+ if(found.size() > 0){
+ uhd::device_addr_t ret = found[0];
+
+ /*
+ * Make sure the device found is an N-Series and not a USRP2. A USRP2
+ * will not respond to this query. If the user supplied specific
+ * arguments that led to a USRP2, throw an error.
+ */
+ udp_simple::sptr rev_xport = udp_simple::make_connected(
+ ret["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)
+ );
+
+ n200_fw_update_data_t pkt_out;
+ boost::uint8_t data_in[udp_simple::mtu];
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in);
+
+ size_t len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in);
+ if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){
+ boost::uint32_t rev = ntohl(pkt_in->data.hw_rev);
+ ret["hw_rev"] = n200_filename_map.get(rev, "n2xx");
+ return ret;
+ }
+ else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.")
+ % ntohl(pkt_in->id)));
+ }
+ else if(user_specified){
+ // At this point, we haven't received any response, so assume it's a USRP2
+ print_usrp2_error(image_loader_args);
+ }
+ }
+
+ return uhd::device_addr_t();
+}
+
+/*
+ * Validate and read firmware image
+ */
+static void n200_validate_firmware_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FW_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[4];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes,4);
+ image_file.close();
+ for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Validate and validate FPGA image
+ */
+static void n200_validate_fpga_image(n200_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ session.size = fs::file_size(session.filepath);
+ session.max_size = N200_FPGA_MAX_SIZE_BYTES;
+
+ if(session.size > session.max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % session.max_size));
+ }
+
+ // File must have proper header
+ std::ifstream image_file(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t test_bytes[63];
+ image_file.seekg(0, std::ios::beg);
+ image_file.read((char*)test_bytes, 63);
+ bool is_good = false;
+ for(int i = 0; i < 63; i++){
+ if(test_bytes[i] == 255) continue;
+ else if(test_bytes[i] == 170 and
+ test_bytes[i+1] == 153){
+ is_good = true;
+ break;
+ }
+ }
+ image_file.close();
+ if(not is_good){
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.")
+ % session.filepath));
+ }
+}
+
+/*
+ * Set up a session for burning an N-Series image. This session info
+ * will be passed into the erase, burn, and verify functions.
+ */
+static void n200_setup_session(n200_session_t &session,
+ const image_loader::image_loader_args_t &image_loader_args,
+ bool fw){
+
+
+ session.fw = fw;
+ session.reset = image_loader_args.args.has_key("reset");
+
+ /*
+ * If no filepath is given, attempt to determine the default image by
+ * querying the device for its revision. If the device has a corrupt
+ * EEPROM or is otherwise unable to provide its revision, this is
+ * impossible, and the user must manually provide a firmware file.
+ */
+ if((session.fw and image_loader_args.firmware_path == "") or
+ image_loader_args.fpga_path == ""){
+ if(session.dev_addr["hw_rev"] == "n2xx"){
+ throw uhd::runtime_error("This device's revision cannot be determined. "
+ "You must manually specify a filepath.");
+ }
+ else{
+ session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin")
+ % erase_tail_copy(session.dev_addr["hw_rev"],3)))
+ : find_image_path(str(boost::format("usrp_%s_fpga.bin")
+ % session.dev_addr["hw_rev"]));
+ }
+ }
+ else{
+ session.filepath = session.fw ? image_loader_args.firmware_path
+ : image_loader_args.fpga_path;
+ }
+ if(session.fw) n200_validate_firmware_image(session);
+ else n200_validate_fpga_image(session);
+
+ session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe");
+ if(session.overwrite_safe){
+ session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR
+ : N200_SAFE_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware safe"
+ : "FPGA safe";
+ }
+ else{
+ session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR
+ : N200_PROD_FPGA_IMAGE_ADDR;
+ session.burn_type = session.fw ? "firmware"
+ : "FPGA";
+ }
+
+ session.xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT));
+}
+
+static void n200_erase_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+
+ // Setting up UDP packet
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr);
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size);
+
+ // Begin erasing
+ size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){
+ std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Did not receive response from device.");
+ }
+
+ // Check for erase completion
+ while(true){
+ len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){
+ std::cout << "successful." << std::endl;
+ break;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+ }
+}
+
+static void n200_write_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Write image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE);
+
+ len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Writing %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r--Writing %s image..failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Writing %s image...successful.")
+ % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_verify_image(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+ const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in);
+ size_t len = 0;
+
+ // Read and verify image
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE];
+ boost::uint32_t current_addr = session.flash_addr;
+ pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE);
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){
+ memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+ memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE);
+
+ pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE);
+ cmp_len = image.gcount();
+
+ len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in);
+ if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){
+ std::cout << boost::format("\r-- Verifying %s image (%d%%)")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::flush;
+
+ if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Failed to verify %s image.")
+ % session.burn_type));
+ }
+ }
+ else if(len < offsetof(n200_fw_update_data_t, data)){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if(ntohl(pkt_in->id) != READ_FLASH_ACK){
+ image.close();
+ std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.")
+ % session.burn_type
+ % int((double(current_addr-session.flash_addr)/double(session.size))*100)
+ << std::endl;
+ throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n")
+ % ntohl(pkt_in->id)));
+ }
+
+ current_addr += N200_FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type
+ << std::endl;
+
+ image.close();
+}
+
+static void n200_reset(n200_session_t &session){
+
+ // UDP receive buffer
+ n200_fw_update_data_t pkt_out;
+
+ // There should be no response
+ std::cout << "-- Resetting device..." << std::flush;
+ size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in);
+ if(len > 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset N200.");
+ }
+ std::cout << "successful." << std::endl;
+}
+
+// n210_r4 -> N210 r4
+static std::string nice_name(const std::string &fw_rev){
+ std::string ret = fw_rev;
+ ret[0] = ::toupper(ret[0]);
+
+ size_t pos = 0;
+ if((pos = fw_rev.find("_")) != std::string::npos){
+ ret[pos] = ' ';
+ }
+
+ return ret;
+}
+
+static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any N2x0 with the given args is found
+ // This will throw if specific args lead to a USRP2
+ n200_session_t session;
+ session.dev_addr = n200_find(image_loader_args);
+ if(session.dev_addr.size() == 0 or (!image_loader_args.load_firmware and !image_loader_args.load_fpga)){
+ return false;
+ }
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)")
+ % nice_name(session.dev_addr.get("hw_rev"))
+ % session.dev_addr.get("serial")
+ % session.dev_addr.get("addr")
+ << std::endl;
+
+ if(image_loader_args.load_firmware){
+ n200_setup_session(session,
+ image_loader_args,
+ true
+ );
+
+ std::cout << "Firmware image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset and !image_loader_args.load_fpga){
+ n200_reset(session);
+ }
+ }
+ if(image_loader_args.load_fpga){
+ n200_setup_session(session,
+ image_loader_args,
+ false
+ );
+
+ std::cout << "FPGA image: " << session.filepath << std::endl;
+
+ n200_erase_image(session);
+ n200_write_image(session);
+ n200_verify_image(session);
+ if(session.reset){
+ n200_reset(session);
+ }
+ }
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_n200_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n"
+ "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index 1d41173f8..2b382ae38 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -387,15 +387,15 @@ public:
//create the burner commands
if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){
- const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string();
- const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
+ const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py");
+ const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);
return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd);
}
else{
const std::string addr = _ctrl_transport->get_recv_addr();
- const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string();
- const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr);
- return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd);
+ const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr);
+ return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd);
}
}
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp
index 1acc1dad3..6073ec1c0 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.cpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.cpp
@@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32;
/***********************************************************************
* Discovery over the udp transport
**********************************************************************/
-static device_addrs_t usrp2_find(const device_addr_t &hint_){
+device_addrs_t usrp2_find(const device_addr_t &hint_){
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
if (hints.size() > 1){
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 701403029..07cd98b4c 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -42,6 +42,7 @@
#include <uhd/transport/vrt_if_packet.hpp>
#include <uhd/transport/udp_simple.hpp>
#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/types/device_addr.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <uhd/usrp/subdev_spec.hpp>
#include <boost/weak_ptr.hpp>
@@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2;
static const boost::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_);
+
//! Make a usrp2 dboard interface.
uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(
uhd::timed_wb_iface::sptr wb_iface,
diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt
index a588f901b..15af44721 100644
--- a/host/lib/usrp/x300/CMakeLists.txt
+++ b/host/lib/usrp/x300/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2013 Ettus Research LLC
+# Copyright 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
@@ -34,5 +34,7 @@ IF(ENABLE_X300)
${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp
${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}/cdecode.c
)
ENDIF(ENABLE_X300)
diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c
new file mode 100644
index 000000000..1d09cbe22
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.c
@@ -0,0 +1,80 @@
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#include "cdecode.h"
+
+int base64_decode_value(char value_in){
+ static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+ static const char decoding_size = sizeof(decoding);
+ value_in -= 43;
+ if ((signed char)value_in < 0 || value_in > decoding_size) return -1;
+ return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in){
+ state_in->step = step_a;
+ state_in->plainchar = 0;
+}
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){
+ const char* codechar = code_in;
+ char* plainchar = plaintext_out;
+ char fragment;
+
+ *plainchar = state_in->plainchar;
+
+ switch (state_in->step){
+ while (1){
+ case step_a:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_a;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar = (fragment & 0x03f) << 2;
+
+ case step_b:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_b;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x030) >> 4;
+ *plainchar = (fragment & 0x00f) << 4;
+ case step_c:
+ do{
+ if (codechar == code_in+length_in)
+ {
+ state_in->step = step_c;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03c) >> 2;
+ *plainchar = (fragment & 0x003) << 6;
+ case step_d:
+ do{
+ if (codechar == code_in+length_in){
+ state_in->step = step_d;
+ state_in->plainchar = *plainchar;
+ return plainchar - plaintext_out;
+ }
+ fragment = (char)base64_decode_value(*codechar++);
+ } while ((signed char)fragment < 0);
+ *plainchar++ |= (fragment & 0x03f);
+ }
+ }
+ /* control should not reach here */
+ return plainchar - plaintext_out;
+}
diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h
new file mode 100644
index 000000000..b8da55aa1
--- /dev/null
+++ b/host/lib/usrp/x300/cdecode.h
@@ -0,0 +1,36 @@
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+typedef enum{
+ step_a, step_b, step_c, step_d
+} base64_decodestep;
+
+typedef struct{
+ base64_decodestep step;
+ char plainchar;
+} base64_decodestate;
+
+void base64_init_decodestate(base64_decodestate* state_in);
+
+int base64_decode_value(char value_in);
+
+size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BASE64_CDECODE_H */
diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp
new file mode 100644
index 000000000..9d92e7932
--- /dev/null
+++ b/host/lib/usrp/x300/x300_image_loader.cpp
@@ -0,0 +1,402 @@
+//
+// Copyright 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 <fstream>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/xml_parser.hpp>
+
+#include <uhd/config.hpp>
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/transport/nirio/niusrprio_session.h>
+#include <uhd/transport/nirio/status.h>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "x300_impl.hpp"
+#include "x300_fw_common.h"
+#include "cdecode.h"
+
+namespace fs = boost::filesystem;
+
+using namespace boost::algorithm;
+using namespace uhd;
+using namespace uhd::transport;
+
+/*
+ * Constants
+ */
+#define X300_FPGA_BIN_SIZE_BYTES 15877916
+#define X300_FPGA_BIT_SIZE_BYTES 15878022
+#define X300_FPGA_PROG_UDP_PORT 49157
+#define X300_FLASH_SECTOR_SIZE 131072
+#define X300_PACKET_SIZE_BYTES 256
+#define X300_FPGA_SECTOR_START 32
+#define X300_MAX_RESPONSE_BYTES 128
+#define UDP_TIMEOUT 3
+#define FPGA_LOAD_TIMEOUT 15
+
+/*
+ * Packet structure
+ */
+typedef struct {
+ boost::uint32_t flags;
+ boost::uint32_t sector;
+ boost::uint32_t index;
+ boost::uint32_t size;
+ union {
+ boost::uint8_t data8[X300_PACKET_SIZE_BYTES];
+ boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2];
+ };
+} x300_fpga_update_data_t;
+
+/*
+ * X-Series burn session
+ */
+typedef struct {
+ bool found;
+ bool ethernet;
+ bool configure; // Reload FPGA after burning to flash (Ethernet only)
+ bool verify; // Device will verify the download along the way (Ethernet only)
+ bool lvbitx;
+ uhd::device_addr_t dev_addr;
+ std::string ip_addr;
+ std::string fpga_type;
+ std::string resource;
+ std::string filepath;
+ std::string rpc_port;
+ boost::uint32_t size;
+ udp_simple::sptr xport;
+ std::vector<char> bitstream; // .bin image extracted from .lvbitx file
+ boost::uint8_t data_in[udp_simple::mtu];
+} x300_session_t;
+
+/*
+ * Extract the .bin image from the given LVBITX file.
+ */
+static void extract_from_lvbitx(x300_session_t &session){
+ boost::property_tree::ptree pt;
+ boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt,
+ boost::property_tree::xml_parser::no_comments |
+ boost::property_tree::xml_parser::trim_whitespace);
+ const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream"));
+ std::vector<char> decoded_bitstream(encoded_bitstream.size());
+
+ base64_decodestate decode_state;
+ base64_init_decodestate(&decode_state);
+ const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(),
+ encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state);
+ decoded_bitstream.resize(decoded_size);
+ session.bitstream.swap(decoded_bitstream);
+
+ session.size = session.bitstream.size();
+}
+
+/*
+ * Validate X300 image and extract if LVBITX.
+ */
+static void x300_validate_image(x300_session_t &session){
+ if(not fs::exists(session.filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".")
+ % session.filepath));
+ }
+
+ std::string extension = fs::extension(session.filepath);
+ session.lvbitx = (extension == ".lvbitx");
+
+ if(session.lvbitx){
+ extract_from_lvbitx(session);
+ if(session.size > X300_FPGA_BIN_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % X300_FPGA_BIN_SIZE_BYTES));
+ }
+
+ /*
+ * PCIe burning just takes a filepath, even for a .lvbitx file,
+ * so just extract it to validate the size.
+ */
+ if(!session.ethernet) session.bitstream.clear();
+ }
+ else if(extension == ".bin" or extension == ".bit"){
+ boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES
+ : X300_FPGA_BIT_SIZE_BYTES;
+
+ session.size = fs::file_size(session.filepath);
+ if(session.size > max_size){
+ throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d")
+ % session.size % max_size));
+ return;
+ }
+ }
+ else{
+ throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.")
+ % extension));
+ }
+}
+
+static void x300_setup_session(x300_session_t &session,
+ const device_addr_t &args,
+ const std::string &filepath){
+ device_addr_t find_args;
+ find_args["type"] = "x300";
+ if(args.has_key("name")) find_args["name"] = args["name"];
+ if(args.has_key("serial")) find_args["serial"] = args["serial"];
+ if(args.has_key("ip-addr")) find_args["addr"] = args["ip-addr"];
+ else if(args.has_key("resource")) find_args["resource"] = args["resource"];
+
+ device_addrs_t devs = x300_find(args);
+ session.found = (devs.size() > 0);
+ if(!session.found) return;
+
+ session.dev_addr = devs[0];
+ session.ethernet = session.dev_addr.has_key("addr");
+ if(session.ethernet){
+ session.ip_addr = session.dev_addr["addr"];
+ session.configure = args.has_key("configure");
+ session.xport = udp_simple::make_connected(session.ip_addr,
+ BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT));
+ session.verify = args.has_key("verify");
+ }
+ else{
+ session.resource = session.dev_addr["resource"];
+ session.rpc_port = args.get("rpc-port", "5444");
+ }
+
+ /*
+ * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user
+ * does not specify one, this will default to the type currently on the device. If this
+ * cannot be determined, then the user is forced to specify a filename.
+ */
+ session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", ""));
+ if(filepath == ""){
+ if(!session.dev_addr.has_key("product") or session.fpga_type == ""){
+ throw uhd::runtime_error("Found a device but could not auto-generate an image filename.");
+ }
+ else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit")
+ % (to_lower_copy(session.dev_addr["product"]))
+ % session.fpga_type));
+ }
+ else session.filepath = filepath;
+
+ // Validate image
+ x300_validate_image(session);
+}
+
+/*
+ * Ethernet communication functions
+ */
+static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport,
+ boost::uint32_t pkt_code,
+ x300_fpga_update_data_t *pkt_out,
+ boost::uint8_t* data){
+ pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code);
+ xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out)));
+ return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT);
+}
+
+static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in,
+ size_t len){
+ return (len > 0 and
+ ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR));
+}
+
+// Image data needs to be bitswapped
+static UHD_INLINE void x300_bitswap(boost::uint8_t *num){
+ *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4);
+ *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2);
+ *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1);
+}
+
+static void x300_ethernet_load(x300_session_t &session){
+
+ // UDP receive buffer
+ x300_fpga_update_data_t pkt_out;
+ const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in);
+
+ // Initialize write session
+ boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT;
+ size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(x300_recv_ok(pkt_in, len)){
+ std::cout << "-- Initializing FPGA loading..." << std::flush;
+ }
+ else if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during initialization.");
+ }
+
+ std::cout << "successful." << std::endl;
+ if(session.verify){
+ std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl;
+ }
+
+ size_t current_pos = 0;
+ size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE);
+ std::ifstream image(session.filepath.c_str(), std::ios::binary);
+
+ // Each sector
+ for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){
+
+ // Print progress percentage at beginning of each sector
+ std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)")
+ % session.fpga_type
+ % (int(double(i) / double(session.size) * 100.0))
+ % (i / X300_FLASH_SECTOR_SIZE)
+ % sectors
+ << std::flush;
+
+ // Each packet
+ for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){
+ flags = X300_FPGA_PROG_FLAGS_ACK;
+ if(j == i) flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector
+ if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY;
+
+ // Set burn location
+ pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE));
+ pkt_out.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2);
+ pkt_out.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2);
+
+ // Read next piece of image
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ if(session.lvbitx){
+ memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES);
+ current_pos += X300_PACKET_SIZE_BYTES;
+ }
+ else{
+ image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES);
+ }
+
+ // Data must be bitswapped and byteswapped
+ for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){
+ x300_bitswap(&pkt_out.data8[k]);
+ }
+ for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){
+ pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]);
+ }
+
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ if(!session.lvbitx) image.close();
+ throw uhd::runtime_error("Device reported an error.");
+ }
+ }
+ }
+ if(!session.lvbitx){
+ image.close();
+ }
+
+ std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)")
+ % session.fpga_type
+ % sectors
+ % sectors
+ << std::endl;
+
+ // Cleanup
+ if(!session.lvbitx) image.close();
+ flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK);
+ pkt_out.sector = pkt_out.index = pkt_out.size = 0;
+ memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES);
+ std::cout << "-- Finalizing image load..." << std::flush;
+ len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error during cleanup.");
+ }
+ else std::cout << "successful." << std::endl;
+
+ // Save new FPGA image (if option set)
+ if(session.configure){
+ flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK);
+ x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in);
+ std::cout << "-- Saving image onto device..." << std::flush;
+ if(len == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Timed out waiting for reply from device.");
+ }
+ else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Device reported an error while saving the image.");
+ }
+ else std::cout << "successful." << std::endl;
+ }
+}
+
+static void x300_pcie_load(x300_session_t &session){
+
+ std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...")
+ % session.fpga_type
+ << std::flush;
+
+ nirio_status status = NiRio_Status_Success;
+ niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port);
+ nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status);
+
+ if(nirio_status_fatal(status)){
+ std::cout << "failed." << std::endl;
+ niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:");
+ }
+ else std::cout << "successful." << std::endl;
+}
+
+static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if any X3x0 with the given args is found
+ device_addrs_t devs = x300_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_fpga) return false;
+
+ x300_session_t session;
+ x300_setup_session(session,
+ image_loader_args.args,
+ image_loader_args.fpga_path
+ );
+ if(!session.found) return false;
+
+ std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n")
+ % session.dev_addr["product"]
+ % session.dev_addr["serial"]
+ % session.dev_addr[session.ethernet ? "addr" : "resource"]
+ % session.filepath;
+
+ if(session.ethernet) x300_ethernet_load(session);
+ else x300_pcie_load(session);
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_x300_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n"
+ "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n"
+ "for details on restoring your device.";
+
+ image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index 91e6ded26..8c565a252 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -235,7 +235,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu
return addrs;
}
-static device_addrs_t x300_find(const device_addr_t &hint_)
+device_addrs_t x300_find(const device_addr_t &hint_)
{
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
@@ -509,7 +509,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
//check compat numbers
//check fpga compat before fw compat because the fw is a subset of the fpga image
- this->check_fpga_compat(mb_path, mb.zpu_ctrl);
+ this->check_fpga_compat(mb_path, mb);
this->check_fw_compat(mb_path, mb.zpu_ctrl);
//store which FPGA image is loaded
@@ -1696,25 +1696,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface)
% compat_major % compat_minor));
}
-void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface)
+void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members)
{
- boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
+ boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
boost::uint32_t compat_major = (compat_num >> 16);
boost::uint32_t compat_minor = (compat_num & 0xffff);
if (compat_major != X300_FPGA_COMPAT_MAJOR)
{
+ std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"")
+ % image_loader_path
+ % (members.xport_path == "eth" ? "addr"
+ : "resource")
+ % members.addr);
+
throw uhd::runtime_error(str(boost::format(
"Expected FPGA compatibility number %d, but got %d:\n"
"The FPGA image on your device is not compatible with this host code build.\n"
"Download the appropriate FPGA images for this version of UHD.\n"
"%s\n\n"
"Then burn a new image to the on-board flash storage of your\n"
- "USRP X3xx device using the burner utility. %s\n\n"
+ "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n"
"For more information, refer to the UHD manual:\n\n"
" http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash"
) % int(X300_FPGA_COMPAT_MAJOR) % compat_major
- % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner")));
+ % print_utility_error("uhd_images_downloader.py")
+ % image_loader_cmd));
}
_tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
% compat_major % compat_minor));
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index dca6360b8..64f14cae6 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -142,6 +142,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);
uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp);
uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy);
+uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_);
+
class x300_impl : public uhd::device
{
public:
@@ -398,7 +400,7 @@ private:
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, uhd::wb_iface::sptr iface);
+ void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);
void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant);
boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt
index e363bb9da..c489657e2 100644
--- a/host/lib/usrp_clock/octoclock/CMakeLists.txt
+++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2014 Ettus Research LLC
+# Copyright 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
@@ -16,17 +16,15 @@
#
########################################################################
-# This file included, use CMake directory variables
-########################################################################
-
-########################################################################
# Conditionally configure the OctoClock support
########################################################################
LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF)
IF(ENABLE_OCTOCLOCK)
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/ihexcvt.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp
)
diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.cpp b/host/lib/usrp_clock/octoclock/ihexcvt.cpp
new file mode 100644
index 000000000..0605ee61c
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/ihexcvt.cpp
@@ -0,0 +1,250 @@
+/* IHexCvt - Intel HEX File <=> Binary Converter (C++)
+ Copyright (C) 2014 Ali Nakisaee
+
+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 2 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/
+
+//Include needed stuff from C++
+#include <iostream>
+#include <fstream>
+#include <string>
+
+//... and also from C
+#include <stdio.h>
+
+#include <boost/filesystem.hpp>
+
+#include <uhd/exception.hpp>
+#include "ihexcvt.hpp"
+
+//Avoid repeating 'std::':
+using namespace std;
+
+//The following function reads a hexadecimal number from a text file.
+template <class T>
+static bool ReadValueFromHex(ifstream& InputFile, T& outCh, unsigned char* ApplyChecksum)
+{
+ char V, L;
+ T X = 0;
+ outCh = 0;
+
+ //Get the characters one by one.
+ //Remember: These values are big-endian.
+ //Remember: Every two hex characters (0-9/A-F) indicate ONE byte.
+ for (size_t i = 0; i < 2 * sizeof(T); i++)
+ {
+ InputFile.get( V );
+ if (InputFile.fail())
+ return false;
+
+ X <<= 4;
+ if (V >= '0' && V <= '9')
+ L = (V - '0');
+ else if (V >= 'a' && V <= 'f')
+ L = (V - 'a' + 10);
+ else if (V >= 'A' && V <= 'F')
+ L = (V - 'A' + 10);
+ else
+ return false;
+ X |= L;
+
+ //Apply this character to the checksum
+ if (ApplyChecksum && i % 2 == 1) *ApplyChecksum += X & 0xFF;
+ }
+
+ //Return...
+ outCh = X;
+ return true;
+}
+
+//The following function writes a hexadecimal number from a text file.
+template <class T>
+static bool WriteHexValue(ofstream& OutFile, T Value, unsigned char* CalcChecksum)
+{
+ unsigned char V0 = 0;
+ char C;
+
+ //Remember: These values are big-endian.
+ for (size_t i = 0; i < sizeof(T); i++)
+ {
+ //Get byte #i from the value.
+ V0 = (Value >> ((sizeof(T) - i - 1) * 8)) & 0xFF;
+
+ //Extract the high nibble (4-bits)
+ if ((V0 & 0xF0) <= 0x90)
+ C = (V0 >> 4) + '0';
+ else
+ C = (V0 >> 4) + ('A' - 10);
+ OutFile.put( C );
+
+ //Extract the low nibble (4-bits)
+ if ((V0 & 0xF) <= 0x9)
+ C = (V0 & 0xF) + '0';
+ else
+ C = (V0 & 0xF) + ('A' - 10);
+ OutFile.put( C );
+
+ //Calculate the checksum
+ if (CalcChecksum) *CalcChecksum += V0;
+ }
+ return true;
+}
+
+//Skip any incoming whitespaces
+static void SkipWhitespace(ifstream& InputFile)
+{
+ for (;;)
+ {
+ char C;
+ InputFile.get(C);
+ if (InputFile.eof() || InputFile.fail()) break;
+ if (!(C == '\n' || C == '\r' || C == ' ' || C == '\t' || C == '\v'))
+ {
+ InputFile.putback(C);
+ break;
+ }
+ }
+}
+
+//The function responsible for conversion from HEX files to BINary.
+void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum)
+{
+ ifstream Src(SrcName);
+ if (Src.bad())
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+
+ ofstream Dst(DstName, ios_base::binary);
+ if (Dst.bad())
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+
+ char Ch;
+ int LineIdx = 1;
+
+ unsigned char ByteCount;
+ unsigned short AddressLow;
+ unsigned short Extra;
+ unsigned long ExtraL;
+ unsigned long AddressOffset = 0;
+ unsigned char RecordType;
+ unsigned char Data[255];
+ unsigned char CurChecksum;
+ unsigned char FileChecksum;
+ bool EOFMarker = false;
+ bool EOFWarn = false;
+
+ for ( ;; )
+ {
+ Src.get(Ch);
+ if (Src.eof())
+ break;
+ if (EOFMarker && !EOFWarn)
+ {
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+ }
+ if (Ch != ':') goto genericErr;
+
+ CurChecksum = 0;
+ if (!ReadValueFromHex( Src, ByteCount, &CurChecksum )) goto genericErr;
+ if (!ReadValueFromHex( Src, AddressLow, &CurChecksum )) goto genericErr;
+ if (!ReadValueFromHex( Src, RecordType, &CurChecksum )) goto genericErr;
+
+ switch (RecordType)
+ {
+ case 0x00: //Data record
+ for (int i = 0; i < ByteCount; i++)
+ if (!ReadValueFromHex( Src, Data[i], &CurChecksum )) goto genericErr;
+ break;
+ case 0x01: //End Marker
+ if ( ByteCount != 0 )
+ {
+ goto onErrExit;
+ }
+ EOFMarker = true;
+ break;
+ case 0x02: //Extended Segment Address
+ if ( ByteCount != 2 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr;
+ AddressOffset = (unsigned long)Extra << 4;
+ break;
+ case 0x03: //Start Segment Address
+ if ( ByteCount != 4 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr;
+ break;
+ case 0x04: //Extended Linear Address
+ if ( ByteCount != 2 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr;
+ AddressOffset = (unsigned long)Extra << 16;
+ break;
+ case 0x05: //Start Linear Address
+ if ( ByteCount != 4 || AddressLow != 0 )
+ {
+ goto onErrExit;
+ }
+ if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr;
+ break;
+ }
+
+ //Verify checksum
+ CurChecksum = (~(CurChecksum & 0xFF) + 1) & 0xFF;
+ if (!ReadValueFromHex( Src, FileChecksum, NULL )) goto genericErr;
+ if (CurChecksum != FileChecksum)
+ {
+ if (!IgnoreChecksum) goto onErrExit;
+ }
+
+ //Put Data
+ if (RecordType == 0x00)
+ {
+ Dst.seekp( AddressLow + AddressOffset );
+ for (int i = 0; i < ByteCount; i++)
+ {
+ Dst.put( Data[i] );
+ }
+ }
+
+ //Skip any white space
+ SkipWhitespace( Src );
+
+ LineIdx++;
+ }
+
+ Dst << flush;
+ Dst.close();
+
+ return;
+
+genericErr:
+ throw uhd::runtime_error("Invalid Intel .hex file detected.");
+
+onErrExit:
+ Dst.close();
+ Src.close();
+ boost::filesystem::remove(DstName);
+ throw uhd::runtime_error("Could not convert Intel .hex file to binary.");
+}
diff --git a/host/lib/usrp_clock/octoclock/ihexcvt.hpp b/host/lib/usrp_clock/octoclock/ihexcvt.hpp
new file mode 100644
index 000000000..d577ece1f
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/ihexcvt.hpp
@@ -0,0 +1,22 @@
+//
+// Copyright 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 <http://www.gnu.org/licenses/>.
+//
+#ifndef _IHEXCVT_HPP_
+#define _IHEXCVT_HPP_
+
+void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum);
+
+#endif /* _IHEXCVT_HPP_ */
diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
new file mode 100644
index 000000000..c88177f0f
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
@@ -0,0 +1,340 @@
+//
+// Copyright 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 <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <boost/cstdint.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/thread.hpp>
+
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include "octoclock_impl.hpp"
+#include "common.h"
+#include "ihexcvt.hpp"
+
+namespace fs = boost::filesystem;
+using namespace uhd;
+using namespace uhd::usrp_clock;
+using namespace uhd::transport;
+
+#define OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES (1024*120) // Last 8 MB are for bootloader
+#define OCTOCLOCK_BLOCK_SIZE 256
+
+/*
+ * OctoClock burn session
+ */
+typedef struct {
+ bool valid;
+ uhd::device_addr_t dev_addr;
+ std::string given_filepath;
+ std::string actual_filepath; // If using a .hex, this is the converted .bin
+ bool from_hex;
+ boost::uint32_t size;
+ boost::uint16_t crc;
+ boost::uint16_t num_blocks;
+ udp_simple::sptr ctrl_xport;
+ udp_simple::sptr fw_xport;
+ boost::uint8_t data_in[udp_simple::mtu];
+} octoclock_session_t;
+
+static void octoclock_calculate_crc(octoclock_session_t &session){
+ std::ifstream ifile(session.actual_filepath.c_str());
+ boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES];
+ ifile.read((char*)temp_image, session.size);
+
+ session.crc = 0xFFFF;
+ for(size_t i = 0; i < session.size; i++){
+ session.crc ^= temp_image[i];
+ for(boost::uint8_t j = 0; j < 8; ++j){
+ if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001;
+ else session.crc = (session.crc >> 1);
+ }
+ }
+
+ ifile.close();
+}
+
+static void octoclock_validate_firmware_image(octoclock_session_t &session){
+ if(not fs::exists(session.given_filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"")
+ % session.given_filepath));
+ }
+
+ std::string extension = fs::extension(session.given_filepath);
+ if(extension == ".bin"){
+ session.actual_filepath = session.given_filepath;
+ session.from_hex = false;
+ }
+ else if(extension == ".hex"){
+ session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) /
+ str(boost::format("octoclock_fw_%d.bin")
+ % time_spec_t::get_system_time().get_full_secs())
+ ).string();
+
+ Hex2Bin(session.given_filepath.c_str(), session.actual_filepath.c_str(), false);
+ session.from_hex = true;
+ }
+ else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.")));
+
+ session.size = fs::file_size(session.actual_filepath);
+ if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d")
+ % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES));
+ }
+
+ session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1)
+ : (session.size / OCTOCLOCK_BLOCK_SIZE);
+
+ octoclock_calculate_crc(session);
+ session.valid = true;
+}
+
+static void octoclock_setup_session(octoclock_session_t &session,
+ const std::string &filepath){
+
+ // If no filepath is given, use the default
+ if(filepath == ""){
+ session.given_filepath = find_image_path(str(boost::format("octoclock_r%d_fw.hex")
+ % boost::lexical_cast<std::string>(
+ session.dev_addr.get("revision","4")
+ )));
+ }
+ else session.given_filepath = filepath;
+
+ octoclock_validate_firmware_image(session);
+
+ session.ctrl_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT));
+ session.fw_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT));
+}
+
+static void octoclock_reset_into_bootloader(octoclock_session_t &session){
+
+ // Already in bootloader
+ if(session.dev_addr["type"] == "octoclock-bootloader")
+ return;
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len;
+
+ std::cout << " -- Resetting into bootloader..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.ctrl_xport, RESET_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){
+
+ // Make sure this device is now in its bootloader
+ boost::this_thread::sleep(boost::posix_time::milliseconds(500));
+ uhd::device_addrs_t octoclocks = uhd::device::find(
+ uhd::device_addr_t(str(boost::format("addr=%s")
+ % session.dev_addr["addr"]
+ )));
+ if(octoclocks.size() == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else if(octoclocks[0]["type"] != "octoclock-bootloader"){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else{
+ std::cout << "successful." << std::endl;
+ session.dev_addr = octoclocks[0];
+ }
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+}
+
+static void octoclock_burn(octoclock_session_t &session){
+
+ // Make sure we're in the bootloader for this
+ octoclock_reset_into_bootloader(session);
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+
+ // Tell OctoClock to prepare for burn
+ pkt_out.len = htonx<boost::uint16_t>(session.size);
+ size_t len = 0;
+ std::cout << " -- Preparing OctoClock for firmware load..." << std::flush;
+ pkt_out.len = session.size;
+ pkt_out.crc = session.crc;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to prepare OctoClock for firmware load.");
+ }
+
+ // Start burning
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Loading firmware: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE);
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in);
+ if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){
+ image.close();
+ std::cout << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to load firmware.");
+ }
+ }
+
+ std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+ image.close();
+}
+
+static void octoclock_verify(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE];
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Verifying firmware load: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE);
+ cmp_len = image.gcount();
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){
+ if(memcmp(pkt_in->data, image_part, cmp_len)){
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+ else{
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+}
+
+static void octoclock_finalize(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::cout << " -- Finalizing firmware load..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FINALIZE_BURNING_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to finalize OctoClock firmware load.");
+ }
+}
+
+bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ // See if we can find an OctoClock with the given args
+ device_addrs_t devs = octoclock_find(image_loader_args.args);
+ if(devs.size() == 0 or !image_loader_args.load_firmware) return false;
+
+ octoclock_session_t session;
+ session.dev_addr = devs[0];
+ octoclock_setup_session(session,
+ image_loader_args.firmware_path
+ );
+
+ std::cout << boost::format("Unit: OctoClock (%s)")
+ % session.dev_addr["addr"]
+ << std::endl;
+ std::cout << "Firmware: " << session.given_filepath << std::endl;
+
+ octoclock_burn(session);
+ octoclock_verify(session);
+ octoclock_finalize(session);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_octoclock_image_loader){
+ std::string recovery_instructions = "Aborting. Your OctoClock firmware is now corrupt. The bootloader\n"
+ "is functional, but the device will not have functional clock distribution."
+ "Run this utility again to restore functionality or refer to:\n\n"
+ "http://files.ettus.com/manual/page_octoclock.html\n\n"
+ "for alternative setups.";
+
+ image_loader::register_image_loader("octoclock",
+ octoclock_image_loader,
+ recovery_instructions);
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
index ab45cd5f0..453e75ec5 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
@@ -31,6 +31,8 @@
#include "common.h"
+uhd::device_addrs_t octoclock_find(const uhd::device_addr_t &hint);
+
/*!
* OctoClock implementation guts
*/