/* * Copyright (C) 2011-2012 Doug Brown * * 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 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 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "mainwindow.h" #include "ui_mainwindow.h" #include "programmer.h" #include "aboutbox.h" #include #include #include #include static Programmer *p; #define selectedCapacityKey "selectedCapacity" #define verifyAfterWriteKey "verifyAfterWrite" #define verifyWhileWritingKey "verifyWhileWriting" #define selectedEraseSizeKey "selectedEraseSize" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), writeFile(NULL), readFile(NULL), writeBuffer(NULL), readBuffer(NULL) { initializing = true; // Make default QSettings use these settings QCoreApplication::setOrganizationName("Doug Brown"); QCoreApplication::setOrganizationDomain("downtowndougbrown.com"); QCoreApplication::setApplicationName("SIMMProgrammer"); QSettings settings; p = new Programmer(); ui->setupUi(this); hideFlashIndividualControls(); ui->pages->setCurrentWidget(ui->notConnectedPage); ui->actionUpdate_firmware->setEnabled(false); // Fill in the list of SIMM chip capacities (programmer can support anywhere up to 8 MB of space) ui->simmCapacityBox->addItem("128 KB (4 x 256Kb/chip)", QVariant(128 * 1024)); ui->simmCapacityBox->addItem("256 KB (4 x 512Kb/chip)", QVariant(256 * 1024)); ui->simmCapacityBox->addItem("512 KB (4 x 1Mb/chip)", QVariant(512 * 1024)); ui->simmCapacityBox->addItem("1 MB (4 x 2Mb/chip)", QVariant(1 * 1024 * 1024)); ui->simmCapacityBox->addItem("2 MB (4 x 4Mb/chip)", QVariant(2 * 1024 * 1024)); ui->simmCapacityBox->addItem("4 MB (2 x 16Mb/chip)", QVariant(4 * 1024 * 1024)); ui->simmCapacityBox->addItem("8 MB (4 x 16Mb/chip)", QVariant(8 * 1024 * 1024)); // Select 2 MB by default (it's what most people will want), or load last-used setting QVariant selectedCapacity = settings.value(selectedCapacityKey, QVariant(2 * 1024 * 1024)); int selectedIndex = ui->simmCapacityBox->findData(selectedCapacity); if (selectedIndex != -1) { ui->simmCapacityBox->setCurrentIndex(selectedIndex); } // Fill in the list of verification options ui->verifyBox->addItem("Don't verify", QVariant(NoVerification)); ui->verifyBox->addItem("Verify while writing", QVariant(VerifyWhileWriting)); ui->verifyBox->addItem("Verify after writing", QVariant(VerifyAfterWrite)); // Decide whether to verify while writing, after writing, or never. // This would probably be better suited as an enum rather than multiple bools, // but I started out with it as a single bool, so for backward compatibility, // I simply added another bool for the "verify while writing" capability. bool verifyAfterWrite = settings.value(verifyAfterWriteKey, false).toBool(); bool verifyWhileWriting = settings.value(verifyWhileWritingKey, true).toBool(); selectedIndex = 0; if (verifyWhileWriting) { selectedIndex = ui->verifyBox->findData(VerifyWhileWriting); } else if (verifyAfterWrite) { selectedIndex = ui->verifyBox->findData(VerifyAfterWrite); } else { selectedIndex = ui->verifyBox->findData(NoVerification); } if (selectedIndex != -1) { ui->verifyBox->setCurrentIndex(selectedIndex); } // Fill in list of "write first xxx bytes" options ui->howMuchToWriteBox->addItem("Erase/write entire SIMM", QVariant(0)); ui->howMuchToWriteBox->addItem("Only erase/write first 256 KB", QVariant(256*1024)); ui->howMuchToWriteBox->addItem("Only erase/write first 512 KB", QVariant(512*1024)); ui->howMuchToWriteBox->addItem("Only erase/write first 1 MB", QVariant(1024*1024)); ui->howMuchToWriteBox->addItem("Only erase/write first 1.5 MB", QVariant(3*512*1024)); ui->howMuchToWriteBox->addItem("Only erase/write first 2 MB", QVariant(2*1024*1024)); // Select "erase entire SIMM" by default, or load last-used setting QVariant selectedEraseSize = settings.value(selectedEraseSizeKey, QVariant(0)); selectedIndex = ui->howMuchToWriteBox->findData(selectedEraseSize); if (selectedIndex != -1) { ui->howMuchToWriteBox->setCurrentIndex(selectedIndex); } ui->chosenWriteFile->setText(""); ui->chosenReadFile->setText(""); writeFileValid = false; readFileValid = false; ui->writeToSIMMButton->setEnabled(false); ui->readFromSIMMButton->setEnabled(false); ui->progressBar->setValue(0); ui->statusLabel->setText(""); ui->cancelButton->setEnabled(false); connect(p, SIGNAL(writeStatusChanged(WriteStatus)), SLOT(programmerWriteStatusChanged(WriteStatus))); connect(p, SIGNAL(writeTotalLengthChanged(uint32_t)), SLOT(programmerWriteTotalLengthChanged(uint32_t))); connect(p, SIGNAL(writeCompletionLengthChanged(uint32_t)), SLOT(programmerWriteCompletionLengthChanged(uint32_t))); connect(p, SIGNAL(writeVerifyTotalLengthChanged(uint32_t)), SLOT(programmerVerifyTotalLengthChanged(uint32_t))); connect(p, SIGNAL(writeVerifyCompletionLengthChanged(uint32_t)), SLOT(programmerVerifyCompletionLengthChanged(uint32_t))); connect(p, SIGNAL(electricalTestStatusChanged(ElectricalTestStatus)), SLOT(programmerElectricalTestStatusChanged(ElectricalTestStatus))); connect(p, SIGNAL(electricalTestFailLocation(uint8_t,uint8_t)), SLOT(programmerElectricalTestLocation(uint8_t,uint8_t))); connect(p, SIGNAL(readStatusChanged(ReadStatus)), SLOT(programmerReadStatusChanged(ReadStatus))); connect(p, SIGNAL(readTotalLengthChanged(uint32_t)), SLOT(programmerReadTotalLengthChanged(uint32_t))); connect(p, SIGNAL(readCompletionLengthChanged(uint32_t)), SLOT(programmerReadCompletionLengthChanged(uint32_t))); connect(p, SIGNAL(identificationStatusChanged(IdentificationStatus)), SLOT(programmerIdentifyStatusChanged(IdentificationStatus))); connect(p, SIGNAL(firmwareFlashStatusChanged(FirmwareFlashStatus)), SLOT(programmerFirmwareFlashStatusChanged(FirmwareFlashStatus))); connect(p, SIGNAL(firmwareFlashTotalLengthChanged(uint32_t)), SLOT(programmerFirmwareFlashTotalLengthChanged(uint32_t))); connect(p, SIGNAL(firmwareFlashCompletionLengthChanged(uint32_t)), SLOT(programmerFirmwareFlashCompletionLengthChanged(uint32_t))); connect(p, SIGNAL(programmerBoardConnected()), SLOT(programmerBoardConnected())); connect(p, SIGNAL(programmerBoardDisconnected()), SLOT(programmerBoardDisconnected())); connect(p, SIGNAL(programmerBoardDisconnectedDuringOperation()), SLOT(programmerBoardDisconnectedDuringOperation())); p->startCheckingPorts(); // Set up the multi chip flasher UI -- connect signals connect(ui->chosenFlashIC1File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenFlashIC2File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenFlashIC3File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenFlashIC4File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenReadIC1File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenReadIC2File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenReadIC3File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->chosenReadIC4File, SIGNAL(textEdited(QString)), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->flashIC1CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->flashIC2CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->flashIC3CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->flashIC4CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->readIC1CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->readIC2CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->readIC3CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->readIC4CheckBox, SIGNAL(clicked()), SLOT(updateFlashIndividualControlsEnabled())); connect(ui->selectFlashIC1Button, SIGNAL(clicked()), SLOT(selectIndividualWriteFileClicked())); connect(ui->selectFlashIC2Button, SIGNAL(clicked()), SLOT(selectIndividualWriteFileClicked())); connect(ui->selectFlashIC3Button, SIGNAL(clicked()), SLOT(selectIndividualWriteFileClicked())); connect(ui->selectFlashIC4Button, SIGNAL(clicked()), SLOT(selectIndividualWriteFileClicked())); connect(ui->selectReadIC1Button, SIGNAL(clicked()), SLOT(selectIndividualReadFileClicked())); connect(ui->selectReadIC2Button, SIGNAL(clicked()), SLOT(selectIndividualReadFileClicked())); connect(ui->selectReadIC3Button, SIGNAL(clicked()), SLOT(selectIndividualReadFileClicked())); connect(ui->selectReadIC4Button, SIGNAL(clicked()), SLOT(selectIndividualReadFileClicked())); initializing = false; } MainWindow::~MainWindow() { delete p; delete ui; } void MainWindow::on_selectWriteFileButton_clicked() { QString filename = QFileDialog::getOpenFileName(this, "Select a ROM image:"); if (!filename.isNull()) { writeFileValid = true; ui->chosenWriteFile->setText(filename); ui->writeToSIMMButton->setEnabled(true); } } void MainWindow::on_selectReadFileButton_clicked() { QString filename = QFileDialog::getSaveFileName(this, "Save ROM image as:"); if (!filename.isNull()) { readFileValid = true; ui->chosenReadFile->setText(filename); ui->readFromSIMMButton->setEnabled(true); } } void MainWindow::on_readFromSIMMButton_clicked() { // Ensure we don't think we're in buffer writing/reading mode...we're reading to // an actual file. if (writeBuffer) { delete writeBuffer; writeBuffer = NULL; } if (readBuffer) { delete readBuffer; readBuffer = NULL; } if (readFile) { readFile->close(); delete readFile; } readFile = new QFile(ui->chosenReadFile->text()); if (readFile) { if (!readFile->open(QFile::WriteOnly)) { delete readFile; readFile = NULL; programmerReadStatusChanged(ReadError); return; } resetAndShowStatusPage(); p->readSIMM(readFile); qDebug() << "Reading from SIMM..."; } else { programmerReadStatusChanged(ReadError); } } void MainWindow::on_writeToSIMMButton_clicked() { // Ensure we don't think we're in buffer writing/reading mode...we're writing // an actual file. if (writeBuffer) { delete writeBuffer; writeBuffer = NULL; } if (readBuffer) { delete readBuffer; readBuffer = NULL; } if (writeFile) { writeFile->close(); delete writeFile; } writeFile = new QFile(ui->chosenWriteFile->text()); if (writeFile) { if (!writeFile->open(QFile::ReadOnly)) { delete writeFile; writeFile = NULL; programmerWriteStatusChanged(WriteError); return; } resetAndShowStatusPage(); uint howMuchToErase = ui->howMuchToWriteBox->itemData(ui->howMuchToWriteBox->currentIndex()).toUInt(); if (howMuchToErase == 0) { p->writeToSIMM(writeFile); } else { p->writeToSIMM(writeFile, 0, howMuchToErase); } qDebug() << "Writing to SIMM..."; } else { programmerWriteStatusChanged(WriteError); } } void MainWindow::on_chosenWriteFile_textEdited(const QString &newText) { QFileInfo fi(newText); if (!newText.isEmpty() && fi.exists() && fi.isFile()) { ui->writeToSIMMButton->setEnabled(true); writeFileValid = true; } else { ui->writeToSIMMButton->setEnabled(false); writeFileValid = false; } } void MainWindow::on_chosenReadFile_textEdited(const QString &newText) { QFileInfo fi(newText); if (!newText.isEmpty() && fi.dir().exists()) { ui->readFromSIMMButton->setEnabled(true); readFileValid = true; } else { ui->readFromSIMMButton->setEnabled(false); readFileValid = false; } } void MainWindow::programmerWriteStatusChanged(WriteStatus newStatus) { switch (newStatus) { case WriteErasing: if (writeBuffer) { ui->statusLabel->setText("Erasing chip(s) to be flashed (this may take a few seconds)..."); } else { ui->statusLabel->setText("Erasing SIMM (this may take a few seconds)..."); } break; case WriteCompleteNoVerify: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } QMessageBox::information(this, "Write complete", "The write operation finished."); returnToControlPage(); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteCompleteVerifyOK: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } QMessageBox::information(this, "Write complete", "The write operation finished, and the contents were verified successfully."); returnToControlPage(); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteVerifying: resetAndShowStatusPage(); break; case WriteVerifyStarting: ui->statusLabel->setText("Verifying SIMM contents..."); break; case WriteVerifyError: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Verify error", "An error occurred reading the SIMM contents for verification."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteVerifyCancelled: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Verify cancelled", "The verify operation was cancelled."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteVerifyTimedOut: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Verify timed out", "The verify operation timed out."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteVerificationFailure: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } // The verify failure code is somewhat complicated so it's best to put // it elsewhere. handleVerifyFailureReply(); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteError: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Write error", "An error occurred writing to the SIMM."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteNeedsFirmwareUpdateBiggerSIMM: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Firmware update needed", "The programmer board needs a firmware update to support a larger SIMM. Please update the firmware and try again."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteNeedsFirmwareUpdateVerifyWhileWrite: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Firmware update needed", "The programmer board needs a firmware update to support the \"verify while writing\" capability. Please update the firmware and try again."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteNeedsFirmwareUpdateErasePortion: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Firmware update needed", "The programmer board needs a firmware update to support the \"erase only a portion of the SIMM\" capability. Please update the firmware and try again."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteNeedsFirmwareUpdateIndividualChips: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Firmware update needed", "The programmer board needs a firmware update to support the \"program individual chips\" capability. Please update the firmware and try again."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteCancelled: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Write cancelled", "The write operation was cancelled."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteEraseComplete: ui->statusLabel->setText("Writing SIMM..."); break; case WriteEraseFailed: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Write error", "An error occurred erasing the SIMM."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteTimedOut: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Write timed out", "The write operation timed out."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteFileTooBig: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "File too big", "The file you chose to write to the SIMM is too big according to the chip size you have selected."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; case WriteEraseBlockWrongSize: if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Bad erase size", "The programmer cannot handle the erase size you chose."); if (writeBuffer) { writeBuffer->close(); delete writeBuffer; writeBuffer = NULL; } break; } } void MainWindow::programmerWriteTotalLengthChanged(uint32_t totalLen) { ui->progressBar->setMaximum((int)totalLen); } void MainWindow::programmerWriteCompletionLengthChanged(uint32_t len) { ui->progressBar->setValue((int)len); } void MainWindow::programmerVerifyTotalLengthChanged(uint32_t totalLen) { ui->progressBar->setMaximum((int)totalLen); } void MainWindow::programmerVerifyCompletionLengthChanged(uint32_t len) { ui->progressBar->setValue((int)len); } void MainWindow::on_electricalTestButton_clicked() { resetAndShowStatusPage(); electricalTestString = ""; p->runElectricalTest(); } void MainWindow::programmerElectricalTestStatusChanged(ElectricalTestStatus newStatus) { switch (newStatus) { case ElectricalTestStarted: ui->statusLabel->setText("Running electrical test (this may take a few seconds)..."); qDebug() << "Electrical test started"; break; case ElectricalTestPassed: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::information(this, "Test passed", "The electrical test passed successfully."); break; case ElectricalTestFailed: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Test failed", "The electrical test failed:\n\n" + electricalTestString); break; case ElectricalTestTimedOut: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Test timed out", "The electrical test operation timed out."); break; case ElectricalTestCouldntStart: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Communication error", "Unable to communicate with programmer board."); break; } } void MainWindow::programmerElectricalTestLocation(uint8_t loc1, uint8_t loc2) { //qDebug() << "Electrical test error at (" << p->electricalTestPinName(loc1) << "," << p->electricalTestPinName(loc2) << ")"; if (electricalTestString != "") { electricalTestString.append("\n"); } electricalTestString.append(p->electricalTestPinName(loc1)); electricalTestString.append(" shorted to "); electricalTestString.append(p->electricalTestPinName(loc2)); } void MainWindow::programmerReadStatusChanged(ReadStatus newStatus) { switch (newStatus) { case ReadStarting: ui->statusLabel->setText("Reading SIMM contents..."); break; case ReadComplete: if (readFile) { readFile->close(); delete readFile; readFile = NULL; } if (readBuffer) { finishMultiRead(); } returnToControlPage(); QMessageBox::information(this, "Read complete", "The read operation finished."); if (readBuffer) { delete readBuffer; readBuffer = NULL; } break; case ReadError: if (readFile) { readFile->close(); delete readFile; readFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Read error", "An error occurred reading from the SIMM."); if (readBuffer) { delete readBuffer; readBuffer = NULL; } break; case ReadCancelled: if (readFile) { readFile->close(); delete readFile; readFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Read cancelled", "The read operation was cancelled."); if (readBuffer) { delete readBuffer; readBuffer = NULL; } break; case ReadTimedOut: if (readFile) { readFile->close(); delete readFile; readFile = NULL; } returnToControlPage(); QMessageBox::warning(this, "Read timed out", "The read operation timed out."); if (readBuffer) { delete readBuffer; readBuffer = NULL; } break; } } void MainWindow::programmerReadTotalLengthChanged(uint32_t totalLen) { ui->progressBar->setMaximum((int)totalLen); } void MainWindow::programmerReadCompletionLengthChanged(uint32_t len) { ui->progressBar->setValue((int)len); } void MainWindow::programmerIdentifyStatusChanged(IdentificationStatus newStatus) { switch (newStatus) { case IdentificationStarting: ui->statusLabel->setText("Identifying chips..."); break; case IdentificationComplete: { ui->pages->setCurrentWidget(ui->controlPage); QString identifyString = "The chips identified themselves as:"; // 4MB SIMM actually only has two chips if (p->SIMMCapacity() == 4*1024*1024) { for (int x = 0; x < 2; x++) { QString thisString; uint8_t manufacturer0 = 0, manufacturer1 = 0; uint8_t device0 = 0, device1 = 0; p->getChipIdentity(x*2, &manufacturer0, &device0); p->getChipIdentity(x*2+1, &manufacturer1, &device1); thisString.sprintf("\nIC%d: Manufacturer 0x%04X, Device 0x%04X", (x + 1), (((uint16_t)manufacturer1) << 8) | manufacturer0, (((uint16_t)device1) << 8) | device0); identifyString.append(thisString); } } else { for (int x = 0; x < 4; x++) { QString thisString; uint8_t manufacturer = 0; uint8_t device = 0; p->getChipIdentity(x, &manufacturer, &device); thisString.sprintf("\nIC%d: Manufacturer 0x%02X, Device 0x%02X", (x + 1), manufacturer, device); identifyString.append(thisString); } } QMessageBox::information(this, "Identification complete", identifyString); break; } case IdentificationError: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Identification error", "An error occurred identifying the chips on the SIMM."); break; case IdentificationTimedOut: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Identification timed out", "The identification operation timed out."); break; case IdentificationNeedsFirmwareUpdate: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Firmware update needed", "The programmer board needs a firmware update to support a larger SIMM. Please update the firmware and try again."); break; } } void MainWindow::programmerFirmwareFlashStatusChanged(FirmwareFlashStatus newStatus) { switch (newStatus) { case FirmwareFlashStarting: ui->statusLabel->setText("Flashing new firmware..."); break; case FirmwareFlashComplete: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::information(this, "Firmware update complete", "The firmware update operation finished."); break; case FirmwareFlashError: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Firmware update error", "An error occurred writing firmware to the device."); break; case FirmwareFlashCancelled: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Firmware update cancelled", "The firmware update was cancelled."); break; case FirmwareFlashTimedOut: ui->pages->setCurrentWidget(ui->controlPage); QMessageBox::warning(this, "Firmware update timed out", "The firmware update operation timed out."); break; } } void MainWindow::programmerFirmwareFlashTotalLengthChanged(uint32_t totalLen) { ui->progressBar->setMaximum((int)totalLen); } void MainWindow::programmerFirmwareFlashCompletionLengthChanged(uint32_t len) { ui->progressBar->setValue((int)len); } void MainWindow::on_actionUpdate_firmware_triggered() { QString filename = QFileDialog::getOpenFileName(this, "Select a firmware image:"); if (!filename.isNull()) { resetAndShowStatusPage(); p->flashFirmware(filename); qDebug() << "Updating firmware..."; } } void MainWindow::on_identifyButton_clicked() { resetAndShowStatusPage(); p->identifySIMMChips(); } void MainWindow::programmerBoardConnected() { returnToControlPage(); ui->actionUpdate_firmware->setEnabled(true); } void MainWindow::programmerBoardDisconnected() { ui->pages->setCurrentWidget(ui->notConnectedPage); ui->actionUpdate_firmware->setEnabled(false); } void MainWindow::programmerBoardDisconnectedDuringOperation() { ui->pages->setCurrentWidget(ui->notConnectedPage); ui->actionUpdate_firmware->setEnabled(false); // Make sure any files have been closed if we were in the middle of something. if (writeFile) { writeFile->close(); delete writeFile; writeFile = NULL; } if (readFile) { readFile->close(); delete readFile; readFile = NULL; } QMessageBox::warning(this, "Programmer lost connection", "Lost contact with the programmer board. Unplug it, plug it back in, and try again."); } void MainWindow::resetAndShowStatusPage() { // Show indeterminate progress bar until communication succeeds/fails ui->progressBar->setRange(0, 0); ui->statusLabel->setText("Communicating with programmer (this may take a few seconds)..."); ui->pages->setCurrentWidget(ui->statusPage); } void MainWindow::on_simmCapacityBox_currentIndexChanged(int index) { uint32_t newCapacity = static_cast(ui->simmCapacityBox->itemData(index).toUInt()); p->setSIMMCapacity(newCapacity); QSettings settings; if (!initializing) { // If we're not initializing (it gets called while we're initializing), // go ahead and save this as the new default. settings.setValue(selectedCapacityKey, newCapacity); } } void MainWindow::on_verifyBox_currentIndexChanged(int index) { if (index < 0) return; VerificationOption vo = static_cast(ui->verifyBox->itemData(index).toUInt()); // Save this as the new default. QSettings settings; p->setVerifyMode(vo); if (!initializing) { if (vo == NoVerification) { settings.setValue(verifyAfterWriteKey, false); settings.setValue(verifyWhileWritingKey, false); } else if (vo == VerifyAfterWrite) { settings.setValue(verifyAfterWriteKey, true); settings.setValue(verifyWhileWritingKey, false); } else if (vo == VerifyWhileWriting) { settings.setValue(verifyAfterWriteKey, false); settings.setValue(verifyWhileWritingKey, true); } } } void MainWindow::on_howMuchToWriteBox_currentIndexChanged(int index) { if (index < 0) return; uint32_t newEraseSize = static_cast(ui->howMuchToWriteBox->itemData(index).toUInt()); QSettings settings; if (!initializing) { // If we're not initializing (it gets called while we're initializing), // go ahead and save this as the new default. settings.setValue(selectedEraseSizeKey, newEraseSize); } } void MainWindow::on_actionAbout_SIMM_Programmer_triggered() { AboutBox::instance()->show(); } void MainWindow::handleVerifyFailureReply() { // Make a comma-separated list of IC names from this list uint8_t badICMask = p->verifyBadChipMask(); QString icList; bool first = true; for (int x = 0; x < 4; x++) { if (badICMask & (1 << x)) { if (first) { // not IC0 through IC3; IC1 through IC4. // that's why I add one. icList.append(QString("IC%1").arg(x+1)); first = false; } else { icList.append(QString(", IC%1").arg(x+1)); } } } returnToControlPage(); QMessageBox::warning(this, "Verify error", "The data read back from the SIMM did not match the data written to it. Bad data on chips: " + icList); } void MainWindow::on_flashIndividualEnterButton_clicked() { showFlashIndividualControls(); updateFlashIndividualControlsEnabled(); ui->pages->setCurrentWidget(ui->flashChipsPage); } void MainWindow::on_returnNormalButton_clicked() { hideFlashIndividualControls(); // to allow the window to be shrunk when not active ui->pages->setCurrentWidget(ui->controlPage); } void MainWindow::hideFlashIndividualControls() { ui->chosenFlashIC1File->hide(); ui->chosenFlashIC2File->hide(); ui->chosenFlashIC3File->hide(); ui->chosenFlashIC4File->hide(); ui->chosenReadIC1File->hide(); ui->chosenReadIC2File->hide(); ui->chosenReadIC3File->hide(); ui->chosenReadIC4File->hide(); ui->flashIC1CheckBox->hide(); ui->flashIC2CheckBox->hide(); ui->flashIC3CheckBox->hide(); ui->flashIC4CheckBox->hide(); ui->readIC1CheckBox->hide(); ui->readIC2CheckBox->hide(); ui->readIC3CheckBox->hide(); ui->readIC4CheckBox->hide(); ui->selectFlashIC1Button->hide(); ui->selectFlashIC2Button->hide(); ui->selectFlashIC3Button->hide(); ui->selectFlashIC4Button->hide(); ui->selectReadIC1Button->hide(); ui->selectReadIC2Button->hide(); ui->selectReadIC3Button->hide(); ui->selectReadIC4Button->hide(); ui->multiFlashChipsButton->hide(); ui->multiReadChipsButton->hide(); ui->returnNormalButton->hide(); } void MainWindow::showFlashIndividualControls() { ui->chosenFlashIC1File->show(); ui->chosenFlashIC2File->show(); ui->chosenFlashIC3File->show(); ui->chosenFlashIC4File->show(); ui->chosenReadIC1File->show(); ui->chosenReadIC2File->show(); ui->chosenReadIC3File->show(); ui->chosenReadIC4File->show(); ui->flashIC1CheckBox->show(); ui->flashIC2CheckBox->show(); ui->flashIC3CheckBox->show(); ui->flashIC4CheckBox->show(); ui->readIC1CheckBox->show(); ui->readIC2CheckBox->show(); ui->readIC3CheckBox->show(); ui->readIC4CheckBox->show(); ui->selectFlashIC1Button->show(); ui->selectFlashIC2Button->show(); ui->selectFlashIC3Button->show(); ui->selectFlashIC4Button->show(); ui->selectReadIC1Button->show(); ui->selectReadIC2Button->show(); ui->selectReadIC3Button->show(); ui->selectReadIC4Button->show(); ui->multiFlashChipsButton->show(); ui->multiReadChipsButton->show(); ui->returnNormalButton->show(); } void MainWindow::updateFlashIndividualControlsEnabled() { int numWriteFilesChecked = 0; int numReadFilesChecked = 0; bool hasBadWriteFileName = false; bool hasBadReadFileName = false; QCheckBox * const flashBoxes[] = {ui->flashIC1CheckBox, ui->flashIC2CheckBox, ui->flashIC3CheckBox, ui->flashIC4CheckBox}; QCheckBox * const readBoxes[] = {ui->readIC1CheckBox, ui->readIC2CheckBox, ui->readIC3CheckBox, ui->readIC4CheckBox}; QPushButton * const flashSelectButtons[] = {ui->selectFlashIC1Button, ui->selectFlashIC2Button, ui->selectFlashIC3Button, ui->selectFlashIC4Button}; QPushButton * const readSelectButtons[] = {ui->selectReadIC1Button, ui->selectReadIC2Button, ui->selectReadIC3Button, ui->selectReadIC4Button}; QLineEdit * const flashChosenFileEdits[] = {ui->chosenFlashIC1File, ui->chosenFlashIC2File, ui->chosenFlashIC3File, ui->chosenFlashIC4File}; QLineEdit * const readChosenFileEdits[] = {ui->chosenReadIC1File, ui->chosenReadIC2File, ui->chosenReadIC3File, ui->chosenReadIC4File}; for (size_t x = 0; x < sizeof(flashBoxes)/sizeof(flashBoxes[0]); x++) { bool isChecked = flashBoxes[x]->isChecked(); flashChosenFileEdits[x]->setEnabled(isChecked); flashSelectButtons[x]->setEnabled(isChecked); if (isChecked) { numWriteFilesChecked++; QFileInfo fi(flashChosenFileEdits[x]->text()); if (flashChosenFileEdits[x]->text().isEmpty() || !fi.exists() || !fi.isFile()) { hasBadWriteFileName = true; } } isChecked = readBoxes[x]->isChecked(); readChosenFileEdits[x]->setEnabled(isChecked); readSelectButtons[x]->setEnabled(isChecked); if (isChecked) { numReadFilesChecked++; QFileInfo fi(readChosenFileEdits[x]->text()); if (readChosenFileEdits[x]->text().isEmpty() || !fi.dir().exists()) { hasBadReadFileName = true; } } } // All of the individual controls should be handled; now do the two main // buttons. if ((numWriteFilesChecked == 0) || hasBadWriteFileName) { ui->multiFlashChipsButton->setEnabled(false); } else { ui->multiFlashChipsButton->setEnabled(true); } if ((numReadFilesChecked == 0) || hasBadReadFileName) { ui->multiReadChipsButton->setEnabled(false); } else { ui->multiReadChipsButton->setEnabled(true); } } void MainWindow::selectIndividualWriteFileClicked() { QString filename = QFileDialog::getOpenFileName(this, "Select a file to write to the chip:"); if (!filename.isNull()) { if (sender() == static_cast(ui->selectFlashIC1Button)) { ui->chosenFlashIC1File->setText(filename); } else if (sender() == static_cast(ui->selectFlashIC2Button)) { ui->chosenFlashIC2File->setText(filename); } else if (sender() == static_cast(ui->selectFlashIC3Button)) { ui->chosenFlashIC3File->setText(filename); } else if (sender() == static_cast(ui->selectFlashIC4Button)) { ui->chosenFlashIC4File->setText(filename); } updateFlashIndividualControlsEnabled(); } } void MainWindow::selectIndividualReadFileClicked() { QString filename = QFileDialog::getSaveFileName(this, "Save binary file as:"); if (!filename.isNull()) { if (sender() == static_cast(ui->selectReadIC1Button)) { ui->chosenReadIC1File->setText(filename); } else if (sender() == static_cast(ui->selectReadIC2Button)) { ui->chosenReadIC2File->setText(filename); } else if (sender() == static_cast(ui->selectReadIC3Button)) { ui->chosenReadIC3File->setText(filename); } else if (sender() == static_cast(ui->selectReadIC4Button)) { ui->chosenReadIC4File->setText(filename); } updateFlashIndividualControlsEnabled(); } } void MainWindow::on_multiFlashChipsButton_clicked() { QCheckBox * const flashBoxes[] = {ui->flashIC1CheckBox, ui->flashIC2CheckBox, ui->flashIC3CheckBox, ui->flashIC4CheckBox}; QLineEdit * const flashChosenFileEdits[] = {ui->chosenFlashIC1File, ui->chosenFlashIC2File, ui->chosenFlashIC3File, ui->chosenFlashIC4File}; // Read up to four files and create a combined image that we flash using // the standard procedure... if (writeBuffer) { delete writeBuffer; } writeBuffer = new QBuffer(); qint64 maxSize = 0; bool hadError = false; uint8_t chipsMask = 0; // Read each file and ensure it exists. Oh, and create the mask of which // chips we're flashing. QFile *files[sizeof(flashBoxes)/sizeof(flashBoxes[0])] = {NULL, NULL, NULL, NULL}; for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (flashBoxes[x]->isChecked()) { files[x] = new QFile(flashChosenFileEdits[x]->text()); if (!files[x]->exists()) { hadError = true; } if (files[x]->size() > maxSize) { maxSize = files[x]->size(); } files[x]->open(QFile::ReadOnly); // Create our chip mask. chip mask is backward from IC numbering // (bit 0 = IC4, bit 1 = IC3, ...) chipsMask |= (1 << (3-x)); } } // If there was an error or one of the files picked was too big, // error out. if (hadError || (maxSize > (p->SIMMCapacity() / 4))) { for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (files[x]) delete files[x]; } programmerWriteStatusChanged(WriteError); return; } // Combine the (up to four) files into a single // interleaved file to send to the SIMM writeBuffer->open(QFile::ReadWrite); for (int x = 0; x < maxSize; x++) { // Go in reverse order through the files so the data is saved to // the SIMM in the correct order for (int y = (int)(sizeof(files)/sizeof(files[0])) - 1; y >= 0; y--) { char c = 0xFF; if (files[y] && !files[y]->atEnd()) { files[y]->getChar(&c); } writeBuffer->putChar(c); } } // Discard the temporary files for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (files[x]) { files[x]->close(); delete files[x]; } } // Now go back to the beginning of the file... // and write it! resetAndShowStatusPage(); writeBuffer->seek(0); p->writeToSIMM(writeBuffer, chipsMask); } void MainWindow::on_multiReadChipsButton_clicked() { QCheckBox * const readBoxes[] = {ui->readIC1CheckBox, ui->readIC2CheckBox, ui->readIC3CheckBox, ui->readIC4CheckBox}; QLineEdit * const readChosenFileEdits[] = {ui->chosenReadIC1File, ui->chosenReadIC2File, ui->chosenReadIC3File, ui->chosenReadIC4File}; // Prepare to read the files; when the read is complete we will save // the files as necessary if (readBuffer) { delete readBuffer; } readBuffer = new QBuffer(); // Try to open each file to make sure it's writable first. Then close it. bool hadError = false; for (size_t x = 0; x < sizeof(readBoxes)/sizeof(readBoxes[0]); x++) { if (readBoxes[x]->isChecked()) { QFile tmp(readChosenFileEdits[x]->text()); if (!tmp.open(QFile::ReadWrite)) { hadError = true; } else { tmp.close(); } } } // If there was an error creating one of the files, bail out. if (hadError) { programmerReadStatusChanged(ReadError); return; } // Open up the buffer to read into readBuffer->open(QFile::ReadWrite); // Now start reading it! resetAndShowStatusPage(); p->readSIMM(readBuffer); } void MainWindow::finishMultiRead() { QCheckBox * const readBoxes[] = {ui->readIC1CheckBox, ui->readIC2CheckBox, ui->readIC3CheckBox, ui->readIC4CheckBox}; QLineEdit * const readChosenFileEdits[] = {ui->chosenReadIC1File, ui->chosenReadIC2File, ui->chosenReadIC3File, ui->chosenReadIC4File}; bool hadError = false; QFile *files[sizeof(readBoxes)/sizeof(readBoxes[0])] = {NULL, NULL, NULL, NULL}; for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (readBoxes[x]->isChecked()) { files[x] = new QFile(readChosenFileEdits[x]->text()); if (!files[x]->open(QFile::WriteOnly)) { hadError = true; } } } if (hadError) { for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (files[x]) delete files[x]; } programmerReadStatusChanged(ReadError); return; } // Take the final read file and de-interleave it into separate chip files readBuffer->seek(0); while (!readBuffer->atEnd()) { // Go in reverse order through the files so the data is saved to the // files in the correct order for (int y = (int)(sizeof(files)/sizeof(files[0])) - 1; y >= 0; y--) { char c = 0xFF; if (readBuffer->getChar(&c)) // grab a character... { // and as long as it was a success, stick it into the appropriate file // (IF we have one), or discard it if we didn't care about the chip // it went with. if (files[y]) { files[y]->putChar(c); } } else // no success? we're probably near end of SIMM. just discard it and close the file { files[y]->close(); delete files[y]; files[y] = NULL; } } } // Close the individual files that are remaining for (size_t x = 0; x < sizeof(files)/sizeof(files[0]); x++) { if (files[x]) { files[x]->close(); delete files[x]; } } } void MainWindow::returnToControlPage() { // Depending on what we were doing, return to the correct page if (writeBuffer || readBuffer) { ui->pages->setCurrentWidget(ui->flashChipsPage); } else { ui->pages->setCurrentWidget(ui->controlPage); } }