001package jmri.jmrix.sprog.update; 002 003import static jmri.jmrix.sprog.SprogConstants.TC_BOOT_REPLY_TIMEOUT; 004 005import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 006import javax.swing.JOptionPane; 007import jmri.jmrix.sprog.SprogConstants.SprogState; 008import jmri.jmrix.sprog.SprogMessage; 009import jmri.jmrix.sprog.SprogSystemConnectionMemo; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Frame for SPROG II firmware update utility. 015 * 016 * Extended to cover SPROG 3 which uses the same bootloader protocol Refactored 017 * 018 * @author Andrew Crosland Copyright (C) 2004 019 */ 020public class SprogIIUpdateFrame 021 extends SprogUpdateFrame 022 implements SprogVersionListener { 023 024 public SprogIIUpdateFrame(SprogSystemConnectionMemo memo) { 025 super(memo); 026 } 027 028 /** 029 * {@inheritDoc} 030 */ 031 @Override 032 public void initComponents() { 033 super.initComponents(); 034 035 // add help menu to window 036 addHelpMenu("package.jmri.jmrix.sprog.update.SprogIIUpdateFrame", true); 037 038 // Set a shorter timeout in the TC. Must be shorter than SprogUpdateFrame long timeout 039 tc.setTimeout(TC_BOOT_REPLY_TIMEOUT); 040 041 // Get the SPROG version 042 _memo.getSprogVersionQuery().requestVersion(this); 043 } 044 045 int bootVer = 0; 046 047 /** 048 * {@inheritDoc} 049 * @param v SPROG version to be decoded 050 */ 051 @SuppressFBWarnings(value = "SWL_SLEEP_WITH_LOCK_HELD") 052 @Override 053 synchronized public void notifyVersion(SprogVersion v) { 054 sv = v; 055 if (sv!=null && sv.sprogType.isSprog() == false) { 056 // Didn't recognize a SPROG so check if it is in boot mode already 057 log.debug("SPROG not found - looking for bootloader"); 058 statusBar.setText(Bundle.getMessage("StatusSprogNotFound")); 059 blockLen = -1; 060 requestBoot(); 061 } else { 062 // Check that it's not a V4 063 if (sv!=null && sv.sprogType.sprogType > SprogType.SPROGV4) { 064 statusBar.setText(Bundle.getMessage("StatusFoundX", sv.toString())); 065 blockLen = sv.sprogType.getBlockLen(); 066 // Put SPROG in boot mode 067 log.debug("Putting SPROG in boot mode"); 068 msg = new SprogMessage("b 1 1 1"); 069 bootState = BootState.SETBOOTSENT; 070 tc.sendSprogMessage(msg, this); 071 // SPROG II and 3 will not reply to this if successfull. Will 072 // reply with error if firmware is locked. Wait a while to allow 073 // Traffic Controller to time out 074 startLongTimer(); 075 } else { 076 log.error("Incorrect SPROG Type detected"); 077 statusBar.setText(Bundle.getMessage("StatusIncorrectSprogType")); 078 bootState = BootState.IDLE; 079 } 080 } 081 } 082 083 @Override 084 synchronized protected void frameCheck() { 085 // If SPROG II is in boot mode, check message framing and checksum 086 if ((bootState != BootState.RESETSENT) && tc.isSIIBootMode() && !reply.strip()) { 087 stopTimer(); 088 JOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorFrameDialogString"), 089 Bundle.getMessage("ErrorFrameDialogTitle"), JOptionPane.ERROR_MESSAGE); 090 log.error("Malformed bootloader reply"); 091 statusBar.setText(Bundle.getMessage("StatusMalformedbootLoaderReply")); 092 bootState = BootState.IDLE; 093 tc.setSprogState(SprogState.NORMAL); 094 return; 095 } 096 if ((bootState != BootState.RESETSENT) && tc.isSIIBootMode() && !reply.getChecksum()) { 097 log.error("Bad bootloader checksum"); 098 statusBar.setText(Bundle.getMessage("StatusBadBootloaderChecksum")); 099 bootState = BootState.IDLE; 100 tc.setSprogState(SprogState.NORMAL); 101 } 102 } 103 104 @Override 105 synchronized protected void stateSetBootSent() { 106 stopTimer(); 107 log.debug("reply in SETBOOTSENT state"); 108 // A reply to the enter bootloader command means the firmware is locked. 109 bootState = BootState.IDLE; 110 tc.setSprogState(SprogState.NORMAL); 111 JOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorFirmwareLocked"), 112 Bundle.getMessage("SprogXFirmwareUpdate"), JOptionPane.ERROR_MESSAGE); 113 statusBar.setText(Bundle.getMessage("ErrorFirmwareLocked")); 114 } 115 116 @Override 117 synchronized protected void stateBootVerReqSent() { 118 stopTimer(); 119 if (log.isDebugEnabled()) { 120 log.debug("reply in VERREQSENT state"); 121 } 122 // see if reply is the version 123 if ((reply.getOpCode() == SprogMessage.RD_VER) && (reply.getElement(1) == 2)) { 124 bootVer = reply.getElement(2); 125 if (log.isDebugEnabled()) { 126 log.debug("Found bootloader version {}", bootVer); 127 } 128 statusBar.setText(Bundle.getMessage("StatusConnectedToBootloader", bootVer)); 129 // Enable the file chooser button 130 setSprogModeButton.setEnabled(true); 131 openFileChooserButton.setEnabled(true); 132 if (blockLen > 0) { 133 // We think we already know the version 134 if (blockLen != SprogType.getBlockLen(bootVer)) { 135 log.error("Bootloader version does not match SPROG type"); 136 bootState = BootState.IDLE; 137 } 138 } else { 139 // Don't yet have correct SPROG version 140 if (bootVer <= 11) { 141 // Force SPROG version SPROG II 1.x or 2.x 142 sv = new SprogVersion(new SprogType(SprogType.SPROGII), ""); 143 } else { 144 // Force SPROG version SPROG SPROG II v3.x (also covers IIv4, SPROG 3 and Nano) 145 sv = new SprogVersion(new SprogType(SprogType.SPROGIIv3), ""); 146 } 147 blockLen = sv.sprogType.getBlockLen(); 148 // We remain in this state until program button is pushed 149 } 150 } else { 151 log.error("Bad reply to RD_VER request"); 152 bootState = BootState.IDLE; 153 tc.setSprogState(SprogState.NORMAL); 154 JOptionPane.showMessageDialog(this, Bundle.getMessage("StatusUnableToConnectBootloader"), 155 Bundle.getMessage("SprogXFirmwareUpdate"), JOptionPane.ERROR_MESSAGE); 156 statusBar.setText(Bundle.getMessage("StatusUnableToConnectBootloader")); 157 } 158 } 159 160 @Override 161 synchronized protected void stateWriteSent() { 162 stopTimer(); 163 if (log.isDebugEnabled()) { 164 log.debug("reply in WRITESENT state"); 165 } 166 // Check for correct response to type of write that was sent 167 if ((reply.getOpCode() == msg.getElement(2)) && (reply.getNumDataElements() == 1) 168 || (reply.getElement(reply.getNumDataElements() - 1) == '.')) { 169 if (hexFile.read() > 0) { 170 // More data to write 171 sendWrite(); 172 } else { 173 doneWriting(); 174 } 175 } else { 176 // Houston, we have a problem 177// JOptionPane.showMessageDialog(this, Bundle.getMessage("StatusBadReplyWriteRequest"), 178// Bundle.getMessage("SprogXFirmwareUpdate", " II"), JOptionPane.ERROR_MESSAGE); 179 log.error("Bad reply to write request"); 180 statusBar.setText(Bundle.getMessage("StatusBadReplyWriteRequest")); 181 bootState = BootState.IDLE; 182 tc.setSprogState(SprogState.NORMAL); 183 } 184 } 185 186 @Override 187 synchronized protected void stateEraseSent() { 188 stopTimer(); 189 if (log.isDebugEnabled()) { 190 log.debug("reply in ERASESENT state"); 191 } 192 // Check for correct response to erase that was sent 193 if ((reply.getOpCode() == msg.getElement(2)) && (reply.getNumDataElements() == 1)) { 194 // Don't erase ICD debug executive if in use 195 if ((sv.sprogType.sprogType < SprogType.SPROGIIv3) && (eraseAddress < 0x7c00) 196 || (sv.sprogType.sprogType >= SprogType.SPROGIIv3) && (eraseAddress < 0x3F00)) { 197 // More data to erase 198 sendErase(); 199 } else { 200 if (log.isDebugEnabled()) { 201 log.debug("Finished erasing"); 202 } 203 statusBar.setText(Bundle.getMessage("StatusEraseComplete")); 204 // Read first line from hexfile 205 if (hexFile.read() > 0) { 206 // Program line and wait for reply 207 if (log.isDebugEnabled()) { 208 log.debug("First write {} {}", hexFile.getLen(), hexFile.getAddress()); 209 } 210 sendWrite(); 211 } else { 212 doneWriting(); 213 } 214 } 215 } else { 216 // Houston, we have a problem 217// JOptionPane.showMessageDialog(this, Bundle.getMessage("StatusBadReplyErase"), 218// Bundle.getMessage("SprogXFirmwareUpdate", " II"), JOptionPane.ERROR_MESSAGE); 219 log.error("Bad reply to erase request"); 220 bootState = BootState.IDLE; 221 tc.setSprogState(SprogState.NORMAL); 222 } 223 } 224 225 @Override 226 synchronized protected void stateSprogModeSent() { 227 stopTimer(); 228 if (log.isDebugEnabled()) { 229 log.debug("reply in SROGMODESENT state"); 230 } 231 // Check for correct response to type of write that was sent 232 if ((reply.getOpCode() == msg.getElement(2)) && (reply.getNumDataElements() == 1)) { 233 if (log.isDebugEnabled()) { 234 log.debug("Reset SPROG"); 235 } 236 msg = SprogMessage.getReset(); 237 bootState = BootState.RESETSENT; 238 tc.sendSprogMessage(msg, this); 239 startLongTimer(); 240 } else { 241 // Houston, we have a problem 242// JOptionPane.showMessageDialog(this, Bundle.getMessage("StatusBadReplyModeRequest"), 243// Bundle.getMessage("SprogXFirmwareUpdate", " II"), JOptionPane.ERROR_MESSAGE); 244 log.error("Bad reply to SPROG Mode request"); 245 bootState = BootState.IDLE; 246 tc.setSprogState(SprogState.NORMAL); 247 } 248 } 249 250 @Override 251 synchronized protected void stateResetSent() { 252 stopTimer(); 253 if (log.isDebugEnabled()) { 254 log.debug("reply in RESETSENT state"); 255 } 256 // Check for correct response to type of write that was sent 257 258 statusBar.setText(Bundle.getMessage("DefaultStatusText")); // Ready, is in jmrixBundle 259 260 tc.setSprogState(SprogState.NORMAL); 261 bootState = BootState.IDLE; 262 } 263 264 @Override 265 synchronized protected void requestBoot() { 266 // Look for SPROG in boot mode by requesting bootloader version. 267 if (log.isDebugEnabled()) { 268 log.debug("Request bootloader version"); 269 } 270 // allow parsing of bootloader replies 271 if (tc == null) { 272 log.warn("requestBoot with null tc, ignored"); 273 return; 274 } 275 tc.setSprogState(SprogState.SIIBOOTMODE); 276 bootState = BootState.VERREQSENT; 277 msg = SprogMessage.getReadBootVersion(); 278 tc.sendSprogMessage(msg, this); 279 startLongTimer(); 280 } 281 282 @Override 283 synchronized protected void sendWrite() { 284 if ((hexFile.getAddressU()&0xFF) >= 0xF0) { 285 // Write to EEPROM 286 if (log.isDebugEnabled()) { 287 log.debug("Send write EE {}", hexFile.getAddress()); 288 } 289 msg = SprogMessage.getWriteEE(hexFile.getAddress(), hexFile.getData()); 290 } else if ((hexFile.getAddressU()&0xFF) >= 0x20) { 291 // Write to user data or config data not supported 292 if (log.isDebugEnabled()) { 293 log.debug("null write {}", hexFile.getAddress()); 294 } 295 msg = null; 296 } else if (sv.sprogType.isValidFlashAddress(hexFile.getAddress())) { 297 // Program code address is above bootloader range and below debug executive 298 if (log.isDebugEnabled()) { 299 log.debug("Send write Flash {}", hexFile.getAddress()); 300 } 301 msg = SprogMessage.getWriteFlash(hexFile.getAddress(), hexFile.getData(), blockLen); 302 if (log.isDebugEnabled()) { 303 log.debug("msg: {}", msg.toString(true)); 304 } 305 } else { 306 // Do nothing 307 if (log.isDebugEnabled()) { 308 log.debug("null write {}", hexFile.getAddress()); 309 } 310 msg = null; 311 } 312 if (msg != null) { 313 bootState = BootState.WRITESENT; 314 statusBar.setText(Bundle.getMessage("StatusWriteX", hexFile.getAddress())); 315 tc.sendSprogMessage(msg, this); 316 if (log.isDebugEnabled()) { 317 log.debug("Sent write command to address {}", hexFile.getAddress()); 318 } 319 startLongTimer(); 320 } else { 321 // use timeout to kick off the next write 322 bootState = BootState.NULLWRITE; 323 statusBar.setText(Bundle.getMessage("StatusSkipX", hexFile.getAddress())); 324 startVShortTimer(); 325 } 326 } 327 328 synchronized private void sendErase() { 329 if (log.isDebugEnabled()) { 330 log.debug("Erase Flash {}", eraseAddress); 331 } 332 int rows = 8; // 512 bytes 333 msg = SprogMessage.getEraseFlash(eraseAddress, rows); 334 bootState = BootState.ERASESENT; 335 statusBar.setText(Bundle.getMessage("StatusEraseX", eraseAddress)); 336 tc.sendSprogMessage(msg, this); 337 if (log.isDebugEnabled()) { 338 log.debug("Sent erase command to address {}", eraseAddress); 339 } 340 eraseAddress += (rows * 64); 341 startLongTimer(); 342 } 343 344 @Override 345 synchronized protected void doneWriting() { 346 // Finished 347 if (log.isDebugEnabled()) { 348 log.debug("Done writing"); 349 } 350 statusBar.setText(Bundle.getMessage("StatusWriteComplete")); 351 openFileChooserButton.setEnabled(false); 352 programButton.setEnabled(false); 353 354 setSprogModeButton.setEnabled(true); 355 bootState = BootState.IDLE; 356 } 357 358 @Override 359 synchronized public void programButtonActionPerformed(java.awt.event.ActionEvent e) { 360 if (hexFile != null) { 361 openFileChooserButton.setEnabled(false); 362 programButton.setEnabled(false); 363 setSprogModeButton.setEnabled(false); 364 eraseAddress = sv.sprogType.getEraseStart(); 365 if (eraseAddress > 0) { 366 if (log.isDebugEnabled()) { 367 log.debug("Start erasing @{}", eraseAddress); 368 } 369 sendErase(); 370 } 371 } 372 } 373 374 @Override 375 synchronized public void setSprogModeButtonActionPerformed(java.awt.event.ActionEvent e) { 376 if (log.isDebugEnabled()) { 377 log.debug("Set SPROG mode"); 378 } 379 msg = SprogMessage.getWriteEE(0xff, new int[]{0}); 380 bootState = BootState.SPROGMODESENT; 381 // Set TC timeout back to normal 382 tc.resetTimeout(); 383 tc.sendSprogMessage(msg, this); 384 startLongTimer(); 385 } 386 387 /** 388 * Removes SprogVersionListener. 389 * Calls Super to stop Timer. 390 * {@inheritDoc} 391 */ 392 @Override 393 public void dispose(){ 394 if (_memo !=null) { 395 _memo.getSprogVersionQuery().removeSprogVersionListener(this); 396 } 397 super.dispose(); 398 } 399 400 private final static Logger log = LoggerFactory 401 .getLogger(SprogIIUpdateFrame.class); 402}