altosuilib: Don't crash when flashing an unknown device
[fw/altos] / altosuilib / AltosFlashUI.java
index 0ed4e75df4616c90fff87de9cbe12ecb6b2adf61..37ab96614af52f73d4ab116aa9c39f9d457f480e 100644 (file)
@@ -3,7 +3,8 @@
  *
  * 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; version 2 of the License.
+ * the Free Software Foundation; either version 2 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
@@ -15,7 +16,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_3;
+package org.altusmetrum.altosuilib_13;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -23,7 +24,7 @@ import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_5.*;
+import org.altusmetrum.altoslib_13.*;
 
 public class AltosFlashUI
        extends AltosUIDialog
@@ -44,7 +45,7 @@ public class AltosFlashUI
        File            file;
 
        // Debug connection
-       AltosDevice     device;
+       AltosUSBDevice  device;
 
        AltosLink       link;
 
@@ -54,33 +55,48 @@ public class AltosFlashUI
        // Flash controller
        AltosProgrammer programmer;
 
-       private static String[] pair_programmed = {
+       private static final String[] pair_programmed_files = {
                "teleballoon",
-               "telebt",
-               "teledongle",
-               "telefire",
+               "telebt-v1",
+               "teledongle-v0",
+               "telefire-v0",
                "telemetrum-v0",
                "telemetrum-v1",
-               "telemini",
+               "telemini-v1",
                "telenano",
                "teleshield",
                "teleterra"
        };
 
+       private static final String[] pair_programmed_devices = {
+               "TeleBalloon",
+               "TeleBT-v1",
+               "TeleDongle-v0",
+               "TeleFire-v0",
+               "TeleFire",
+               "TeleMetrum-v0",
+               "TeleMetrum-v1",
+               "TeleMini-v1",
+               "TeleNano",
+               "TeleShield",
+               "TeleTerra"
+       };
+
        private boolean is_pair_programmed() {
 
                if (file != null) {
                        String  name = file.getName();
-                       for (int i = 0; i < pair_programmed.length; i++) {
-                               if (name.startsWith(pair_programmed[i]))
+                       for (int i = 0; i < pair_programmed_files.length; i++) {
+                               if (name.startsWith(pair_programmed_files[i]))
                                        return true;
                        }
                }
                if (device != null) {
-                       if (!device.matchProduct(AltosLib.product_altusmetrum) &&
-                           (device.matchProduct(AltosLib.product_teledongle) ||
-                            device.matchProduct(AltosLib.product_telebt)))
-                               return true;
+                       String  name = device.toString();
+                       for (int i = 0; i < pair_programmed_devices.length; i++) {
+                               if (name.startsWith(pair_programmed_devices[i]))
+                                       return true;
+                       }
                }
                return false;
        }
@@ -100,10 +116,10 @@ public class AltosFlashUI
                                                              JOptionPane.ERROR_MESSAGE);
                                setVisible(false);
                                dispose();
-                       } else if (cmd.equals("done")) {
+                       } else if (cmd.equals(AltosFlashListener.flash_done)) {
                                setVisible(false);
                                dispose();
-                       } else if (cmd.equals("start")) {
+                       } else if (cmd.equals(AltosFlashListener.flash_start)) {
                                setVisible(true);
                        } else {
                                pbar.setValue(e.getID());
@@ -189,14 +205,20 @@ public class AltosFlashUI
        }
 
        static class AltosHexfileFilter extends javax.swing.filechooser.FileFilter {
-               int product;
                String head;
                String description;
 
-               public AltosHexfileFilter(int product, String head, String description) {
-                       this.product = product;
-                       this.head = head;
-                       this.description = description;
+               public AltosHexfileFilter(String usb_product) {
+                       int l;
+
+                       /* Trim off any trailing variants (1.0a vs 1.0) */
+                       for (l = usb_product.length(); l > 0; l--) {
+                               char c = usb_product.charAt(l-1);
+                               if (c < 'a' || 'z' < c)
+                                       break;
+                       }
+                       head = usb_product.substring(0, l).toLowerCase();
+                       description = String.format("%s Image File", usb_product);
                }
 
                public boolean accept(File file) {
@@ -208,13 +230,6 @@ public class AltosFlashUI
                }
        }
 
-       static AltosHexfileFilter[] filters = {
-               new AltosHexfileFilter(AltosLib.product_telemetrum, "telemetrum", "TeleMetrum Image"),
-               new AltosHexfileFilter(AltosLib.product_teledongle, "teledongle", "TeleDongle Image"),
-               new AltosHexfileFilter(AltosLib.product_telemega, "telemega", "TeleMega Image"),
-               new AltosHexfileFilter(AltosLib.product_easymini, "easymini", "EasyMini Image"),
-       };
-
        boolean select_source_file() {
                JFileChooser    hexfile_chooser = new JFileChooser();
 
@@ -224,18 +239,14 @@ public class AltosFlashUI
 
                hexfile_chooser.setDialogTitle("Select Flash Image");
 
-               for (int i = 0; i < filters.length; i++) {
-                       hexfile_chooser.addChoosableFileFilter(filters[i]);
-               }
                javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
                hexfile_chooser.addChoosableFileFilter(ihx_filter);
                hexfile_chooser.setFileFilter(ihx_filter);
 
                if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
-                       for (int i = 0; i < filters.length; i++) {
-                               if (device != null && device.matchProduct(filters[i].product))
-                                       hexfile_chooser.setFileFilter(filters[i]);
-                       }
+                       AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
+                       hexfile_chooser.addChoosableFileFilter(filter);
+                       hexfile_chooser.setFileFilter(filter);
                }
 
                int returnVal = hexfile_chooser.showOpenDialog(frame);
@@ -253,15 +264,72 @@ public class AltosFlashUI
        boolean select_device() {
                int     product = AltosLib.product_any;
 
-               device = AltosDeviceUIDialog.show(frame, AltosLib.product_any);
+               device = AltosDeviceUIDialog.show_usb(frame, AltosLib.product_any);
 
                if (device == null)
                        return false;
                return true;
        }
 
-       boolean update_rom_config_info(AltosRomconfig existing_config) {
+       boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
+               if (a == null || b == null)
+                       return (a == null && b == null);
+
+               if (!a.valid || !b.valid)
+                       return false;
+
+               if (a.usb_id != null && b.usb_id != null &&
+                   (a.usb_id.vid != b.usb_id.vid ||
+                    a.usb_id.pid != b.usb_id.pid))
+                       return false;
+
+               if (a.usb_product != null && b.usb_product != null &&
+                   !a.usb_product.equals(b.usb_product))
+                       return false;
+
+               return true;
+       }
+
+       boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
                AltosRomconfig  new_config;
+
+               if (!rom_config_matches(existing_config, image_config)) {
+                       int ret;
+                       if (existing_config == null || !existing_config.valid) {
+                               ret = JOptionPane.showConfirmDialog(this,
+                                                                   String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
+                                                                                 image_config.usb_id.vid,
+                                                                                 image_config.usb_id.pid,
+                                                                                 image_config.usb_product),
+                                                                   "Unknown Target Device",
+                                                                   JOptionPane.YES_NO_OPTION);
+                       } else {
+                               ret = JOptionPane.showConfirmDialog(this,
+                                                                   String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
+                                                                                 existing_config.usb_id.vid,
+                                                                                 existing_config.usb_id.pid,
+                                                                                 existing_config.usb_product,
+                                                                                 image_config.usb_id.vid,
+                                                                                 image_config.usb_id.pid,
+                                                                                 image_config.usb_product),
+                                                                   "Image doesn't match Device",
+                                                                   JOptionPane.YES_NO_OPTION);
+                       }
+                       if (ret != JOptionPane.YES_OPTION)
+                               return false;
+               }
+
+               if (existing_config != null && existing_config.radio_calibration_broken) {
+                       int ret = JOptionPane.showConfirmDialog(this,
+                                                               String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
+                                                                             existing_config.radio_calibration),
+                                                               "Radio Calibration Invalid",
+                                                               JOptionPane.YES_NO_OPTION);
+                       if (ret != JOptionPane.YES_OPTION)
+                               return false;
+               }
+
+
                new_config = AltosRomconfigUI.show(frame, existing_config);
                if (new_config == null)
                        return false;
@@ -283,7 +351,7 @@ public class AltosFlashUI
                                                                    device.toShortString()),
                                                      "Device in use",
                                                      JOptionPane.ERROR_MESSAGE);
-               } else if (e instanceof IOException) {
+               } else {
                        JOptionPane.showMessageDialog(frame,
                                                      e.getMessage(),
                                                      file.toString(),
@@ -319,13 +387,17 @@ public class AltosFlashUI
                                else
                                        programmer = new AltosSelfFlash(ui.file, link, this);
 
-                               final AltosRomconfig    current_config = programmer.romconfig();
+                               final AltosRomconfig    current_config = programmer.target_romconfig(device.usb_id(), device.usb_product());
+
+                               final AltosRomconfig    image_config = programmer.image_romconfig();
+
+                               System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
 
                                final Semaphore await_rom_config = new Semaphore(0);
                                SwingUtilities.invokeLater(new Runnable() {
                                                public void run() {
                                                        ui.programmer = programmer;
-                                                       ui.update_rom_config_info(current_config);
+                                                       ui.update_rom_config_info(current_config, image_config);
                                                        await_rom_config.release();
                                                }
                                        });
@@ -364,45 +436,235 @@ public class AltosFlashUI
 
        flash_task      flasher;
 
-       private boolean open_device() throws InterruptedException {
-               try {
-                       link = new AltosSerial(device);
-                       if (is_pair_programmed())
-                               return true;
 
-                       if (link == null)
-                               throw new IOException(String.format("%s: open failed", device.toShortString()));
+       class open_task implements Runnable {
+               AltosDevice     device;
+               Thread          t;
+               open_dialog     dialog;
+
+               public void do_exception(final Exception e) {
+                       SwingUtilities.invokeLater(
+                               new Runnable() {
+                                       public void run() {
+                                               try { dialog.open_exception(e); } catch (Exception ex) { }
+                                       }
+                               });
+               }
+
+               public void do_success(final AltosLink link) {
+                       SwingUtilities.invokeLater(
+                               new Runnable() {
+                                       public void run() {
+                                               try { dialog.open_success(link); } catch (Exception ex) { }
+                                       }
+                               });
+               }
+
+               public void do_failure() {
+                       SwingUtilities.invokeLater(
+                               new Runnable() {
+                                       public void run() {
+                                               try { dialog.open_failure(); } catch (Exception ex) { }
+                                       }
+                               });
+               }
 
-                       while (!link.is_loader()) {
-                               link.to_loader();
+               public void do_cancel() {
+                       SwingUtilities.invokeLater(
+                               new Runnable() {
+                                       public void run() {
+                                               try { dialog.open_cancel(); } catch (Exception ex) { }
+                                       }
+                               });
+               }
+
+               public void run () {
+                       try {
+                               AltosLink link = null;
 
-                               java.util.List<AltosDevice> devices = null;
+                               for (;;) {
+                                       System.out.printf("Attempting to open %s\n", device.toShortString());
 
-                               for (int tries = 0; tries < 10; tries++) {
-                                       Thread.sleep(100);
-                                       devices = AltosUSBDevice.list(AltosLib.product_altusmetrum);
-                                       if (devices.size() != 0)
-                                               break;
-                               }
+                                       link = new AltosSerial(device);
 
-                               if (devices.size() == 1)
-                                       device = devices.get(0);
-                               else {
-                                       device = AltosDeviceUIDialog.show(frame, AltosLib.product_altusmetrum);
-                                       if (device == null)
-                                               return false;
+                                       if (link == null)
+                                               throw new IOException(String.format("%s: open failed",
+                                                                                   device.toShortString()));
+
+                                       /* See if the link is usable already */
+                                       if (is_pair_programmed() || link.is_loader()) {
+                                               System.out.printf("Device ready for use\n");
+                                               do_success(link);
+                                               return;
+                                       }
+
+                                       java.util.List<AltosDevice> prev_devices =
+                                               AltosUSBDevice.list(AltosLib.product_altusmetrum);
+
+                                       /* Nope, switch to loader and
+                                        * wait for it to re-appear
+                                        */
+
+                                       System.out.printf("Switch to loader\n");
+
+                                       link.to_loader();
+
+                                       /* This is a bit fragile, but
+                                        * I'm not sure what else to
+                                        * do other than ask the user.
+                                        *
+                                        * Search for a device which
+                                        * wasn't there before we
+                                        * asked the target to switch
+                                        * to loader mode
+                                        */
+
+                                       device = null;
+                                       for (;;) {
+                                               Thread.sleep(100);
+                                               java.util.List<AltosDevice> devices =
+                                                       AltosUSBDevice.list(AltosLib.product_altusmetrum);
+
+                                               for (AltosDevice d : devices) {
+                                                       boolean matched = false;
+                                                       System.out.printf("\tfound device %s\n", d.toShortString());
+                                                       for (AltosDevice p : prev_devices)
+                                                               if (d.equals(p)) {
+                                                                       matched = true;
+                                                                       break;
+                                                               }
+                                                       if (!matched) {
+                                                               System.out.printf("Identified new device %s\n", d.toShortString());
+                                                               device = (AltosUSBDevice) d;
+                                                               break;
+                                                       }
+                                               }
+                                               if (device != null)
+                                                       break;
+                                       }
                                }
-                               link = new AltosSerial(device);
+                       } catch (AltosSerialInUseException ee) {
+                               do_exception(ee);
+                       } catch (FileNotFoundException fe) {
+                               do_exception(fe);
+                       } catch (IOException ie) {
+                               do_exception (ie);
+                       } catch (InterruptedException ie) {
                        }
-                       return true;
-               } catch (AltosSerialInUseException ee) {
-                       exception(ee);
-               } catch (FileNotFoundException fe) {
-                       exception(fe);
-               } catch (IOException ie) {
-                       exception (ie);
                }
-               return false;
+
+               public void cancel() {
+                       t.interrupt();
+               }
+
+               public open_task(AltosDevice device, open_dialog dialog) {
+                       this.device = device;
+                       this.dialog = dialog;
+                       t = new Thread(this);
+                       t.start();
+               }
+       }
+
+       class open_dialog
+               extends AltosUIDialog
+               implements ActionListener
+       {
+               AltosUIFrame owner;
+
+               private JLabel  opening_label;
+               private JButton cancel_button;
+
+               boolean done = false;
+
+               AltosLink link = null;
+
+               open_task open = null;
+
+               public void open_exception(Exception e) {
+                       System.out.printf("open_exception\n");
+                       setVisible(false);
+                       exception(e);
+                       done = true;
+               }
+
+               public void open_success(AltosLink link) {
+                       System.out.printf("open_success\n");
+                       setVisible(false);
+                       this.link = link;
+                       done = true;
+               }
+
+               public void open_failure() {
+                       System.out.printf("open_failure\n");
+                       setVisible(false);
+                       done = true;
+               }
+
+               public void open_cancel() {
+                       System.out.printf("open_cancel\n");
+                       setVisible(false);
+                       done = true;
+               }
+
+               public AltosLink do_open(open_task open) throws InterruptedException {
+                       this.open = open;
+                       setVisible(true);
+                       return link;
+               }
+
+               public void actionPerformed(ActionEvent e) {
+                       String cmd = e.getActionCommand();
+
+                       if (cmd.equals("cancel"))
+                               if (open != null)
+                                       open.cancel();
+                       done = true;
+                       setVisible(false);
+               }
+
+               public open_dialog(AltosUIFrame in_owner) {
+                       super(in_owner, "Open Flash Target Device", true);
+                       owner = in_owner;
+
+                       Container               pane = getContentPane();
+                       GridBagConstraints      c = new GridBagConstraints();
+                       Insets                  i = new Insets(4,4,4,4);
+
+
+                       pane.setLayout(new GridBagLayout());
+
+                       opening_label = new JLabel("Opening Device");
+                       c.fill = GridBagConstraints.HORIZONTAL;
+                       c.anchor = GridBagConstraints.LINE_START;
+                       c.insets = i;
+                       c.weightx = 0;
+                       c.weighty = 0;
+
+                       c.gridx = 0;
+                       c.gridy = 0;
+
+                       pane.add(opening_label, c);
+
+                       cancel_button = new JButton("Cancel");
+                       cancel_button.addActionListener(this);
+                       cancel_button.setActionCommand("cancel");
+
+                       c.gridy = 1;
+                       pane.add(cancel_button, c);
+                       pack();
+                       setLocationRelativeTo(owner);
+               }
+       }
+
+       private boolean open_device() throws InterruptedException {
+
+               open_dialog     dialog = new open_dialog(frame);
+
+               open_task       open = new open_task(device, dialog);
+
+               link = dialog.do_open(open);
+
+               return link != null;
        }
 
        /*