aboutsummaryrefslogtreecommitdiffstats
path: root/host/utils
diff options
context:
space:
mode:
Diffstat (limited to 'host/utils')
-rw-r--r--host/utils/CMakeLists.txt39
-rw-r--r--host/utils/uhd_burn_db_eeprom.cpp103
-rw-r--r--host/utils/uhd_find_devices.cpp60
-rw-r--r--host/utils/uhd_usrp_probe.cpp166
-rw-r--r--host/utils/usrp2_addr_burner.cpp90
-rwxr-xr-xhost/utils/usrp2_card_burner.py226
-rwxr-xr-xhost/utils/usrp2_card_burner_gui.py169
-rwxr-xr-xhost/utils/usrp2_recovery.py68
8 files changed, 921 insertions, 0 deletions
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
new file mode 100644
index 000000000..8d260c06c
--- /dev/null
+++ b/host/utils/CMakeLists.txt
@@ -0,0 +1,39 @@
+#
+# Copyright 2010 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/>.
+#
+
+ADD_EXECUTABLE(uhd_find_devices uhd_find_devices.cpp)
+TARGET_LINK_LIBRARIES(uhd_find_devices uhd)
+INSTALL(TARGETS uhd_find_devices RUNTIME DESTINATION ${RUNTIME_DIR})
+
+ADD_EXECUTABLE(uhd_usrp_probe uhd_usrp_probe.cpp)
+TARGET_LINK_LIBRARIES(uhd_usrp_probe uhd)
+INSTALL(TARGETS uhd_usrp_probe RUNTIME DESTINATION ${RUNTIME_DIR})
+
+ADD_EXECUTABLE(usrp2_addr_burner usrp2_addr_burner.cpp)
+TARGET_LINK_LIBRARIES(usrp2_addr_burner uhd)
+INSTALL(TARGETS usrp2_addr_burner RUNTIME DESTINATION ${PKG_DATA_DIR}/utils)
+
+ADD_EXECUTABLE(uhd_burn_db_eeprom uhd_burn_db_eeprom.cpp)
+TARGET_LINK_LIBRARIES(uhd_burn_db_eeprom uhd)
+INSTALL(TARGETS uhd_burn_db_eeprom RUNTIME DESTINATION ${PKG_DATA_DIR}/utils)
+
+INSTALL(PROGRAMS
+ usrp2_recovery.py
+ usrp2_card_burner.py
+ usrp2_card_burner_gui.py
+ DESTINATION ${PKG_DATA_DIR}/utils
+)
diff --git a/host/utils/uhd_burn_db_eeprom.cpp b/host/utils/uhd_burn_db_eeprom.cpp
new file mode 100644
index 000000000..dfd9decba
--- /dev/null
+++ b/host/utils/uhd_burn_db_eeprom.cpp
@@ -0,0 +1,103 @@
+//
+// Copyright 2010 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/safe_main.hpp>
+#include <uhd/device.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <uhd/usrp/device_props.hpp>
+#include <uhd/usrp/mboard_props.hpp>
+#include <uhd/usrp/dboard_props.hpp>
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+#include <boost/assign.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+namespace po = boost::program_options;
+
+//used with lexical cast to parse a hex string
+template <class T> struct to_hex{
+ T value;
+ operator T() const {return value;}
+ friend std::istream& operator>>(std::istream& in, to_hex& out){
+ in >> std::hex >> out.value;
+ return in;
+ }
+};
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+ //command line variables
+ std::string args, db_name, unit;
+ static const uhd::dict<std::string, mboard_prop_t> unit_to_db_prop = boost::assign::map_list_of
+ ("RX", MBOARD_PROP_RX_DBOARD) ("TX", MBOARD_PROP_TX_DBOARD)
+ ;
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]")
+ ("db", po::value<std::string>(&db_name)->default_value(""), "dboard name [default = \"\"]")
+ ("unit", po::value<std::string>(&unit)->default_value(""), "which unit [RX or TX]")
+ ("id", po::value<std::string>(), "dboard id to burn (hex string), omit for readback")
+ ;
+
+ 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("UHD Burn DB EEPROM %s") % desc << std::endl;
+ std::cout << boost::format(
+ "Omit the id argument to perform readback,\n"
+ "Or specify a new id to burn into the eeprom.\n"
+ ) << std::endl;
+ return ~0;
+ }
+
+ //check inputs
+ if (not unit_to_db_prop.has_key(unit)){
+ std::cout << "Error: specify RX or TX for unit" << std::endl;
+ return ~0;
+ }
+
+ //make the device and extract the dboard w/ property
+ device::sptr dev = device::make(args);
+ wax::obj dboard = (*dev)[DEVICE_PROP_MBOARD][named_prop_t(unit_to_db_prop[unit], db_name)];
+ std::string prefix = (db_name == "")? unit : (unit + ":" + db_name);
+
+ //read the current dboard id from eeprom
+ if (vm.count("id") == 0){
+ std::cout << boost::format("Getting dbid on %s dboard...") % prefix << std::endl;
+ dboard_id_t id = dboard[DBOARD_PROP_DBOARD_ID].as<dboard_id_t>();
+ std::cout << boost::format(" Current dbid: %s") % id.to_pp_string() << std::endl;
+ }
+
+ //write a new dboard id to eeprom
+ else{
+ dboard_id_t id = dboard_id_t::from_string(vm["id"].as<std::string>());
+ std::cout << boost::format("Setting dbid on %s dboard...") % prefix << std::endl;
+ std::cout << boost::format(" New dbid: %s") % id.to_pp_string() << std::endl;
+ dboard[DBOARD_PROP_DBOARD_ID] = id;
+ }
+
+ std::cout << " Done" << std::endl << std::endl;
+ return 0;
+}
diff --git a/host/utils/uhd_find_devices.cpp b/host/utils/uhd_find_devices.cpp
new file mode 100644
index 000000000..b778eeb68
--- /dev/null
+++ b/host/utils/uhd_find_devices.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2010 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/safe_main.hpp>
+#include <uhd/device.hpp>
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("args", po::value<std::string>()->default_value(""), "device address args")
+ ;
+
+ 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("UHD Find Devices %s") % desc << std::endl;
+ return ~0;
+ }
+
+ //discover the usrps and print the results
+ uhd::device_addrs_t device_addrs = uhd::device::find(vm["args"].as<std::string>());
+
+ if (device_addrs.size() == 0){
+ std::cerr << "No UHD Devices Found" << std::endl;
+ return ~0;
+ }
+
+ for (size_t i = 0; i < device_addrs.size(); i++){
+ std::cout << "--------------------------------------------------" << std::endl;
+ std::cout << "-- UHD Device " << i << std::endl;
+ std::cout << "--------------------------------------------------" << std::endl;
+ std::cout << device_addrs[i].to_pp_string() << std::endl << std::endl;
+ //uhd::device::make(device_addrs[i]); //test make
+ }
+
+ return 0;
+}
diff --git a/host/utils/uhd_usrp_probe.cpp b/host/utils/uhd_usrp_probe.cpp
new file mode 100644
index 000000000..1e8e726d2
--- /dev/null
+++ b/host/utils/uhd_usrp_probe.cpp
@@ -0,0 +1,166 @@
+//
+// Copyright 2010 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/safe_main.hpp>
+#include <uhd/device.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/usrp/device_props.hpp>
+#include <uhd/usrp/mboard_props.hpp>
+#include <uhd/usrp/dboard_props.hpp>
+#include <uhd/usrp/dsp_props.hpp>
+#include <uhd/usrp/subdev_props.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <boost/program_options.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <iostream>
+#include <sstream>
+
+namespace po = boost::program_options;
+using namespace uhd;
+
+static std::string indent(size_t level){
+ return (level)? (indent(level-1) + " ") : "";
+}
+
+static std::string make_border(const std::string &text){
+ std::stringstream ss;
+ ss << boost::format(" _____________________________________________________") << std::endl;
+ ss << boost::format(" /") << std::endl;
+ std::vector<std::string> lines; boost::split(lines, text, boost::is_any_of("\n"));
+ while (lines.back() == "") lines.pop_back(); //strip trailing newlines
+ if (lines.size()) lines[0] = " " + lines[0]; //indent the title line
+ BOOST_FOREACH(const std::string &line, lines){
+ ss << boost::format("| %s") % line << std::endl;
+ }
+ //ss << boost::format(" \\____________________________________________________") << std::endl;
+ return ss.str();
+}
+
+static std::string get_dsp_pp_string(const std::string &type, wax::obj dsp){
+ std::stringstream ss;
+ ss << boost::format("%s DSP: %s") % type % dsp[usrp::DSP_PROP_NAME].as<std::string>() << std::endl;
+ //ss << std::endl;
+ ss << boost::format("Codec Rate: %f Msps") % (dsp[usrp::DSP_PROP_CODEC_RATE].as<double>()/1e6) << std::endl;
+ //ss << boost::format("Host Rate: %f Msps") % (dsp[usrp::DSP_PROP_HOST_RATE].as<double>()/1e6) << std::endl;
+ //ss << boost::format("Freq Shift: %f Mhz") % (dsp[usrp::DSP_PROP_FREQ_SHIFT].as<double>()/1e6) << std::endl;
+ return ss.str();
+}
+
+static std::string prop_names_to_pp_string(const prop_names_t &prop_names){
+ std::stringstream ss; size_t count = 0;
+ BOOST_FOREACH(const std::string &prop_name, prop_names){
+ ss << ((count++)? ", " : "") << prop_name;
+ }
+ return ss.str();
+}
+
+static std::string get_subdev_pp_string(const std::string &type, wax::obj subdev){
+ std::stringstream ss;
+ ss << boost::format("%s Subdev: %s") % type % subdev[usrp::SUBDEV_PROP_NAME].as<std::string>() << std::endl;
+ //ss << std::endl;
+
+ prop_names_t ant_names(subdev[usrp::SUBDEV_PROP_ANTENNA_NAMES].as<prop_names_t>());
+ ss << boost::format("Antennas: %s") % prop_names_to_pp_string(ant_names) << std::endl;
+
+ freq_range_t freq_range(subdev[usrp::SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>());
+ ss << boost::format("Freq range: %.3f to %.3f Mhz") % (freq_range.min/1e6) % (freq_range.max/1e6) << std::endl;
+
+ prop_names_t gain_names(subdev[usrp::SUBDEV_PROP_GAIN_NAMES].as<prop_names_t>());
+ if (gain_names.size() == 0) ss << "Gain Elements: None" << std::endl;
+ BOOST_FOREACH(const std::string &gain_name, gain_names){
+ gain_range_t gain_range(subdev[named_prop_t(usrp::SUBDEV_PROP_GAIN_RANGE, gain_name)].as<gain_range_t>());
+ ss << boost::format("Gain range %s: %.1f to %.1f step %.1f dB") % gain_name % gain_range.min % gain_range.max % gain_range.step << std::endl;
+ }
+
+ ss << boost::format("Is Quadrature: %s") % (subdev[usrp::SUBDEV_PROP_QUADRATURE].as<bool>()? "Yes" : "No") << std::endl;
+ ss << boost::format("Is IQ Swapped: %s") % (subdev[usrp::SUBDEV_PROP_IQ_SWAPPED].as<bool>()? "Yes" : "No") << std::endl;
+ ss << boost::format("Is Spectrum Inverted: %s") % (subdev[usrp::SUBDEV_PROP_SPECTRUM_INVERTED].as<bool>()? "Yes" : "No") << std::endl;
+ ss << boost::format("Uses LO offset: %s") % (subdev[usrp::SUBDEV_PROP_USE_LO_OFFSET].as<bool>()? "Yes" : "No") << std::endl;
+
+ return ss.str();
+}
+
+static std::string get_dboard_pp_string(const std::string &type, wax::obj dboard){
+ std::stringstream ss;
+ ss << boost::format("%s Dboard: %s") % type % dboard[usrp::DBOARD_PROP_NAME].as<std::string>() << std::endl;
+ //ss << std::endl;
+ BOOST_FOREACH(const std::string &subdev_name, dboard[usrp::DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>()){
+ ss << make_border(get_subdev_pp_string(type, dboard[named_prop_t(usrp::DBOARD_PROP_SUBDEV, subdev_name)]));
+ }
+ return ss.str();
+}
+
+static std::string get_mboard_pp_string(wax::obj mboard){
+ std::stringstream ss;
+ ss << boost::format("Mboard: %s") % mboard[usrp::MBOARD_PROP_NAME].as<std::string>() << std::endl;
+ //ss << std::endl;
+ BOOST_FOREACH(const std::string &other_name, mboard[usrp::MBOARD_PROP_OTHERS].as<prop_names_t>()){
+ try{
+ ss << boost::format("%s: %s") % other_name % mboard[other_name].as<std::string>() << std::endl;
+ } catch(...){}
+ }
+ BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_RX_DSP_NAMES].as<prop_names_t>()){
+ ss << make_border(get_dsp_pp_string("RX", mboard[named_prop_t(usrp::MBOARD_PROP_RX_DSP, dsp_name)]));
+ }
+ BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_TX_DSP_NAMES].as<prop_names_t>()){
+ ss << make_border(get_dsp_pp_string("TX", mboard[named_prop_t(usrp::MBOARD_PROP_TX_DSP, dsp_name)]));
+ }
+ BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_RX_DBOARD_NAMES].as<prop_names_t>()){
+ ss << make_border(get_dboard_pp_string("RX", mboard[named_prop_t(usrp::MBOARD_PROP_RX_DBOARD, dsp_name)]));
+ }
+ BOOST_FOREACH(const std::string &dsp_name, mboard[usrp::MBOARD_PROP_TX_DBOARD_NAMES].as<prop_names_t>()){
+ ss << make_border(get_dboard_pp_string("TX", mboard[named_prop_t(usrp::MBOARD_PROP_TX_DBOARD, dsp_name)]));
+ }
+ return ss.str();
+}
+
+
+static std::string get_device_pp_string(device::sptr dev){
+ std::stringstream ss;
+ ss << boost::format("Device: %s") % (*dev)[usrp::DEVICE_PROP_NAME].as<std::string>() << std::endl;
+ //ss << std::endl;
+ BOOST_FOREACH(const std::string &mboard_name, (*dev)[usrp::DEVICE_PROP_MBOARD_NAMES].as<prop_names_t>()){
+ ss << make_border(get_mboard_pp_string((*dev)[named_prop_t(usrp::DEVICE_PROP_MBOARD, mboard_name)]));
+ }
+ return ss.str();
+}
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("args", po::value<std::string>()->default_value(""), "device address args")
+ ;
+
+ 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("UHD USRP Probe %s") % desc << std::endl;
+ return ~0;
+ }
+
+ device::sptr dev = device::make(vm["args"].as<std::string>());
+
+ std::cout << make_border(get_device_pp_string(dev)) << std::endl;
+
+ return 0;
+}
diff --git a/host/utils/usrp2_addr_burner.cpp b/host/utils/usrp2_addr_burner.cpp
new file mode 100644
index 000000000..08fc1e218
--- /dev/null
+++ b/host/utils/usrp2_addr_burner.cpp
@@ -0,0 +1,90 @@
+//
+// Copyright 2010 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/safe_main.hpp>
+#include <uhd/usrp/usrp2.hpp>
+#include <uhd/usrp/device_props.hpp>
+#include <boost/program_options.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+namespace po = boost::program_options;
+
+int UHD_SAFE_MAIN(int argc, char *argv[]){
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "help message")
+ ("addr", po::value<std::string>(), "resolvable network address")
+ ("new-ip", po::value<std::string>(), "new ip address (optional)")
+ ("new-mac", po::value<std::string>(), "new mac address (optional)")
+ ;
+
+ 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("USRP2 Address Burner %s") % desc << std::endl;
+ return ~0;
+ }
+
+ //load the options into the address
+ uhd::device_addr_t device_addr;
+ if (vm.count("addr")){
+ device_addr["addr"] = vm["addr"].as<std::string>();
+ }
+ else{
+ std::cerr << "Error: missing addr option" << std::endl;
+ return ~0;
+ }
+
+ //create a usrp2 device
+ uhd::device::sptr u2_dev = uhd::usrp::usrp2::make(device_addr);
+ //FIXME usees the default mboard for now (until the mimo link is supported)
+ wax::obj u2_mb = (*u2_dev)[uhd::usrp::DEVICE_PROP_MBOARD];
+ std::cout << std::endl;
+
+ //fetch and print current settings
+ std::cout << "Fetching current settings from usrp2 eeprom:" << std::endl;
+ std::string curr_ip = u2_mb[std::string("ip-addr")].as<std::string>();
+ std::cout << boost::format(" Current IP Address: %s") % curr_ip << std::endl;
+ std::string curr_mac = u2_mb[std::string("mac-addr")].as<std::string>();
+ std::cout << boost::format(" Current MAC Address: %s") % curr_mac << std::endl;
+ std::cout << " Done" << std::endl << std::endl;
+
+ //try to set the new ip (if provided)
+ if (vm.count("new-ip")){
+ std::cout << "Burning a new ip address into the usrp2 eeprom:" << std::endl;
+ std::string new_ip = vm["new-ip"].as<std::string>();
+ std::cout << boost::format(" New IP Address: %s") % new_ip << std::endl;
+ u2_mb[std::string("ip-addr")] = new_ip;
+ std::cout << " Done" << std::endl << std::endl;
+ }
+
+ //try to set the new mac (if provided)
+ if (vm.count("new-mac")){
+ std::cout << "Burning a new mac address into the usrp2 eeprom:" << std::endl;
+ std::string new_mac = vm["new-mac"].as<std::string>();
+ std::cout << boost::format(" New MAC Address: %s") % new_mac << std::endl;
+ u2_mb[std::string("mac-addr")] = new_mac;
+ std::cout << " Done" << std::endl << std::endl;
+ }
+
+ std::cout << "Power-cycle the usrp2 for the changes to take effect." << std::endl;
+ return 0;
+}
diff --git a/host/utils/usrp2_card_burner.py b/host/utils/usrp2_card_burner.py
new file mode 100755
index 000000000..d47a4f5f4
--- /dev/null
+++ b/host/utils/usrp2_card_burner.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 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 platform
+import tempfile
+import subprocess
+import urllib
+import optparse
+import os
+import re
+
+########################################################################
+# constants
+########################################################################
+SECTOR_SIZE = 512 # bytes
+MAX_FILE_SIZE = 1 * (2**20) # maximum number of bytes we'll burn to a slot
+
+FPGA_OFFSET = 0 # offset in flash to fpga image
+FIRMWARE_OFFSET = 1 * (2**20) # offset in flash to firmware image
+
+MAX_SD_CARD_SIZE = 2048e6 # bytes (any bigger is sdhc)
+
+########################################################################
+# helper functions
+########################################################################
+def command(*args):
+ p = subprocess.Popen(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ ret = p.wait()
+ verbose = p.stdout.read()
+ if ret != 0: raise Exception, verbose
+ return verbose
+
+def get_dd_path():
+ if platform.system() == 'Windows':
+ dd_path = os.path.join(tempfile.gettempdir(), 'dd.exe')
+ if not os.path.exists(dd_path):
+ print 'Downloading dd.exe to %s'%dd_path
+ dd_bin = urllib.urlopen('http://www.ettus.com/downloads/dd.exe').read()
+ open(dd_path, 'wb').write(dd_bin)
+ return dd_path
+ return 'dd'
+
+########################################################################
+# list possible devices
+########################################################################
+def get_raw_device_hints():
+ ####################################################################
+ # Platform Windows: parse the output of dd.exe --list
+ ####################################################################
+ if platform.system() == 'Windows':
+ def extract_info_value(info, key):
+ return info.split(key)[-1].split()[0]
+ def get_info_list(output):
+ in_info = False
+ for line in output.splitlines():
+ if line.startswith('\\\\'): in_info = True; info = ''
+ elif in_info and not line.strip(): in_info = False; yield info
+ if in_info: info += '\n'+line.strip()
+ def is_info_valid(info):
+ try:
+ assert 'link to' in info
+ #handles two spellings of remov(e)able:
+ assert 'remov' in info.lower()
+ if 'size is' in info: assert int(extract_info_value(info, 'size is')) <= MAX_SD_CARD_SIZE
+ return True
+ except: return False
+ def extract_info_name(info):
+ for key in ('Mounted on', 'link to'):
+ if key in info: return extract_info_value(info, key)
+ return info.splitlines()[0].strip()
+
+ return sorted(set(map(extract_info_name, filter(is_info_valid, get_info_list(command(get_dd_path(), '--list'))))))
+
+ ####################################################################
+ # Platform Linux: parse procfs /proc/partitions
+ ####################################################################
+ if platform.system() == 'Linux':
+ devs = list()
+ try: output = open('/proc/partitions', 'r').read()
+ except: return devs
+ for line in output.splitlines():
+ try:
+ major, minor, blocks, name = line.split()
+ assert not name[-1].isdigit() or int(minor) == 0
+ assert int(blocks)*1024 <= MAX_SD_CARD_SIZE
+ except: continue
+ devs.append(os.path.join('/dev', name))
+
+ return sorted(set(devs))
+
+ ####################################################################
+ # Platform Mac OS X: parse diskutil list and info commands
+ ####################################################################
+ if platform.system() == 'Darwin':
+ devs = map(lambda d: d.split()[0], filter(lambda l: l.startswith('/dev'), command('diskutil', 'list').splitlines()))
+ def output_to_info(output):
+ return dict([map(str.strip, pair.lower().split(':')) for pair in filter(lambda l: ':' in l, output.splitlines())])
+ def is_dev_valid(dev):
+ info = output_to_info(command('diskutil', 'info', dev))
+ try:
+ if info.has_key('internal'): assert info['internal'] == 'no'
+ if info.has_key('ejectable'): assert info['ejectable'] == 'yes'
+ if info.has_key('total size'):
+ size_match = re.match('^.*\((\d+)\s*bytes\).*$', info['total size'])
+ if size_match: assert int(size_match.groups()[0]) <= MAX_SD_CARD_SIZE
+ return True
+ except: return False
+
+ return sorted(set(filter(is_dev_valid, devs)))
+
+ ####################################################################
+ # Platform Others:
+ ####################################################################
+ return ()
+
+########################################################################
+# write and verify with dd
+########################################################################
+def verify_image(image_file, device_file, offset):
+ #create a temporary file to store the readback
+ tmp = tempfile.mkstemp()
+ os.close(tmp[0])
+ tmp_file = tmp[1]
+
+ #execute a dd subprocess
+ verbose = command(
+ get_dd_path(),
+ "of=%s"%tmp_file,
+ "if=%s"%device_file,
+ "skip=%d"%(offset/SECTOR_SIZE),
+ "bs=%d"%SECTOR_SIZE,
+ "count=%d"%(MAX_FILE_SIZE/SECTOR_SIZE),
+ )
+
+ #read in the image and readback
+ img_data = open(image_file, 'rb').read()
+ tmp_data = open(tmp_file, 'rb').read(len(img_data))
+
+ #verfy the data
+ if img_data != tmp_data: return 'Verification Failed:\n%s'%verbose
+ return 'Verification Passed:\n%s'%verbose
+
+def write_image(image_file, device_file, offset):
+ verbose = command(
+ get_dd_path(),
+ "if=%s"%image_file,
+ "of=%s"%device_file,
+ "seek=%d"%(offset/SECTOR_SIZE),
+ "bs=%d"%SECTOR_SIZE,
+ )
+
+ try: #exec the sync command (only works on linux)
+ if platform.system() == 'Linux': command('sync')
+ except: pass
+
+ return verbose
+
+def write_and_verify(image_file, device_file, offset):
+ if os.path.getsize(image_file) > MAX_FILE_SIZE:
+ raise Exception, 'Image file larger than %d bytes!'%MAX_FILE_SIZE
+ return '%s\n%s'%(
+ write_image(
+ image_file=image_file,
+ device_file=device_file,
+ offset=offset,
+ ), verify_image(
+ image_file=image_file,
+ device_file=device_file,
+ offset=offset,
+ ),
+ )
+
+def burn_sd_card(dev, fw, fpga):
+ verbose = ''
+ if fw: verbose += 'Burn firmware image:\n%s\n'%write_and_verify(
+ image_file=fw, device_file=dev, offset=FIRMWARE_OFFSET
+ )
+ if fpga: verbose += 'Burn fpga image:\n%s\n'%write_and_verify(
+ image_file=fpga, device_file=dev, offset=FPGA_OFFSET
+ )
+ return verbose
+
+########################################################################
+# command line options
+########################################################################
+def get_options():
+ parser = optparse.OptionParser()
+ parser.add_option("--dev", type="string", help="raw device path", default='')
+ parser.add_option("--fw", type="string", help="firmware image path (optional)", default='')
+ parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='')
+ parser.add_option("--list", action="store_true", help="list possible raw devices", default=False)
+ (options, args) = parser.parse_args()
+
+ if options.list:
+ print 'Possible raw devices:'
+ print ' ' + '\n '.join(get_raw_device_hints())
+ exit()
+
+ return options
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = get_options()
+ if not options.dev: raise Exception, 'no raw device path specified'
+ print burn_sd_card(dev=options.dev, fw=options.fw, fpga=options.fpga)
diff --git a/host/utils/usrp2_card_burner_gui.py b/host/utils/usrp2_card_burner_gui.py
new file mode 100755
index 000000000..61fbadbe3
--- /dev/null
+++ b/host/utils/usrp2_card_burner_gui.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 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 usrp2_card_burner #import implementation
+import Tkinter, Tkconstants, tkFileDialog, tkFont, tkMessageBox
+import os
+
+class BinFileEntry(Tkinter.Frame):
+ """
+ Simple file entry widget for getting the file path of bin files.
+ Combines a label, entry, and button with file dialog callback.
+ """
+
+ def __init__(self, root, what, def_path=''):
+ self._what = what
+ Tkinter.Frame.__init__(self, root)
+ Tkinter.Label(self, text=what+":").pack(side=Tkinter.LEFT)
+ self._entry = Tkinter.Entry(self, width=50)
+ self._entry.insert(Tkinter.END, def_path)
+ self._entry.pack(side=Tkinter.LEFT)
+ Tkinter.Button(self, text="...", command=self._button_cb).pack(side=Tkinter.LEFT)
+
+ def _button_cb(self):
+ filename = tkFileDialog.askopenfilename(
+ parent=self,
+ filetypes=[('bin files', '*.bin'), ('all files', '*.*')],
+ title="Select bin file for %s"%self._what,
+ initialdir=os.path.dirname(self.get_filename()),
+ )
+
+ # open file on your own
+ if filename:
+ self._entry.delete(0, Tkinter.END)
+ self._entry.insert(0, filename)
+
+ def get_filename(self):
+ return self._entry.get()
+
+class DeviceEntryWidget(Tkinter.Frame):
+ """
+ Simple entry widget for getting the raw device name.
+ Combines a label, entry, and helpful text box with hints.
+ """
+
+ def __init__(self, root, text=''):
+ Tkinter.Frame.__init__(self, root)
+
+ Tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack()
+
+ self._hints = Tkinter.Listbox(self)
+ self._hints.bind("<<ListboxSelect>>", self._listbox_cb)
+ self._reload_cb()
+ self._hints.pack(expand=Tkinter.YES, fill=Tkinter.X)
+
+ frame = Tkinter.Frame(self)
+ frame.pack()
+
+ Tkinter.Label(frame, text="Raw Device:").pack(side=Tkinter.LEFT)
+ self._entry = Tkinter.Entry(frame, width=50)
+ self._entry.insert(Tkinter.END, text)
+ self._entry.pack(side=Tkinter.LEFT)
+
+ def _reload_cb(self):
+ self._hints.delete(0, Tkinter.END)
+ for hint in usrp2_card_burner.get_raw_device_hints():
+ self._hints.insert(Tkinter.END, hint)
+
+ def _listbox_cb(self, event):
+ try:
+ sel = self._hints.get(self._hints.curselection()[0])
+ self._entry.delete(0, Tkinter.END)
+ self._entry.insert(0, sel)
+ except Exception, e: print e
+
+ def get_devname(self):
+ return self._entry.get()
+
+class SectionLabel(Tkinter.Label):
+ """
+ Make a text label with bold font.
+ """
+
+ def __init__(self, root, text):
+ Tkinter.Label.__init__(self, root, text=text)
+
+ #set the font bold
+ f = tkFont.Font(font=self['font'])
+ f['weight'] = 'bold'
+ self['font'] = f.name
+
+class USRP2CardBurnerApp(Tkinter.Frame):
+ """
+ The top level gui application for the usrp2 sd card burner.
+ Creates entry widgets and button with callback to write images.
+ """
+
+ def __init__(self, root, dev, fw, fpga):
+
+ Tkinter.Frame.__init__(self, root)
+
+ #pack the file entry widgets
+ SectionLabel(self, text="Select Images").pack(pady=5)
+ self._fw_img_entry = BinFileEntry(self, "Firmware Image", def_path=fw)
+ self._fw_img_entry.pack()
+ self._fpga_img_entry = BinFileEntry(self, "FPGA Image", def_path=fpga)
+ self._fpga_img_entry.pack()
+
+ #pack the destination entry widget
+ SectionLabel(self, text="Select Device").pack(pady=5)
+ self._raw_dev_entry = DeviceEntryWidget(self, text=dev)
+ self._raw_dev_entry.pack()
+
+ #the do it button
+ SectionLabel(self, text="").pack(pady=5)
+ Tkinter.Label(self, text="Warning! This tool can overwrite your hard drive. Use with caution.").pack()
+ Tkinter.Button(self, text="Burn SD Card", command=self._burn).pack()
+
+ def _burn(self):
+ #grab strings from the gui
+ fw = self._fw_img_entry.get_filename()
+ fpga = self._fpga_img_entry.get_filename()
+ dev = self._raw_dev_entry.get_devname()
+
+ #check input
+ if not dev:
+ tkMessageBox.showerror('Error:', 'No device specified!')
+ return
+ if not fw and not fpga:
+ tkMessageBox.showerror('Error:', 'No images specified!')
+ return
+ if fw and not os.path.exists(fw):
+ tkMessageBox.showerror('Error:', 'Firmware image not found!')
+ return
+ if fpga and not os.path.exists(fpga):
+ tkMessageBox.showerror('Error:', 'FPGA image not found!')
+ return
+
+ #burn the sd card
+ try:
+ verbose = usrp2_card_burner.burn_sd_card(dev=dev, fw=fw, fpga=fpga)
+ tkMessageBox.showinfo('Verbose:', verbose)
+ except Exception, e:
+ tkMessageBox.showerror('Verbose:', 'Error: %s'%str(e))
+
+########################################################################
+# main
+########################################################################
+if __name__=='__main__':
+ options = usrp2_card_burner.get_options()
+ root = Tkinter.Tk()
+ root.title('USRP2 SD Card Burner')
+ USRP2CardBurnerApp(root, dev=options.dev, fw=options.fw, fpga=options.fpga).pack()
+ root.mainloop()
+ exit()
diff --git a/host/utils/usrp2_recovery.py b/host/utils/usrp2_recovery.py
new file mode 100755
index 000000000..5654e93d3
--- /dev/null
+++ b/host/utils/usrp2_recovery.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 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/>.
+#
+
+"""
+The usrp2 recovery app:
+
+When the usrp2 has an unknown or bad ip address in its eeprom,
+it may not be possible to communicate with the usrp2 over ip/udp.
+
+This app will send a raw ethernet packet to bypass the ip layer.
+The packet will contain a known ip address to burn into eeprom.
+Because the recovery packet is sent with a broadcast mac address,
+only one usrp2 should be present on the interface upon execution.
+
+This app requires super-user privileges and only works on linux.
+"""
+
+import socket
+import struct
+import optparse
+
+BCAST_MAC_ADDR = 'ff:ff:ff:ff:ff:ff'
+RECOVERY_ETHERTYPE = 0xbeee
+IP_RECOVERY_CODE = 'addr'
+
+def mac_addr_repr_to_binary_string(mac_addr):
+ return ''.join(map(lambda x: chr(int(x, 16)), mac_addr.split(':')))
+
+if __name__ == '__main__':
+ parser = optparse.OptionParser(usage='usage: %prog [options]\n'+__doc__)
+ parser.add_option('--ifc', type='string', help='ethernet interface name [default=%default]', default='eth0')
+ parser.add_option('--new-ip', type='string', help='ip address to set [default=%default]', default='192.168.10.2')
+ (options, args) = parser.parse_args()
+
+ #create the raw socket
+ print "Opening raw socket on interface:", options.ifc
+ soc = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
+ soc.bind((options.ifc, RECOVERY_ETHERTYPE))
+
+ #create the recovery packet
+ print "Loading packet with ip address:", options.new_ip
+ packet = struct.pack(
+ '!6s6sH4s4s',
+ mac_addr_repr_to_binary_string(BCAST_MAC_ADDR),
+ mac_addr_repr_to_binary_string(BCAST_MAC_ADDR),
+ RECOVERY_ETHERTYPE,
+ IP_RECOVERY_CODE,
+ socket.inet_aton(options.new_ip),
+ )
+
+ print "Sending packet (%d bytes)"%len(packet)
+ soc.send(packet)
+ print "Done"