diff options
| author | Josh Blum <josh@joshknows.com> | 2011-03-27 04:18:00 -0700 | 
|---|---|---|
| committer | Josh Blum <josh@joshknows.com> | 2011-03-27 04:18:00 -0700 | 
| commit | f5390d04229572b3d342f910e731e2b9dccf8505 (patch) | |
| tree | c1c9b17132cfd469a4cc4cd9eea10e75ca1d3cd9 | |
| parent | eb0777811de2f1f1a486391e05aa467f3c64875b (diff) | |
| parent | ae8fd1009794cd80ee79fcf56a6bddb609bb32b5 (diff) | |
| download | uhd-f5390d04229572b3d342f910e731e2b9dccf8505.tar.gz uhd-f5390d04229572b3d342f910e731e2b9dccf8505.tar.bz2 uhd-f5390d04229572b3d342f910e731e2b9dccf8505.zip | |
Merge branch 'usrp_n2xx_net_burner_gui'
| -rw-r--r-- | host/docs/usrp2.rst | 11 | ||||
| -rw-r--r-- | host/utils/CMakeLists.txt | 1 | ||||
| -rwxr-xr-x | host/utils/usrp_n2xx_net_burner.py | 54 | ||||
| -rwxr-xr-x | host/utils/usrp_n2xx_net_burner_gui.py | 188 | 
4 files changed, 227 insertions, 27 deletions
| diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 70101bd87..912f7d2bd 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -58,17 +58,20 @@ Use the net burner tool (unix)  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  :: +    sudo <prefix>/share/uhd/utils/usrp_n2xx_net_burner_gui.py + +    -- OR -- +      cd <prefix>/share/uhd/utils -    ./usrp_n2xx_net_burner.py --ip=<ip address> --fw=<path for firmware image> -    ./usrp_n2xx_net_burner.py --ip=<ip address> --fpga=<path to FPGA image> +    ./usrp_n2xx_net_burner.py --addr=<ip address> --fw=<path for firmware image> +    ./usrp_n2xx_net_burner.py --addr=<ip address> --fpga=<path to FPGA image>  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Use the net burner tool (Windows)  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  :: -    <path_to_python.exe> <prefix>/share/uhd/utils/usrp_n2xx_net_burner.py --ip=<ip address> --fw=<path for firmware image> -    <path_to_python.exe> <prefix>/share/uhd/utils/usrp_n2xx_net_burner.py --ip=<ip address> --fpga=<path to FPGA image> +    <path_to_python.exe> <prefix>/share/uhd/utils/usrp_n2xx_net_burner_gui.py  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  Device recovery and bricking diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 3c18324a5..98b5d41fb 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -59,6 +59,7 @@ IF(ENABLE_USRP2)          usrp2_card_burner.py          usrp2_card_burner_gui.py          usrp_n2xx_net_burner.py +        usrp_n2xx_net_burner_gui.py          DESTINATION ${PKG_DATA_DIR}/utils          COMPONENT utilities      ) diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index 6fdc9df20..c715f3364 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -118,31 +118,29 @@ def is_valid_fw_image(fw_image):  # Burner class, holds a socket and send/recv routines  ########################################################################  class burner_socket(object): -    def __init__(self, ip): +    def __init__(self, addr):          self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)          self._sock.settimeout(UDP_TIMEOUT) -        self._sock.connect((ip, UDP_FW_UPDATE_PORT)) +        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 -    def send_and_recv(self, pkt): -        try: self._sock.send(pkt) -        except Exception, e: -            print e -            sys.exit(1) - -        try: recv_pkt = self._sock.recv(UDP_MAX_XFER_BYTES) -        except Exception, e: -            print e -            sys.exit(1) +    def set_callbacks(self, progress_cb, status_cb): +        self._progress_cb = progress_cb +        self._status_cb = status_cb -        return recv_pkt +    def send_and_recv(self, pkt): +        self._sock.send(pkt) +        return self._sock.recv(UDP_MAX_XFER_BYTES)      #just here to validate comms      def init_update(self):          out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") -        in_pkt = self.send_and_recv(out_pkt) +        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 "USRP2P found." +            print "USRP-N2XX found."          else:              raise Exception, "Invalid reply received from device." @@ -214,9 +212,11 @@ class burner_socket(object):      def write_image(self, image, addr):          print "Writing image" +        self._status_cb("Writing") +        writedata = image          #we split the image into smaller (256B) bits and send them down the wire -        while image: -            out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) +        while writedata: +            out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, writedata[:FLASH_DATA_PACKET_SIZE])              in_pkt = self.send_and_recv(out_pkt)              (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) @@ -224,11 +224,13 @@ class burner_socket(object):              if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG:                raise Exception, "Invalid reply %c from device." % (chr(pktid)) -            image = image[FLASH_DATA_PACKET_SIZE:] +            writedata = writedata[FLASH_DATA_PACKET_SIZE:]              addr += FLASH_DATA_PACKET_SIZE +            self._progress_cb(float(len(image)-len(writedata))/len(image))      def verify_image(self, image, addr):          print "Verifying data" +        self._status_cb("Verifying")          readsize = len(image)          readdata = str()          while readsize > 0: @@ -245,6 +247,7 @@ class burner_socket(object):              readdata += data[:thisreadsize]              readsize -= FLASH_DATA_PACKET_SIZE              addr += FLASH_DATA_PACKET_SIZE +            self._progress_cb(float(len(readdata))/len(image))          print "Read back %i bytes" % len(readdata)          #  print readdata @@ -253,7 +256,7 @@ class burner_socket(object):          #    print "out: %i in: %i" % (ord(image[i]), ord(readdata[i]))          if readdata != image: -            print "Verify failed. Image did not write correctly." +            raise Exception, "Verify failed. Image did not write correctly."          else:              print "Success." @@ -285,13 +288,15 @@ class burner_socket(object):      def reset_usrp(self):          out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL, seq(), 0, 0, "") -        in_pkt = self.send_and_recv(out_pkt) +        try: in_pkt = self.send_and_recv(out_pkt) +        except socket.timeout: return          (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt)          if pktid == update_id_t.USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG:              raise Exception, "Device failed to reset."      def erase_image(self, addr, length): +        self._status_cb("Erasing")          #get flash info first          out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "")          in_pkt = self.send_and_recv(out_pkt) @@ -302,6 +307,7 @@ class burner_socket(object):              raise Exception, "Invalid reply %c from device." % (chr(pktid))          print "Erasing %i bytes at %i" % (length, addr) +        start_time = time.time()          #now wait for it to finish          while(True): @@ -313,6 +319,8 @@ class burner_socket(object):              if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break              elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG:                  raise Exception, "Invalid reply %c from device." % (chr(pktid)) +            time.sleep(0.01) #decrease network overhead by waiting a bit before polling +            self._progress_cb(min(1.0, (time.time() - start_time)/(length/80e3)))  ######################################################################## @@ -320,7 +328,7 @@ class burner_socket(object):  ########################################################################  def get_options():      parser = optparse.OptionParser() -    parser.add_option("--ip",   type="string",                 help="USRP2P firmware address",        default='') +    parser.add_option("--addr", type="string",                 help="USRP-N2XX device address",       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("--reset", action="store_true",          help="reset the device after writing", default=False) @@ -335,7 +343,7 @@ def get_options():  ########################################################################  if __name__=='__main__':      options = get_options() -    if not options.ip: raise Exception, 'no ip address specified' +    if not options.addr: raise Exception, 'no address specified'      if not options.fpga and not options.fw and not options.reset: raise Exception, 'Must specify either a firmware image or FPGA image, and/or reset.' @@ -345,7 +353,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(ip=options.ip) +    burner = burner_socket(addr=options.addr)      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 new file mode 100755 index 000000000..7fcb7d121 --- /dev/null +++ b/host/utils/usrp_n2xx_net_burner_gui.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# +# Copyright 2011 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 threading +import usrp_n2xx_net_burner #import implementation +import Tkinter, 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 ProgressBar(Tkinter.Canvas): +    """ +    A simple implementation of a progress bar. +    Draws rectangle that fills from left to right. +    """ + +    def __init__(self, root, width=500, height=20): +        self._width = width +        self._height = height +        Tkinter.Canvas.__init__(self, root, relief="sunken", borderwidth=2, width=self._width-2, height=self._height-2) +        self._last_fill_pixels = None +        self.set(0.0) + +    def set(self, frac): +        """ +        Update the progress where fraction is between 0.0 and 1.0 +        """ +        #determine the number of pixels to draw +        fill_pixels = int(round(self._width*frac)) +        if fill_pixels == self._last_fill_pixels: return +        self._last_fill_pixels = fill_pixels + +        #draw a rectangle representing the progress +        if frac: self.create_rectangle(0, 0, fill_pixels, self._height, fill="#357EC7") +        else:    self.create_rectangle(0, 0, self._width, self._height, fill="#E8E8E8") + +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 USRPN2XXNetBurnerApp(Tkinter.Frame): +    """ +    The top level gui application for the usrp-n2xx network burner. +    Creates entry widgets and button with callback to write images. +    """ + +    def __init__(self, root, addr, 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 Address").pack(pady=5) +        self._addr_entry = Tkinter.Entry(self, width=30) +        self._addr_entry.insert(Tkinter.END, addr) +        self._addr_entry.pack() + +        #the do it button +        SectionLabel(self, text="").pack(pady=5) +        button = Tkinter.Button(self, text="Burn Images", command=self._burn) +        self._enable_input = lambda: button.configure(state=Tkinter.NORMAL) +        self._disable_input = lambda: button.configure(state=Tkinter.DISABLED) +        button.pack() + +        #a progress bar to monitor the status +        progress_frame = Tkinter.Frame(self) +        progress_frame.pack() +        self._status = Tkinter.StringVar() +        Tkinter.Label(progress_frame, textvariable=self._status).pack(side=Tkinter.LEFT) +        self._pbar = ProgressBar(progress_frame) +        self._pbar.pack(side=Tkinter.RIGHT, expand=True) + +    def _burn(self): +        self._disable_input() +        threading.Thread(target=self._burn_bg).start() + +    def _burn_bg(self): +        #grab strings from the gui +        fw = self._fw_img_entry.get_filename() +        fpga = self._fpga_img_entry.get_filename() +        addr = self._addr_entry.get() + +        #check input +        if not addr: +            tkMessageBox.showerror('Error:', 'No address 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 + +        try: +            #make a new burner object and attempt the burner operation +            burner = usrp_n2xx_net_burner.burner_socket(addr=addr) + +            for (image_type, fw_img, fpga_img) in (('FPGA', '', fpga), ('Firmware', fw, '')): +                #setup callbacks that update the gui +                def status_cb(status): +                    self._pbar.set(0.0) #status change, reset the progress +                    self._status.set("%s %s "%(status.title(), image_type)) +                burner.set_callbacks(progress_cb=self._pbar.set, status_cb=status_cb) +                burner.burn_fw(fw=fw_img, fpga=fpga_img, reset=False, safe=False) + +            if tkMessageBox.askyesno("Burn was successful!", "Reset the device?"): +                burner.reset_usrp() + +        except Exception, e: +            tkMessageBox.showerror('Verbose:', 'Error: %s'%str(e)) + +        #reset the progress bar +        self._pbar.set(0.0) +        self._status.set("") +        self._enable_input() + +######################################################################## +# main +######################################################################## +if __name__=='__main__': +    options = usrp_n2xx_net_burner.get_options() +    root = Tkinter.Tk() +    root.title('USRP-N2XX Net Burner') +    USRPN2XXNetBurnerApp(root, addr=options.addr, fw=options.fw, fpga=options.fpga).pack() +    root.mainloop() +    exit() | 
