aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Corgan <nick.corgan@ettus.com>2012-10-09 15:03:54 -0700
committerJosh Blum <josh@joshknows.com>2012-10-10 13:29:12 -0700
commit01d90259772520e555de4eacedd33aeebf51fa45 (patch)
tree676d5ed1751db31bccc2af3afaa45a59721eff46
parentadffee268c57c070ea1ed631a94c24975d0a40ae (diff)
downloaduhd-01d90259772520e555de4eacedd33aeebf51fa45.tar.gz
uhd-01d90259772520e555de4eacedd33aeebf51fa45.tar.bz2
uhd-01d90259772520e555de4eacedd33aeebf51fa45.zip
utils: USRP N2XX Simple Net Burner
* More automated C++ implementation of usrp_n2xx_net_burner.py * By default, installs images from standard image install directories
-rw-r--r--host/lib/usrp/usrp2/usrp2_iface.cpp7
-rw-r--r--host/lib/utils/images.cpp2
-rw-r--r--host/utils/CMakeLists.txt1
-rw-r--r--host/utils/usrp_n2xx_simple_net_burner.cpp517
-rw-r--r--host/utils/usrp_simple_burner_utils.hpp99
5 files changed, 621 insertions, 5 deletions
diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp
index f0b2a90a6..8804433e7 100644
--- a/host/lib/usrp/usrp2/usrp2_iface.cpp
+++ b/host/lib/usrp/usrp2/usrp2_iface.cpp
@@ -410,8 +410,7 @@ public:
const std::string ml = "\\\n ";
#endif
- //create the images downloader and burner commands
- const std::string images_downloader_cmd = str(boost::format("%s\"%s\"") % sudo % find_images_downloader());
+ //create the burner commands
if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){
const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.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);
@@ -419,8 +418,8 @@ public:
}
else{
const std::string addr = _ctrl_transport->get_recv_addr();
- const std::string net_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_net_burner.py").string();
- const std::string net_burner_cmd = str(boost::format("\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\" %s--addr=\"%s\"") % net_burner % ml % fpga_image_path % ml % fw_image_path % ml % addr);
+ const std::string net_burner_path = (fs::path(fw_image_path).branch_path().branch_path() / "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_images_error() % net_burner_cmd);
}
}
diff --git a/host/lib/utils/images.cpp b/host/lib/utils/images.cpp
index 654e31179..251cadeaa 100644
--- a/host/lib/utils/images.cpp
+++ b/host/lib/utils/images.cpp
@@ -38,7 +38,7 @@ std::string uhd::find_image_path(const std::string &image_name){
fs::path image_path = path / image_name;
if (fs::exists(image_path)) return image_path.string();
}
- throw uhd::io_error("Could not find path for image: " + image_name);
+ throw uhd::io_error("Could not find path for image: " + image_name + "\n\n" + uhd::print_images_error());
}
std::string uhd::find_images_downloader(void){
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index 763bb47bc..9e997071e 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -41,6 +41,7 @@ SET(util_share_sources
query_gpsdo_sensors.cpp
usrp_burn_db_eeprom.cpp
usrp_burn_mb_eeprom.cpp
+ usrp_n2xx_simple_net_burner.cpp
)
IF(ENABLE_USB)
diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp
new file mode 100644
index 000000000..223021510
--- /dev/null
+++ b/host/utils/usrp_n2xx_simple_net_burner.cpp
@@ -0,0 +1,517 @@
+//
+// Copyright 2012 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 <fstream>
+#include <time.h>
+#include <vector>
+
+#include <boost/foreach.hpp>
+#include <boost/asio.hpp>
+#include <boost/program_options.hpp>
+#include <boost/assign.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/format.hpp>
+#include <boost/algorithm/string/erase.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+
+#include "usrp_simple_burner_utils.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/images.hpp>
+#include <uhd/utils/safe_main.hpp>
+#include <uhd/utils/safe_call.hpp>
+
+namespace po = boost::program_options;
+using namespace boost::algorithm;
+using namespace uhd;
+using namespace uhd::transport;
+
+//Mapping revision numbers to filenames
+std::map<boost::uint32_t, std::string> filename_map = boost::assign::map_list_of
+ (0xa, "n200_r3")
+ (0x100a, "n200_r4")
+ (0x10a, "n210_r3")
+ (0x110a, "n210_r4")
+;
+
+//For non-standard images not covered by uhd::find_image_path()
+bool does_image_exist(std::string image_filepath){
+
+ std::ifstream ifile((char*)image_filepath.c_str());
+ return ifile;
+}
+
+//Checks for existence of file and validity of filename
+void check_image_file_validity(std::string rev_str, std::string default_fpga_filename, std::string default_fw_filename,
+ std::string fpga_path, std::string fw_path, bool burn_fpga, bool burn_fw, bool use_custom_fpga, bool use_custom_fw){
+
+ if(burn_fpga){
+ if(use_custom_fpga){
+ //Check for existence of file
+ if(!does_image_exist(fpga_path)) throw std::runtime_error(str(boost::format("No file at specified FPGA path: %s") % fw_path));
+
+ //Check to find rev_str in filename
+ boost::filesystem::path custom_fpga_path(fpga_path);
+ if(custom_fpga_path.filename().string().find("fw") != std::string::npos){
+ throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename indicates that this is a firmware image.")
+ % fpga_path));
+ }
+ if(custom_fpga_path.filename().string().find(rev_str) == std::string::npos){
+ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.")
+ % fw_path % rev_str));
+ }
+ }
+ //Check for image in UHD_IMAGES_DIR
+ else find_image_path(default_fpga_filename);
+ }
+ if(burn_fw){
+ if(use_custom_fw){
+ //Check for existence of file
+ if(!does_image_exist(fw_path)) throw std::runtime_error(str(boost::format("No file at specified firmware path: %s") % fw_path));
+
+ //Check to find truncated rev_str in filename
+ boost::filesystem::path custom_fw_path(fw_path);
+ if(custom_fw_path.filename().string().find("fpga") != std::string::npos){
+ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename indicates that this is an FPGA image.")
+ % fw_path));
+ }
+ if(custom_fw_path.filename().string().find(erase_tail_copy(rev_str,3)) == std::string::npos){
+ throw std::runtime_error(str(boost::format("Invalid firmware image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.")
+ % fw_path % erase_tail_copy(rev_str,3)));
+ }
+ }
+ //Check for image in UHD_IMAGES_DIR
+ else find_image_path(default_fw_filename);
+ }
+}
+
+//Basic checks of images to make sure they're actually images
+void check_image_binary_validity(std::string fpga_path, std::string fw_path, bool burn_fpga, bool burn_fw){
+
+ if(burn_fpga){
+ std::ifstream fpga_image((char*)fpga_path.c_str(), std::ios::binary);
+
+ //Check size of image
+ fpga_image.seekg(0, std::ios::end);
+ int fpga_size = fpga_image.tellg();
+ if(fpga_size > FPGA_IMAGE_SIZE_BYTES){
+ throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") % fpga_size % FPGA_IMAGE_SIZE_BYTES));
+ }
+
+ //Check sequence of bytes in image
+ char fpga_image_buffer[2];
+ bool fpga_image_is_good = false;
+ for(int i = 0; i < 63; i++){
+ fpga_image.seekg(i, std::ios::beg);
+ fpga_image.read(fpga_image_buffer,2);
+
+ if((unsigned char)fpga_image_buffer[0] == 255) continue;
+ else if((unsigned char)fpga_image_buffer[0] == 170 and
+ (unsigned char)fpga_image_buffer[1] == 153){
+ fpga_image_is_good = true;
+ break;
+ }
+ }
+ if(!fpga_image_is_good) throw std::runtime_error("Not a valid FPGA image.");
+
+ fpga_image.close();
+ }
+ if(burn_fw){
+ std::ifstream fw_image((char*)fw_path.c_str(), std::ios::binary);
+
+ //Check size of image
+ fw_image.seekg(0, std::ios::end);
+ int fw_size = fw_image.tellg();
+ if(fw_size > FW_IMAGE_SIZE_BYTES){
+ throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") % fw_size % FW_IMAGE_SIZE_BYTES));
+ }
+
+ //Check first four bytes of image
+ char fw_image_buffer[4];
+ fw_image.seekg(0, std::ios::beg);
+ fw_image.read(fw_image_buffer, 4);
+ for(int i = 0; i < 4; i++) if(int(fw_image_buffer[i]) != 11) throw std::runtime_error("Not a valid firmware image.");
+
+ fw_image.close();
+ }
+}
+
+boost::uint32_t* get_flash_info(std::string ip_addr){
+
+ boost::uint32_t *flash_info = new boost::uint32_t[2];
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT));
+ usrp2_fw_update_data_t get_flash_info_pkt = usrp2_fw_update_data_t();
+ get_flash_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ get_flash_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL);
+ udp_transport->send(boost::asio::buffer(&get_flash_info_pkt, sizeof(get_flash_info_pkt)));
+
+ //Loop and receive until the timeout
+ while(true){
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem));
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){
+ flash_info[0] = ntohl(update_data_in->data.flash_info_args.sector_size_bytes);
+ flash_info[1] = ntohl(update_data_in->data.flash_info_args.memory_size_bytes);
+ }
+ if(len == 0) break;
+ }
+
+ return flash_info;
+}
+
+void erase_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint32_t memory_size){
+
+ //Making sure this won't attempt to erase past end of device
+ if(is_fw){
+ if(PROD_FW_IMAGE_LOCATION_ADDR+FW_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device.");
+ }
+ else{
+ if(PROD_FPGA_IMAGE_LOCATION_ADDR+FPGA_IMAGE_SIZE_BYTES > memory_size) throw std::runtime_error("Cannot erase past end of device.");
+ }
+
+ //Setting up UDP transport
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ //Setting up UDP packet
+ usrp2_fw_update_data_t erase_pkt = usrp2_fw_update_data_t();
+ erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL);
+ erase_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ if(is_fw){
+ erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FW_IMAGE_LOCATION_ADDR);
+ erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FW_IMAGE_SIZE_BYTES);
+ }
+ else{
+ erase_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(PROD_FPGA_IMAGE_LOCATION_ADDR);
+ erase_pkt.data.flash_args.length = htonx<boost::uint32_t>(FPGA_IMAGE_SIZE_BYTES);
+ }
+
+ //Begin erasing
+ udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt)));
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){
+ if(is_fw) std::cout << "Erasing firmware image." << std::endl;
+ else std::cout << "Erasing FPGA image." << std::endl;
+ }
+ else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG){
+ throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id)));
+ }
+
+ //Check for erase completion
+ erase_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL);
+ while(true){
+ udp_transport->send(boost::asio::buffer(&erase_pkt, sizeof(erase_pkt)));
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG){
+ if(is_fw) std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FW_IMAGE_SIZE_BYTES % PROD_FW_IMAGE_LOCATION_ADDR;
+ else std::cout << boost::format(" * Successfully erased %d bytes at %d.\n") % FPGA_IMAGE_SIZE_BYTES % PROD_FPGA_IMAGE_LOCATION_ADDR;
+ break;
+ }
+ else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG){
+ throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id)));
+ }
+ }
+}
+
+void write_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){
+
+ boost::uint32_t current_addr;
+ if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR;
+ else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR;
+
+ //Making sure this won't attempt to write past end of device
+ if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot write past end of device.");
+
+ //Setting up UDP transport
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ //Setting up UDP packet
+ usrp2_fw_update_data_t write_pkt = usrp2_fw_update_data_t();
+ write_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL);
+ write_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ write_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE);
+
+ //Write image
+ if(is_fw) std::cout << "Writing firmware image." << std::endl;
+ else std::cout << "Writing FPGA image." << std::endl;
+
+ for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){
+ write_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+ std::copy(image+(i*FLASH_DATA_PACKET_SIZE), image+((i+1)*FLASH_DATA_PACKET_SIZE), write_pkt.data.flash_args.data);
+
+ udp_transport->send(boost::asio::buffer(&write_pkt, sizeof(write_pkt)));
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG){
+ throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id)));
+ }
+
+ current_addr += FLASH_DATA_PACKET_SIZE;
+ }
+ std::cout << boost::format(" * Successfully wrote %d bytes.\n") % image_size;
+}
+
+void verify_image(udp_simple::sptr udp_transport, bool is_fw, boost::uint8_t* image, boost::uint32_t memory_size, int image_size){
+
+ int current_index = 0;
+ boost::uint32_t current_addr;
+ if(is_fw) current_addr = PROD_FW_IMAGE_LOCATION_ADDR;
+ else current_addr = PROD_FPGA_IMAGE_LOCATION_ADDR;
+
+ //Array size needs to be known at runtime, this constant is guaranteed to be larger than any firmware or FPGA image
+ boost::uint8_t from_usrp[FPGA_IMAGE_SIZE_BYTES];
+
+ //Making sure this won't attempt to read past end of device
+ if(current_addr+image_size > memory_size) throw std::runtime_error("Cannot read past end of device.");
+
+ //Setting up UDP transport
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ //Setting up UDP packet
+ usrp2_fw_update_data_t verify_pkt = usrp2_fw_update_data_t();
+ verify_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL);
+ verify_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ verify_pkt.data.flash_args.length = htonx<boost::uint32_t>(FLASH_DATA_PACKET_SIZE);
+
+ //Verify image
+ if(is_fw) std::cout << "Verifying firmware image." << std::endl;
+ else std::cout << "Verifying FPGA image." << std::endl;
+
+ for(int i = 0; i < ((image_size/FLASH_DATA_PACKET_SIZE)+1); i++){
+ verify_pkt.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr);
+
+ udp_transport->send(boost::asio::buffer(&verify_pkt, sizeof(verify_pkt)));
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG){
+ throw std::runtime_error(str(boost::format("Invalid reply %d from device.") % ntohl(update_data_in->id)));
+ }
+ for(int j = 0; j < FLASH_DATA_PACKET_SIZE; j++) from_usrp[current_index+j] = update_data_in->data.flash_args.data[j];
+
+ current_addr += FLASH_DATA_PACKET_SIZE;
+ current_index += FLASH_DATA_PACKET_SIZE;
+ }
+ for(int i = 0; i < image_size; i++) if(from_usrp[i] != image[i]) throw std::runtime_error("Image write failed.");
+
+ std::cout << " * Successful." << std::endl;
+}
+
+void reset_usrp(udp_simple::sptr udp_transport){
+
+ //Set up UDP transport
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ //Set up UDP packet
+ usrp2_fw_update_data_t reset_pkt = usrp2_fw_update_data_t();
+ reset_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL);
+ reset_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+
+ //Reset USRP
+ udp_transport->send(boost::asio::buffer(&reset_pkt, sizeof(reset_pkt)));
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG){
+ throw std::runtime_error("USRP reset failed."); //There should be no response to this UDP packet
+ }
+ else std::cout << "Resetting USRP." << std::endl;
+}
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+
+ //Establish user options
+ std::string fw_path;
+ std::string ip_addr;
+ std::string fpga_path;
+
+ po::options_description desc("Allowed options:");
+ desc.add_options()
+ ("help", "Display this help message.")
+ ("addr", po::value<std::string>(&ip_addr)->default_value("192.168.10.2"), "Specify an IP address.")
+ ("fw", po::value<std::string>(&fw_path), "Specify a filepath for a custom firmware image.")
+ ("fpga", po::value<std::string>(&fpga_path), "Specify a filepath for a custom FPGA image.")
+ ("no_fw", "Do not burn a firmware image.")
+ ("no_fpga", "Do not burn an FPGA image.")
+ ("list", "List available N2XX USRP devices.")
+ ;
+ po::variables_map vm;
+ po::store(po::parse_command_line(argc, argv, desc), vm);
+ po::notify(vm);
+
+ //Apply options
+ if(vm.count("help") > 0){
+ std::cout << boost::format("N2XX Simple Net Burner\n");
+ std::cout << boost::format("Automatically detects and burns standard firmware and FPGA images onto USRP N2XX devices.\n");
+ std::cout << boost::format("Can optionally take user input for custom images.\n\n");
+ std::cout << desc << std::endl;
+ return ~0;
+ }
+
+ bool burn_fpga = (vm.count("no_fpga") == 0);
+ bool burn_fw = (vm.count("no_fw") == 0);
+ bool use_custom_fpga = (vm.count("fpga") > 0);
+ bool use_custom_fw = (vm.count("fw") > 0);
+ bool list_usrps = (vm.count("list") > 0);
+
+ if(!burn_fpga && !burn_fw){
+ std::cout << "No images will be burned." << std::endl;
+ return ~0;
+ }
+
+ if(!burn_fw && use_custom_fw) std::cout << boost::format("Conflicting firmware options presented. Will not burn a firmware image.\n\n");
+ if(!burn_fpga && use_custom_fpga) std::cout << boost::format("Conflicting FPGA options presented. Will not burn an FPGA image.\n\n");
+
+ //Variables not from options
+ boost::uint32_t hw_rev;
+ bool found_it = false;
+ boost::uint8_t usrp2_update_data_in_mem[udp_simple::mtu];
+ const usrp2_fw_update_data_t *update_data_in = reinterpret_cast<const usrp2_fw_update_data_t *>(usrp2_update_data_in_mem);
+
+ //List option
+ if(list_usrps){
+ udp_simple::sptr udp_bc_transport;
+ usrp2_fw_update_data_t usrp2_ack_pkt = usrp2_fw_update_data_t();
+ usrp2_ack_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_OHAI_LOL);
+
+ std::cout << "Available USRP N2XX devices:" << std::endl;
+
+ //Send UDP packets to all broadcast addresses
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
+ //Avoid the loopback device
+ if(if_addrs.inet == boost::asio::ip::address_v4::loopback().to_string()) continue;
+ udp_bc_transport = udp_simple::make_broadcast(if_addrs.bcast, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT));
+ udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt)));
+
+ size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_OHAI_OMG){
+ usrp2_ack_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL);
+ udp_bc_transport->send(boost::asio::buffer(&usrp2_ack_pkt, sizeof(usrp2_ack_pkt)));
+
+ size_t len = udp_bc_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){
+ hw_rev = ntohl(update_data_in->data.hw_rev);
+ }
+
+ std::cout << boost::format(" * %s (%s)\n") % udp_bc_transport->get_recv_addr() % filename_map[hw_rev];
+ }
+
+ }
+ return ~0;
+ }
+ std::cout << boost::format("Searching for USRP N2XX with IP address %s.\n") % ip_addr;
+
+ //Address specified
+ udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(USRP2_UDP_UPDATE_PORT));
+ usrp2_fw_update_data_t hw_info_pkt = usrp2_fw_update_data_t();
+ hw_info_pkt.proto_ver = htonx<boost::uint32_t>(USRP2_FW_PROTO_VERSION);
+ hw_info_pkt.id = htonx<boost::uint32_t>(USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL);
+ udp_transport->send(boost::asio::buffer(&hw_info_pkt, sizeof(hw_info_pkt)));
+
+ //Loop and receive until the timeout
+ size_t len = udp_transport->recv(boost::asio::buffer(usrp2_update_data_in_mem), UDP_TIMEOUT);
+ if(len > offsetof(usrp2_fw_update_data_t, data) and ntohl(update_data_in->id) == USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG){
+ hw_rev = ntohl(update_data_in->data.hw_rev);
+ if(filename_map.find(hw_rev) != filename_map.end()){
+ std::cout << boost::format("Found %s.\n\n") % filename_map[hw_rev];
+ found_it = true;
+ }
+ else throw std::runtime_error("Invalid revision found.");
+ }
+ if(!found_it) throw std::runtime_error("No USRP N2XX found.");
+
+ //Determining default image filenames for validation
+ std::string default_fw_filename = str(boost::format("usrp_%s_fw.bin") % erase_tail_copy(filename_map[hw_rev],3));
+ std::string default_fpga_filename = str(boost::format("usrp_%s_fpga.bin") % filename_map[hw_rev]);
+ std::string default_fw_filepath = "";
+ std::string default_fpga_filepath = "";
+
+ //Check validity of file locations and binaries before attempting burn
+ std::cout << "Searching for specified images." << std::endl << std::endl;
+ check_image_file_validity(filename_map[hw_rev], default_fpga_filename, default_fw_filename, fpga_path, fw_path,
+ burn_fpga, burn_fw, use_custom_fpga, use_custom_fw);
+ if(burn_fw && !use_custom_fw) fw_path = find_image_path(default_fw_filename);
+ if(burn_fpga && !use_custom_fpga) fpga_path = find_image_path(default_fpga_filename);
+ check_image_binary_validity(fpga_path, fw_path, burn_fpga, burn_fw);
+
+ std::cout << "Will burn the following images:" << std::endl;
+ if(burn_fw) std::cout << boost::format(" * Firmware: %s\n") % fw_path;
+ if(burn_fpga) std::cout << boost::format(" * FPGA: %s\n") % fpga_path;
+ std::cout << std::endl;
+
+ boost::uint32_t* flash_info = get_flash_info(ip_addr);
+ std::cout << boost::format("Querying %s for flash information.\n") % filename_map[hw_rev];
+ std::cout << boost::format(" * Flash size: %3.2f\n") % flash_info[1];
+ std::cout << boost::format(" * Sector size: %3.2f\n\n") % flash_info[0];
+
+ //Burning images
+
+ if(burn_fpga){
+ std::ifstream to_read_fpga((char*)fpga_path.c_str(), std::ios::binary);
+ to_read_fpga.seekg(0, std::ios::end);
+ int fpga_image_size = to_read_fpga.tellg();
+ to_read_fpga.seekg(0, std::ios::beg);
+ char fpga_read[FPGA_IMAGE_SIZE_BYTES];
+ to_read_fpga.read(fpga_read,fpga_image_size);
+ to_read_fpga.close();
+ boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES];
+ for(int i = 0; i < fpga_image_size; i++) fpga_image[i] = (boost::uint8_t)fpga_read[i];
+
+ erase_image(udp_transport, false, flash_info[1]);
+ write_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size);
+ verify_image(udp_transport, false, fpga_image, flash_info[1], fpga_image_size);
+ }
+
+ if(burn_fpga and burn_fw) std::cout << std::endl; //Formatting
+
+ if(burn_fw){
+ std::ifstream to_read_fw((char*)fw_path.c_str(), std::ios::binary);
+ to_read_fw.seekg(0, std::ios::end);
+ int fw_image_size = to_read_fw.tellg();
+ to_read_fw.seekg(0, std::ios::beg);
+ char fw_read[FW_IMAGE_SIZE_BYTES];
+ to_read_fw.read(fw_read,fw_image_size);
+ to_read_fw.close();
+ boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES];
+ for(int i = 0; i < fw_image_size; i++) fw_image[i] = (boost::uint8_t)fw_read[i];
+
+ erase_image(udp_transport, true, flash_info[1]);
+ write_image(udp_transport, true, fw_image, flash_info[1], fw_image_size);
+ verify_image(udp_transport, true, fw_image, flash_info[1], fw_image_size);
+ }
+
+ //Prompt user to reset USRP
+ std::string user_response = "";
+ bool reset = false;
+ while(user_response != "yes" and user_response != "no" and user_response != "y" and user_response != "n"){
+ std::cout << std::endl << "Image burning successful. Reset USRP (yes/no)? ";
+ std::getline(std::cin, user_response);
+ std::transform(user_response.begin(), user_response.end(), user_response.begin(), ::tolower);
+ reset = (user_response == "yes" or user_response == "y");
+ }
+ std::cout << std::endl; //Formatting
+ if(reset) reset_usrp(udp_transport);
+ else return 0;
+
+ return 0;
+}
diff --git a/host/utils/usrp_simple_burner_utils.hpp b/host/utils/usrp_simple_burner_utils.hpp
new file mode 100644
index 000000000..f386c3620
--- /dev/null
+++ b/host/utils/usrp_simple_burner_utils.hpp
@@ -0,0 +1,99 @@
+//
+// Copyright 2012 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 <math.h>
+#include <stdint.h>
+
+#include <boost/foreach.hpp>
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+
+#include <uhd/exception.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/utils/msg.hpp>
+
+#define UDP_FW_UPDATE_PORT 49154
+#define UDP_MAX_XFER_BYTES 1024
+#define UDP_TIMEOUT 3
+#define UDP_POLL_INTERVAL 0.10 //in seconds
+#define USRP2_FW_PROTO_VERSION 7 //should be unused after r6
+#define USRP2_UDP_UPDATE_PORT 49154
+#define FLASH_DATA_PACKET_SIZE 256
+#define FPGA_IMAGE_SIZE_BYTES 1572864
+#define FW_IMAGE_SIZE_BYTES 31744
+#define PROD_FPGA_IMAGE_LOCATION_ADDR 0x00180000
+#define PROD_FW_IMAGE_LOCATION_ADDR 0x00300000
+#define SAFE_FPGA_IMAGE_LOCATION_ADDR 0x00000000
+#define SAFE_FW_IMAGE_LOCATION_ADDR 0x003F0000
+
+using namespace uhd;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+typedef enum {
+ USRP2_FW_UPDATE_ID_WAT = ' ',
+
+ USRP2_FW_UPDATE_ID_OHAI_LOL = 'a',
+ USRP2_FW_UPDATE_ID_OHAI_OMG = 'A',
+
+ USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f',
+ USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F',
+
+ USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e',
+ USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E',
+
+ USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd',
+ USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D',
+ USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B',
+
+ USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w',
+ USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W',
+
+ USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r',
+ USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R',
+
+ USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's',
+ USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S',
+
+ USRP2_FW_UPDATE_ID_I_CAN_HAS_HW_REV_LOL = 'v',
+ USRP2_FW_UPDATE_ID_HERES_TEH_HW_REV_OMG = 'V',
+
+ USRP2_FW_UPDATE_ID_KTHXBAI = '~'
+
+} usrp2_fw_update_id_t;
+
+typedef struct {
+ uint32_t proto_ver;
+ uint32_t id;
+ uint32_t seq;
+ union {
+ uint32_t ip_addr;
+ uint32_t hw_rev;
+ struct {
+ uint32_t flash_addr;
+ uint32_t length;
+ uint8_t data[256];
+ } flash_args;
+ struct {
+ uint32_t sector_size_bytes;
+ uint32_t memory_size_bytes;
+ } flash_info_args;
+ } data;
+} usrp2_fw_update_data_t;