diff options
-rw-r--r-- | host/docs/usrp2.rst | 2 | ||||
-rw-r--r-- | host/utils/CMakeLists.txt | 12 | ||||
-rw-r--r-- | host/utils/usrp2_addr_burner.cpp (renamed from host/utils/usrp2_burner.cpp) | 2 | ||||
-rwxr-xr-x | host/utils/usrp2_card_burner.py | 285 |
4 files changed, 295 insertions, 6 deletions
diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index dfde06b27..c453354ca 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -103,7 +103,7 @@ Run the following commands: :: cd <prefix>/share/uhd/utils - ./usrp_burner --addr=192.168.10.2 --new-ip=192.168.10.3 + ./usrp_addr_burner --addr=192.168.10.2 --new-ip=192.168.10.3 **Method 2 (Linux Only):** This method assumes that you do not know the IP address of your USRP2. diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 3c2236379..d28576e8c 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -19,12 +19,16 @@ 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(usrp2_burner usrp2_burner.cpp) -TARGET_LINK_LIBRARIES(usrp2_burner uhd) -INSTALL(TARGETS usrp2_burner RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) +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 DESTINATION ${PKG_DATA_DIR}/utils) +INSTALL(PROGRAMS + usrp2_recovery.py + usrp2_card_burner.py + DESTINATION ${PKG_DATA_DIR}/utils +) diff --git a/host/utils/usrp2_burner.cpp b/host/utils/usrp2_addr_burner.cpp index 9c1bf72fe..08fc1e218 100644 --- a/host/utils/usrp2_burner.cpp +++ b/host/utils/usrp2_addr_burner.cpp @@ -39,7 +39,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ - std::cout << boost::format("USRP2 Burner %s") % desc << std::endl; + std::cout << boost::format("USRP2 Address Burner %s") % desc << 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..99cdcb626 --- /dev/null +++ b/host/utils/usrp2_card_burner.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python + +######################################################################## +# Deal with raw devices +######################################################################## +import glob +import platform +import tempfile +import subprocess +import urllib + +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 + +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' + +def get_raw_device_hints(): + if platform.system() == 'Windows': + output = subprocess.Popen( + [get_dd_path(), '--list'],#, '--filter=removable'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ).stdout.read() + volumes = list() + for info in output.replace('\r', '').split('\n\\\\'): + if 'removeable' not in info.lower(): continue + if 'link to' not in info: continue + if 'size is' in info: + size = info.split('size is')[-1].split()[0] + if int(size) > 2048e6: continue + if 'Mounted on' in info: + volumes.append(info.split('Mounted on')[-1].split()[0]) + continue + volumes.append(info.split('link to')[-1].split()[0]) + return volumes + if platform.system() == 'Linux': + return sorted(set(filter(lambda sd: not sd[-1].isdigit(), glob.glob("/dev/sd*")))) + return () + +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 + args = [ + get_dd_path(), + "of=%s"%tmp_file, + "if=%s"%device_file, + "skip=%d"%offset, + "bs=%d"%SECTOR_SIZE, + "count=%d"%(MAX_FILE_SIZE/SECTOR_SIZE), + ] + p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + ret = p.wait() + verbose = p.stdout.read() + if ret != 0: raise Exception, verbose + + #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): + args = [ + get_dd_path(), + "if=%s"%image_file, + "of=%s"%device_file, + "seek=%d"%offset, + "bs=%d"%SECTOR_SIZE, + ] + p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + ret = p.wait() + verbose = p.stdout.read() + + #get the verbose back to the caller + if ret != 0: raise Exception, verbose + 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 verbose:\n%s\n'%write_and_verify( + image_file=fw, device_file=dev, offset=FIRMWARE_OFFSET + ) + if fpga: verbose += 'Burn fpga image verbose:\n%s\n'%write_and_verify( + image_file=fpga, device_file=dev, offset=FPGA_OFFSET + ) + return verbose + +######################################################################## +# Graphical Tk stuff +######################################################################## +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) + + self._hints = Tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + for hint in get_raw_device_hints(): + self._hints.insert(Tkinter.END, hint) + 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 _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.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 = burn_sd_card(dev=dev, fw=fw, fpga=fpga) + tkMessageBox.showinfo('Verbose:', verbose) + except Exception, e: + tkMessageBox.showerror('Verbose:', 'Error: %s'%str(e)) + +######################################################################## +# Main +######################################################################## +import optparse + +if __name__=='__main__': + parser = optparse.OptionParser() + parser.add_option("--gui", action="store_true", default=False, help="run in gui mode") + 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='') + (options, args) = parser.parse_args() + + if options.gui: + root = Tkinter.Tk() + root.title('USRP2 SD Card Burner') + USRP2CardBurnerApp(root, dev=options.dev, fw=options.fw, fpga=options.fpga).pack() + root.mainloop() + exit() + + if not options.dev: raise Exception, 'no raw device path specified' + print burn_sd_card(dev=options.dev, fw=options.fw, fpga=options.fpga) |