diff options
Diffstat (limited to 'host/utils')
-rw-r--r-- | host/utils/CMakeLists.txt | 39 | ||||
-rw-r--r-- | host/utils/uhd_burn_db_eeprom.cpp | 103 | ||||
-rw-r--r-- | host/utils/uhd_find_devices.cpp | 60 | ||||
-rw-r--r-- | host/utils/uhd_usrp_probe.cpp | 166 | ||||
-rw-r--r-- | host/utils/usrp2_addr_burner.cpp | 90 | ||||
-rwxr-xr-x | host/utils/usrp2_card_burner.py | 226 | ||||
-rwxr-xr-x | host/utils/usrp2_card_burner_gui.py | 169 | ||||
-rwxr-xr-x | host/utils/usrp2_recovery.py | 68 |
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" |