diff options
Diffstat (limited to 'host/utils')
-rw-r--r-- | host/utils/CMakeLists.txt | 23 | ||||
-rwxr-xr-x | host/utils/FastSendDatagramThreshold.reg | bin | 0 -> 312 bytes | |||
-rw-r--r-- | host/utils/query_gpsdo_sensors.cpp | 122 | ||||
-rw-r--r-- | host/utils/uhd_cal_rx_iq_balance.cpp | 5 | ||||
-rw-r--r-- | host/utils/uhd_cal_tx_dc_offset.cpp | 5 | ||||
-rw-r--r-- | host/utils/uhd_cal_tx_iq_balance.cpp | 5 | ||||
-rw-r--r-- | host/utils/uhd_images_downloader.py.in | 109 | ||||
-rw-r--r-- | host/utils/uhd_usrp_probe.cpp | 12 | ||||
-rw-r--r-- | host/utils/usrp_cal_utils.hpp | 46 | ||||
-rwxr-xr-x | host/utils/usrp_n2xx_net_burner.py | 16 | ||||
-rwxr-xr-x | host/utils/usrp_n2xx_net_burner_gui.py | 9 | ||||
-rw-r--r-- | host/utils/usrp_n2xx_simple_net_burner.cpp | 518 | ||||
-rw-r--r-- | host/utils/usrp_simple_burner_utils.hpp | 99 |
13 files changed, 949 insertions, 20 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 0ecd6b4e7..9e997071e 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-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 @@ -38,8 +38,10 @@ ENDFOREACH(util_source) # Utilities that get installed into the share path ######################################################################## 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) @@ -64,6 +66,17 @@ FOREACH(util_source ${util_share_sources}) INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +#UHD images downloader configuration +CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/uhd_images_downloader.py.in + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py +@ONLY) +INSTALL(PROGRAMS + ${CMAKE_CURRENT_BINARY_DIR}/uhd_images_downloader.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities +) + IF(ENABLE_USRP2) IF(WIN32 AND UHD_RELEASE_MODE) #include dd.exe FILE(DOWNLOAD @@ -76,8 +89,14 @@ IF(ENABLE_USRP2) COMPONENT utilities ) ENDIF(WIN32 AND UHD_RELEASE_MODE) + IF(LINUX) + INSTALL(PROGRAMS + usrp2_recovery.py + DESTINATION ${PKG_LIB_DIR}/utils + COMPONENT utilities + ) + ENDIF(LINUX) INSTALL(PROGRAMS - usrp2_recovery.py usrp2_card_burner.py usrp2_card_burner_gui.py usrp_n2xx_net_burner.py diff --git a/host/utils/FastSendDatagramThreshold.reg b/host/utils/FastSendDatagramThreshold.reg Binary files differnew file mode 100755 index 000000000..c0665d09e --- /dev/null +++ b/host/utils/FastSendDatagramThreshold.reg diff --git a/host/utils/query_gpsdo_sensors.cpp b/host/utils/query_gpsdo_sensors.cpp new file mode 100644 index 000000000..de6bdcd72 --- /dev/null +++ b/host/utils/query_gpsdo_sensors.cpp @@ -0,0 +1,122 @@ +// +// 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 <uhd/utils/paths.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <boost/filesystem.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> +#include <boost/thread.hpp> +#include <string> +#include <cmath> +#include <cstdlib> + +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +void print_notes(void) { + // Helpful notes + std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n"); + std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n"); + std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n"); + std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n"); + std::cout << boost::format("****************************************************************************************************************\n"); +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + std::string args; + + //Set up program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "Specify a single USRP.") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //Print the help message + if (vm.count("help")) { + std::cout << boost::format("Query GPSDO Sensors %s") % desc << std::endl; + return EXIT_FAILURE; + } + + //Create a USRP device + std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string(); + + print_notes(); + + + //Verify GPS sensors are present (i.e. EEPROM has been burnt) + std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0); + + if(std::find(sensor_names.begin(), sensor_names.end(), "gps_locked") == sensor_names.end()) { + std::cout << boost::format("\ngps_locked sensor not found. This could mean that you have not installed the GPSDO correctly.\n\n"); + std::cout << boost::format("Visit this page if the problem persists:\n"); + std::cout << boost::format("http://files.ettus.com/uhd_docs/manual/html/gpsdo.html\n\n"); + exit(EXIT_FAILURE); + } + + //Check for GPS lock + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked",0); + if(not gps_locked.to_bool()) { + std::cout << boost::format("\nGPS does not have lock. Wait a few minutes and try again.\n"); + std::cout << boost::format("NMEA strings and device time may not be accurate until lock is achieved.\n\n"); + } else + std::cout << boost::format("GPS Locked\n"); + + //Check for 10 MHz lock + if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) { + uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("ref_locked",0); + if(not gps_locked.to_bool()) { + std::cout << boost::format("USRP NOT Locked to GPSDO 10 MHz Reference.\n"); + std::cout << boost::format("Double check installation instructions: https://www.ettus.com/content/files/gpsdo-kit_2.pdf\n\n"); + } else + std::cout << boost::format("USRP Locked to GPSDO 10 MHz Reference.\n"); + }else + std::cout << boost::format("ref_locked sensor not present on this board.\n"); + + //Check PPS and compare UHD device time to GPS time + boost::this_thread::sleep(boost::posix_time::seconds(1)); + uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time"); + const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps(); + if (last_pps_time.get_full_secs() == gps_time.to_int()) { + std::cout << boost::format("GPS and UHD Device time are aligned.\n"); + } else + std::cout << boost::format("\nGPS and UHD Device time are NOT aligned. Try re-running the program. Double check 1 PPS connection from GPSDO.\n\n"); + + //print NMEA strings + std::cout << boost::format("Printing available NMEA strings:\n"); + uhd::sensor_value_t gga_string = usrp->get_mboard_sensor("gps_gpgga"); + uhd::sensor_value_t rmc_string = usrp->get_mboard_sensor("gps_gprmc"); + std::cout << boost::format("%s\n%s\n%s\n") % gga_string.to_pp_string() % rmc_string.to_pp_string() % gps_time.to_pp_string(); + std::cout << boost::format("UHD Device time: %.0f seconds\n") % (last_pps_time.get_real_secs()); + + //finished + std::cout << boost::format("\nDone!\n\n"); + + return EXIT_SUCCESS; +} diff --git a/host/utils/uhd_cal_rx_iq_balance.cpp b/host/utils/uhd_cal_rx_iq_balance.cpp index 7b6f10f3b..68d0443da 100644 --- a/host/utils/uhd_cal_rx_iq_balance.cpp +++ b/host/utils/uhd_cal_rx_iq_balance.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,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 @@ -135,6 +135,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_rx_antenna("CAL"); usrp->set_tx_antenna("CAL"); + //fail if daughterboard has no serial + check_for_empty_serial(usrp, "RX", "rx", args); + //set optimum defaults set_optimum_defaults(usrp); diff --git a/host/utils/uhd_cal_tx_dc_offset.cpp b/host/utils/uhd_cal_tx_dc_offset.cpp index 1b2510ba4..8f69b3ce1 100644 --- a/host/utils/uhd_cal_tx_dc_offset.cpp +++ b/host/utils/uhd_cal_tx_dc_offset.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,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 @@ -138,6 +138,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_rx_antenna("CAL"); usrp->set_tx_antenna("CAL"); + //fail if daughterboard has no serial + check_for_empty_serial(usrp, "TX", "tx", args); + //set optimum defaults set_optimum_defaults(usrp); diff --git a/host/utils/uhd_cal_tx_iq_balance.cpp b/host/utils/uhd_cal_tx_iq_balance.cpp index cff3d1646..5478b07e3 100644 --- a/host/utils/uhd_cal_tx_iq_balance.cpp +++ b/host/utils/uhd_cal_tx_iq_balance.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,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 @@ -138,6 +138,9 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ usrp->set_rx_antenna("CAL"); usrp->set_tx_antenna("CAL"); + //fail if daughterboard has no serial + check_for_empty_serial(usrp, "TX", "tx", args); + //set optimum defaults set_optimum_defaults(usrp); diff --git a/host/utils/uhd_images_downloader.py.in b/host/utils/uhd_images_downloader.py.in new file mode 100644 index 000000000..8c8d2df81 --- /dev/null +++ b/host/utils/uhd_images_downloader.py.in @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# +# 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/>. +# + +import atexit +from optparse import OptionParser +import os +import os.path +import shutil +import sys +import tempfile +import urllib2 +import zipfile + +class temp_dir(): + + def __enter__(self): + self.name = tempfile.mkdtemp() + return self.name + def __exit__(self, type, value, traceback): + os.removedirs(self.name) + +if __name__ == "__main__": + + #Command line options + parser = OptionParser() + parser.add_option("--install-location", type="string", default="", help="Set custom install location for images") + parser.add_option("--buffer-size", type="int", default=8192, help="Set download buffer size, [default=%default]",) + (options, args) = parser.parse_args() + + #Configuring image download info + images_src = "@UHD_IMAGES_DOWNLOAD_SRC@" + filename = images_src.split("/")[-1] + + with temp_dir() as dirname: + os.chdir(dirname) + + #Configuring image destination + if options.install_location != "": + images_dir = options.install_location + elif os.environ.get("UHD_IMAGES_DIR") != "" and os.environ.get("UHD_IMAGES_DIR") != None: + images_dir = os.environ.get("UHD_IMAGES_DIR") + else: + images_dir = "@CMAKE_INSTALL_PREFIX@/share/uhd/images" + + u = urllib2.urlopen(images_src) + f = open(filename, "wb") + meta = u.info() + filesize = float(meta.getheaders("Content-Length")[0]) + + print "Downloading images from: %s" % images_src + + filesize_dl = 0.0 + + #Downloading file + while True: + buffer = u.read(options.buffer_size) + if not buffer: + break + + filesize_dl -= len(buffer) + f.write(buffer) + + status = r"%2.2f MB/%2.2f MB (%3.2f" % (-filesize_dl/1e6, filesize/1e6, (-filesize_dl*100.)/filesize) + r"%)" + status += chr(8)*(len(status)+1) + print status, + + f.close() + + #Extracting contents of zip file + if os.path.exists("tempdir"): + shutil.rmtree("tempdir") + os.mkdir("tempdir") + + images_zip = zipfile.ZipFile(filename) + images_zip.extractall("tempdir") + + #Removing images currently in images_dir + if os.path.exists(images_dir): + try: + shutil.rmtree(images_dir) + except: + sys.stderr.write("\nMake sure you have write permissions in the images directory.\n") + sys.exit(0) + + #Copying downloaded images into images_dir + shutil.copytree("tempdir/%s/share/uhd/images" % filename[:-4],images_dir) + + #Removing tempdir and zip file + shutil.rmtree("tempdir") + images_zip.close() + os.remove(filename) + + os.chdir(images_dir) + print "\nImages successfully installed to: %s" % images_dir diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp index 1bd49a5ff..5b3702fb4 100644 --- a/host/utils/uhd_usrp_probe.cpp +++ b/host/utils/uhd_usrp_probe.cpp @@ -69,9 +69,9 @@ static std::string prop_names_to_pp_string(const std::vector<std::string> &prop_ return ss.str(); } -static std::string get_subdev_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ +static std::string get_frontend_pp_string(const std::string &type, property_tree::sptr tree, const fs_path &path){ std::stringstream ss; - ss << boost::format("%s Subdev: %s") % type % path.leaf() << std::endl; + ss << boost::format("%s Frontend: %s") % type % path.leaf() << std::endl; //ss << std::endl; ss << boost::format("Name: %s") % (tree->access<std::string>(path / "name").get()) << std::endl; @@ -123,7 +123,7 @@ static std::string get_dboard_pp_string(const std::string &type, property_tree:: if (not gdb_eeprom.serial.empty()) ss << boost::format("Serial: %s") % gdb_eeprom.serial << std::endl; } BOOST_FOREACH(const std::string &name, tree->list(path / (prefix + "_frontends"))){ - ss << make_border(get_subdev_pp_string(type, tree, path / (prefix + "_frontends") / name)); + ss << make_border(get_frontend_pp_string(type, tree, path / (prefix + "_frontends") / name)); } ss << make_border(get_codec_pp_string(type, tree, path.branch_path().branch_path() / (prefix + "_codecs") / path.leaf())); return ss.str(); @@ -137,6 +137,12 @@ static std::string get_mboard_pp_string(property_tree::sptr tree, const fs_path BOOST_FOREACH(const std::string &key, mb_eeprom.keys()){ if (not mb_eeprom[key].empty()) ss << boost::format("%s: %s") % key % mb_eeprom[key] << std::endl; } + if (tree->exists(path / "fw_version")){ + ss << "FW Version: " << tree->access<std::string>(path / "fw_version").get() << std::endl; + } + if (tree->exists(path / "fpga_version")){ + ss << "FPGA Version: " << tree->access<std::string>(path / "fpga_version").get() << std::endl; + } ss << std::endl; ss << "Time sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "time_source" / "options").get()) << std::endl; ss << "Clock sources: " << prop_names_to_pp_string(tree->access<std::vector<std::string> >(path / "clock_source" / "options").get()) << std::endl; diff --git a/host/utils/usrp_cal_utils.hpp b/host/utils/usrp_cal_utils.hpp index ba9ebe35a..bda6fc31b 100644 --- a/host/utils/usrp_cal_utils.hpp +++ b/host/utils/usrp_cal_utils.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2011-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 @@ -19,11 +19,14 @@ #include <uhd/property_tree.hpp> #include <uhd/usrp/multi_usrp.hpp> #include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/utils/paths.hpp> #include <boost/filesystem.hpp> +#include <boost/format.hpp> #include <iostream> #include <vector> #include <complex> #include <cmath> +#include <cstdlib> #include <fstream> namespace fs = boost::filesystem; @@ -68,7 +71,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0"; const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get(); - if (tx_name.find("WBX") != std::string::npos or tx_name.find("SBX") != std::string::npos){ + if (tx_name.find("WBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("SBX") != std::string::npos){ + usrp->set_tx_gain(0); + } + else if (tx_name.find("RFX") != std::string::npos){ usrp->set_tx_gain(0); } else{ @@ -77,7 +86,13 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/rx_frontends/0"; const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get(); - if (rx_name.find("WBX") != std::string::npos or rx_name.find("SBX") != std::string::npos){ + if (rx_name.find("WBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("SBX") != std::string::npos){ + usrp->set_rx_gain(25); + } + else if (rx_name.find("RFX") != std::string::npos){ usrp->set_rx_gain(25); } else{ @@ -87,6 +102,30 @@ static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){ } /*********************************************************************** + * Check for empty serial + **********************************************************************/ + +void check_for_empty_serial( + uhd::usrp::multi_usrp::sptr usrp, + std::string XX, + std::string xx, + std::string uhd_args +){ + + //extract eeprom + uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); + const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom"; + const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); + + std::string args_str = ""; + if(uhd_args != "") args_str = str(boost::format(" --args=%s") % uhd_args); + + std::string error_string = str(boost::format("This %s dboard has no serial!\n\nPlease see the Calibration documentation for details on how to fix this.") % XX); + + if (db_eeprom.serial.empty()) throw std::runtime_error(error_string); +} + +/*********************************************************************** * Sinusoid wave table **********************************************************************/ class wave_table{ @@ -148,7 +187,6 @@ static void store_results( uhd::property_tree::sptr tree = usrp->get_device()->get_tree(); const uhd::fs_path db_path = "/mboards/0/dboards/A/" + xx + "_eeprom"; const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); - if (db_eeprom.serial.empty()) throw std::runtime_error(XX + " dboard has empty serial!"); //make the calibration file path fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd"; diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index f2cfb8ecf..8f16de501 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -222,7 +222,9 @@ def enumerate_devices(): pkt = sock.recv(UDP_MAX_XFER_BYTES) (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(pkt) if(pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG): - yield socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + use_addr = socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))) + burner = burner_socket(use_addr, True) + yield "%s (%s)" % (socket.inet_ntoa(struct.pack("<L", socket.ntohl(ip_addr))), n2xx_revs[burner.get_hw_rev()][0]) except socket.timeout: still_goin = False @@ -230,12 +232,13 @@ def enumerate_devices(): # Burner class, holds a socket and send/recv routines ######################################################################## class burner_socket(object): - def __init__(self, addr): + def __init__(self, addr, quiet): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._quiet = quiet self._sock.settimeout(UDP_TIMEOUT) self._sock.connect((addr, UDP_FW_UPDATE_PORT)) self.set_callbacks(lambda *a: None, lambda *a: None) - self.init_update() #check that the device is there + self.init_update(quiet) #check that the device is there self.get_hw_rev() def set_callbacks(self, progress_cb, status_cb): @@ -247,13 +250,13 @@ class burner_socket(object): return self._sock.recv(UDP_MAX_XFER_BYTES) #just here to validate comms - def init_update(self): + def init_update(self,quiet): out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0) try: in_pkt = self.send_and_recv(out_pkt) except socket.timeout: raise Exception("No response from device") (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: - print("USRP-N2XX found.") + if not quiet: print("USRP-N2XX found.") else: raise Exception("Invalid reply received from device.") @@ -488,6 +491,7 @@ if __name__=='__main__': if options.list: print('Possible network devices:') print(' ' + '\n '.join(enumerate_devices())) + #enumerate_devices() exit() if not options.addr: raise Exception('no address specified') @@ -500,7 +504,7 @@ if __name__=='__main__': response = raw_input("""Type "yes" to continue, or anything else to quit: """) if response != "yes": sys.exit(0) - burner = burner_socket(addr=options.addr) + burner = burner_socket(addr=options.addr,quiet=False) if options.read: if options.fw: diff --git a/host/utils/usrp_n2xx_net_burner_gui.py b/host/utils/usrp_n2xx_net_burner_gui.py index e2b79e72c..a9150bd88 100755 --- a/host/utils/usrp_n2xx_net_burner_gui.py +++ b/host/utils/usrp_n2xx_net_burner_gui.py @@ -96,7 +96,11 @@ class DeviceEntryWidget(tkinter.Frame): tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() self._hints = tkinter.Listbox(self) + self._hints_addrs_only = tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + self._hints_addrs_only.bind("<<ListboxSelect>>", self._listbox_cb) + self._reload_cb() self._hints.pack(expand=tkinter.YES, fill=tkinter.X) @@ -112,10 +116,11 @@ class DeviceEntryWidget(tkinter.Frame): self._hints.delete(0, tkinter.END) for hint in usrp_n2xx_net_burner.enumerate_devices(): self._hints.insert(tkinter.END, hint) + self._hints_addrs_only.insert(tkinter.END, hint.split(" (")[0]) def _listbox_cb(self, event): try: - sel = self._hints.get(self._hints.curselection()[0]) + sel = self._hints_addrs_only.get(self._hints.curselection()[0]) self._entry.delete(0, tkinter.END) self._entry.insert(0, sel) except Exception as e: print(e) @@ -196,7 +201,7 @@ class USRPN2XXNetBurnerApp(tkinter.Frame): self._disable_input() try: #make a new burner object and attempt the burner operation - burner = usrp_n2xx_net_burner.burner_socket(addr=addr) + burner = usrp_n2xx_net_burner.burner_socket(addr=addr,quiet=False) for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): #setup callbacks that update the gui 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..901842538 --- /dev/null +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -0,0 +1,518 @@ +// +// 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/property_tree.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") +; + +//Images and image sizes, to be populated as necessary +boost::uint8_t fpga_image[FPGA_IMAGE_SIZE_BYTES]; +boost::uint8_t fw_image[FW_IMAGE_SIZE_BYTES]; +int fpga_image_size = 0; +int fw_image_size = 0; + +//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; +} + +/*********************************************************************** + * Custom filename validation functions + **********************************************************************/ + +void validate_custom_fpga_file(std::string rev_str, std::string fpga_path){ + + //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") % fpga_path)); + + //Check to find rev_str in filename + uhd::fs_path custom_fpga_path(fpga_path); + if(custom_fpga_path.leaf().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.leaf().find(rev_str) == std::string::npos){ + throw std::runtime_error(str(boost::format("Invalid FPGA image filename at path: %s\nFilename must contain '%s' to be considered valid for this model.") + % fpga_path % rev_str)); + } +} + +void validate_custom_fw_file(std::string rev_str, std::string fw_path){ + + //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 + uhd::fs_path custom_fw_path(fw_path); + if(custom_fw_path.leaf().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.leaf().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))); + } +} + +/*********************************************************************** + * Grabbing and validating image binaries + **********************************************************************/ + +int grab_fpga_image(std::string fpga_path){ + + //Reading FPGA image from file + std::ifstream to_read_fpga((char*)fpga_path.c_str(), std::ios::binary); + to_read_fpga.seekg(0, std::ios::end); + 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(); + for(int i = 0; i < fpga_image_size; i++) fpga_image[i] = (boost::uint8_t)fpga_read[i]; + + //Checking validity of image + if(fpga_image_size > FPGA_IMAGE_SIZE_BYTES){ + throw std::runtime_error(str(boost::format("FPGA image is too large. %d > %d") % fpga_image_size % FPGA_IMAGE_SIZE_BYTES)); + } + + //Check sequence of bytes in image + bool is_good = false; + for(int i = 0; i < 63; i++){ + if((boost::uint8_t)fpga_image[i] == 255) continue; + else if((boost::uint8_t)fpga_image[i] == 170 and + (boost::uint8_t)fpga_image[i+1] == 153){ + is_good = true; + break; + } + } + + if(!is_good) throw std::runtime_error("Not a valid FPGA image."); + + //Return image size + return fpga_image_size; +} + +int grab_fw_image(std::string fw_path){ + + //Reading firmware image from file + std::ifstream to_read_fw((char*)fw_path.c_str(), std::ios::binary); + to_read_fw.seekg(0, std::ios::end); + 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(); + for(int i = 0; i < fw_image_size; i++) fw_image[i] = (boost::uint8_t)fw_read[i]; + + //Checking validity of image + if(fw_image_size > FW_IMAGE_SIZE_BYTES){ + throw std::runtime_error(str(boost::format("Firmware image is too large. %d > %d") % fw_image_size % FW_IMAGE_SIZE_BYTES)); + } + + //Check first four bytes of image + for(int i = 0; i < 4; i++) if((boost::uint8_t)fw_image[i] != 11) throw std::runtime_error("Not a valid firmware image."); + + //Return image size + return fw_image_size; +} + +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 + 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_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); + } + else if(ntohl(update_data_in->id) != USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG){ + throw std::runtime_error(str(boost::format("Received invalid reply %d from device.\n") % ntohl(update_data_in->id))); + } + + return flash_info; +} + +/*********************************************************************** + * Image burning functions + **********************************************************************/ + +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 EXIT_FAILURE; + } + + 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 EXIT_FAILURE; + } + + 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 EXIT_FAILURE; + } + 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; + if(burn_fpga){ + if(!use_custom_fpga) fpga_path = find_image_path(default_fpga_filename); + else validate_custom_fpga_file(filename_map[hw_rev], fpga_path); + + grab_fpga_image(fpga_path); + } + if(burn_fw){ + if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); + else validate_custom_fw_file(filename_map[hw_rev], fw_path); + + grab_fw_image(fw_path); + } + + 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){ + 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){ + 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 = "foo"; + bool reset = false; + while(user_response != "y" and user_response != "" and user_response != "n"){ + std::cout << std::endl << "Image burning successful. Reset USRP (Y/n)? "; + std::getline(std::cin, user_response); + std::transform(user_response.begin(), user_response.end(), user_response.begin(), ::tolower); + reset = (user_response == "" or user_response == "y"); + } + std::cout << std::endl; //Formatting + if(reset) reset_usrp(udp_transport); + else return EXIT_SUCCESS; + + return EXIT_SUCCESS; +} 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; |