/* * 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 "programmer.h" #include #include #include typedef enum ProgrammerCommandState { WaitingForNextCommand = 0, WriteSIMMWaitingEraseReply, WriteSIMMWaitingWriteReply, WriteSIMMWaitingFinishReply, WriteSIMMWaitingWriteMoreReply, ElectricalTestWaitingStartReply, ElectricalTestWaitingNextStatus, ElectricalTestWaitingFirstFail, ElectricalTestWaitingSecondFail, ReadSIMMWaitingStartReply, ReadSIMMWaitingLengthReply, ReadSIMMWaitingData, ReadSIMMWaitingStatusReply, BootloaderStateAwaitingOKReply, BootloaderStateAwaitingReply, BootloaderStateAwaitingOKReplyToBootloader, BootloaderStateAwaitingReplyToBootloader, BootloaderStateAwaitingUnplug, BootloaderStateAwaitingPlug, BootloaderStateAwaitingUnplugToBootloader, BootloaderStateAwaitingPlugToBootloader, IdentificationAwaitingOKReply, IdentificationWaitingData, IdentificationAwaitingDoneReply, BootloaderEraseProgramAwaitingStartOKReply, BootloaderEraseProgramWaitingFinishReply, BootloaderEraseProgramWaitingWriteMoreReply, BootloaderEraseProgramWaitingWriteReply } ProgrammerCommandState; typedef enum ProgrammerBoardFoundState { ProgrammerBoardNotFound, ProgrammerBoardFound } ProgrammerBoardFoundState; typedef enum ProgrammerCommand { EnterWaitingMode = 0, DoElectricalTest, IdentifyChips, ReadByte, ReadChips, EraseChips, WriteChips, GetBootloaderState, EnterBootloader, EnterProgrammer, BootloaderEraseAndWriteProgram } ProgrammerCommand; typedef enum ProgrammerReply { CommandReplyOK, CommandReplyError, CommandReplyInvalid } ProgrammerReply; typedef enum ComputerReadReply { ComputerReadOK, ComputerReadCancel } ComputerReadReply; typedef enum ProgrammerReadReply { ProgrammerReadOK, ProgrammerReadError, ProgrammerReadMoreData, ProgrammerReadFinished, ProgrammerReadConfirmCancel } ProgrammerReadReply; typedef enum ComputerWriteReply { ComputerWriteMore, ComputerWriteFinish, ComputerWriteCancel } ComputerWriteReply; typedef enum ProgrammerWriteReply { ProgrammerWriteOK, ProgrammerWriteError, ProgrammerWriteConfirmCancel } ProgrammerWriteReply; typedef enum ProgrammerIdentifyReply { ProgrammerIdentifyDone } ProgrammerIdentifyReply; typedef enum ProgrammerElectricalTestReply { ProgrammerElectricalTestFail, ProgrammerElectricalTestDone } ProgrammerElectricalTestReply; typedef enum BootloaderStateReply { BootloaderStateInBootloader, BootloaderStateInProgrammer } BootloaderStateReply; typedef enum ProgrammerBootloaderEraseWriteReply { BootloaderWriteOK, BootloaderWriteError, BootloaderWriteConfirmCancel } ProgrammerBootloaderEraseWriteReply; typedef enum ComputerBootloaderEraseWriteRequest { ComputerBootloaderWriteMore = 0, ComputerBootloaderFinish, ComputerBootloaderCancel } ComputerBootloaderEraseWriteRequest; #define PROGRAMMER_USB_VENDOR_ID 0x16D0 #define PROGRAMMER_USB_DEVICE_ID 0x06AA #define WRITE_CHUNK_SIZE 1024 #define READ_CHUNK_SIZE 1024 #define FIRMWARE_CHUNK_SIZE 1024 static ProgrammerCommandState curState = WaitingForNextCommand; // After identifying that we're in the main program, what will be the command // we will send and the state we will be waiting in? static ProgrammerCommandState nextState = WaitingForNextCommand; static uint8_t nextSendByte = 0; static ProgrammerBoardFoundState foundState = ProgrammerBoardNotFound; static QString programmerBoardPortName; Programmer::Programmer(QObject *parent) : QObject(parent) { serialPort = new QextSerialPort(QextSerialPort::EventDriven); connect(serialPort, SIGNAL(readyRead()), SLOT(dataReady())); } Programmer::~Programmer() { closePort(); delete serialPort; } void Programmer::readSIMM(QIODevice *device) { //readFile = new QFile(filename); //readFile->open(QFile::WriteOnly); //lenRead = 0; //lenRemaining = _simmCapacity; readDevice = device; lenRead = 0; lenRemaining = _simmCapacity; startProgrammerCommand(ReadChips, ReadSIMMWaitingStartReply); } void Programmer::writeToSIMM(QIODevice *device) { /*writeFile = new QFile(filename); if (!writeFile->open(QFile::ReadOnly)) { curState = WaitingForNextCommand; emit writeStatusChanged(WriteError); return; } if (writeFile->size() > _simmCapacity) { curState = WaitingForNextCommand; emit writeStatusChanged(WriteFileTooBig); return; } else { lenWritten = 0; writeLenRemaining = writeFile->size(); startProgrammerCommand(EraseChips, WriteSIMMWaitingEraseReply); }*/ writeDevice = device; if (writeDevice->size() > _simmCapacity) { curState = WaitingForNextCommand; emit writeStatusChanged(WriteFileTooBig); return; } else { lenWritten = 0; writeLenRemaining = writeDevice->size(); startProgrammerCommand(EraseChips, WriteSIMMWaitingEraseReply); } } void Programmer::sendByte(uint8_t b) { serialPort->write((const char *)&b, 1); } uint8_t Programmer::readByte() { uint8_t returnVal; serialPort->read((char *)&returnVal, 1); // TODO: Error checking if read fails? return returnVal; } void Programmer::dataReady() { while (!serialPort->atEnd()) { handleChar(readByte()); } } void Programmer::handleChar(uint8_t c) { switch (curState) { case WaitingForNextCommand: // Not expecting anything. Ignore it. break; case WriteSIMMWaitingEraseReply: { switch (c) { case CommandReplyOK: sendByte(WriteChips); curState = WriteSIMMWaitingWriteReply; qDebug() << "Chips erased. Now asking to start writing..."; emit writeStatusChanged(WriteEraseComplete); emit writeTotalLengthChanged(writeLenRemaining); emit writeCompletionLengthChanged(lenWritten); break; case CommandReplyError: qDebug() << "Error erasing chips."; curState = WaitingForNextCommand; closePort(); emit writeStatusChanged(WriteEraseFailed); break; } break; } case WriteSIMMWaitingWriteReply: switch (c) { case CommandReplyOK: // We're in write SIMM mode. Now ask to start writing if (writeLenRemaining > 0) { sendByte(ComputerWriteMore); curState = WriteSIMMWaitingWriteMoreReply; qDebug() << "Write more..." << writeLenRemaining << "remaining."; } else { sendByte(ComputerWriteFinish); curState = WriteSIMMWaitingFinishReply; qDebug() << "Finished writing. Sending write finish command..."; } break; case CommandReplyError: qDebug() << "Error entering write mode."; curState = WaitingForNextCommand; closePort(); emit writeStatusChanged(WriteError); break; } break; case WriteSIMMWaitingWriteMoreReply: { switch (c) { case ProgrammerWriteOK: { qDebug() << "Programmer replied OK to send 1024 bytes of data! Sending..."; // Write the next chunk of data to the SIMM... int chunkSize = WRITE_CHUNK_SIZE; if (writeLenRemaining < WRITE_CHUNK_SIZE) { chunkSize = writeLenRemaining; } // Read the chunk from the file! QByteArray thisChunk = writeDevice->read(chunkSize); // If it isn't a WRITE_CHUNK_SIZE chunk, pad the rest of it with 0xFFs (unprogrammed bytes) // so the total chunk size is WRITE_CHUNK_SIZE, since that's what the programmer board expects. for (int x = writeLenRemaining; x < WRITE_CHUNK_SIZE; x++) { thisChunk.append(0xFF); } // Write the chunk out (it's asynchronous so will return immediately) serialPort->write(thisChunk); // OK, now we're waiting to hear back from the programmer on the result qDebug() << "Waiting for status reply..."; curState = WriteSIMMWaitingWriteReply; writeLenRemaining -= chunkSize; lenWritten += chunkSize; emit writeCompletionLengthChanged(lenWritten); break; } case ProgrammerWriteError: default: qDebug() << "Error writing to chips."; curState = WaitingForNextCommand; closePort(); emit writeStatusChanged(WriteError); break; } break; } case WriteSIMMWaitingFinishReply: switch (c) { case ProgrammerWriteOK: curState = WaitingForNextCommand; qDebug() << "Write success at end"; closePort(); emit writeStatusChanged(WriteComplete); break; case ProgrammerWriteError: default: qDebug() << "Write failure at end"; curState = WaitingForNextCommand; closePort(); emit writeStatusChanged(WriteError); break; } break; // ELECTRICAL TEST STATE HANDLERS case ElectricalTestWaitingStartReply: switch (c) { case CommandReplyOK: curState = ElectricalTestWaitingNextStatus; emit electricalTestStatusChanged(ElectricalTestStarted); electricalTestErrorCounter = 0; break; case CommandReplyError: case CommandReplyInvalid: default: curState = WaitingForNextCommand; closePort(); emit electricalTestStatusChanged(ElectricalTestCouldntStart); } break; case ElectricalTestWaitingNextStatus: switch (c) { case ProgrammerElectricalTestDone: curState = WaitingForNextCommand; closePort(); if (electricalTestErrorCounter > 0) { emit electricalTestStatusChanged(ElectricalTestFailed); } else { emit electricalTestStatusChanged(ElectricalTestPassed); } break; case ProgrammerElectricalTestFail: electricalTestErrorCounter++; curState = ElectricalTestWaitingFirstFail; break; } break; case ElectricalTestWaitingFirstFail: electricalTestFirstErrorLoc = c; curState = ElectricalTestWaitingSecondFail; break; case ElectricalTestWaitingSecondFail: emit electricalTestFailLocation(electricalTestFirstErrorLoc, c); curState = ElectricalTestWaitingNextStatus; break; // READ SIMM STATE HANDLERS case ReadSIMMWaitingStartReply: switch (c) { case CommandReplyOK: emit readStatusChanged(ReadStarting); curState = ReadSIMMWaitingLengthReply; // Send the length requesting to be read sendByte((lenRemaining >> 0) & 0xFF); sendByte((lenRemaining >> 8) & 0xFF); sendByte((lenRemaining >> 16) & 0xFF); sendByte((lenRemaining >> 24) & 0xFF); // Now wait for the go-ahead from the programmer's side break; case CommandReplyError: case CommandReplyInvalid: default: curState = WaitingForNextCommand; emit readStatusChanged(ReadError); break; } break; case ReadSIMMWaitingLengthReply: switch (c) { case ProgrammerReadOK: curState = ReadSIMMWaitingData; emit readTotalLengthChanged(_simmCapacity); emit readCompletionLengthChanged(0); readChunkLenRemaining = READ_CHUNK_SIZE; break; case ProgrammerReadError: default: curState = WaitingForNextCommand; closePort(); emit readStatusChanged(ReadError); break; } break; case ReadSIMMWaitingData: readDevice->write((const char *)&c, 1); lenRead++; if (--readChunkLenRemaining == 0) { emit readCompletionLengthChanged(lenRead); qDebug() << "Received a chunk of data"; sendByte(ComputerReadOK); curState = ReadSIMMWaitingStatusReply; } break; case ReadSIMMWaitingStatusReply: switch (c) { case ProgrammerReadFinished: curState = WaitingForNextCommand; closePort(); emit readStatusChanged(ReadComplete); break; case ProgrammerReadConfirmCancel: curState = WaitingForNextCommand; closePort(); emit readStatusChanged(ReadCancelled); break; case ProgrammerReadMoreData: curState = ReadSIMMWaitingData; readChunkLenRemaining = READ_CHUNK_SIZE; break; } break; // BOOTLOADER STATE HANDLERS case BootloaderStateAwaitingOKReply: if (c == CommandReplyOK) { // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply. curState = BootloaderStateAwaitingReply; } else { curState = WaitingForNextCommand; qDebug() << "Unable to enter programmer mode"; // TODO: Error out somehow } break; case BootloaderStateAwaitingReply: switch (c) { case BootloaderStateInBootloader: // Oops! We're in the bootloader. Better change over to the programmer. // TODO: Send "enter programmer" command. // Close serial port. // Wait for serial port to reappear (or just wait a fixed time?) // Open serial port. // Ensure we're in the programmer? // Then do the command correctly qDebug() << "We're in the bootloader, so sending an \"enter programmer\" request."; emit startStatusChanged(ProgrammerInitializing); sendByte(EnterProgrammer); closePort(); // Now wait for it to reconnect curState = BootloaderStateAwaitingUnplug; break; case BootloaderStateInProgrammer: // Good to go... // So change to the next state and send out the next command // to begin whatever sequence of events we expected. qDebug() << "Already in programmer. Good! Do the command now..."; emit startStatusChanged(ProgrammerInitialized); curState = nextState; sendByte(nextSendByte); // Special case: Send out notification we are starting an erase command. // I don't have any hooks into the process between now and the erase reply. if (nextSendByte == EraseChips) { emit writeStatusChanged(WriteErasing); } break; // TODO: Otherwise, raise an error? } break; case BootloaderStateAwaitingOKReplyToBootloader: if (c == CommandReplyOK) { // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply. curState = BootloaderStateAwaitingReplyToBootloader; } else { curState = WaitingForNextCommand; qDebug() << "Unable to enter bootloader mode"; // TODO: Error out somehow } break; case BootloaderStateAwaitingReplyToBootloader: switch (c) { case BootloaderStateInProgrammer: // Oops! We're in the programmer. Better change over to the bootloader. // TODO: Send "enter bootloader" command. // Close serial port. // Wait for serial port to reappear (or just wait a fixed time?) // Open serial port. // Ensure we're in the bootloader? // Then do the command correctly qDebug() << "We're in the programmer, so sending an \"enter bootloader\" request."; emit startStatusChanged(ProgrammerInitializing); sendByte(EnterBootloader); closePort(); // Now wait for it to reconnect curState = BootloaderStateAwaitingUnplugToBootloader; break; case BootloaderStateInBootloader: // Good to go... // So change to the next state and send out the next command // to begin whatever sequence of events we expected. qDebug() << "Already in bootloader. Good! Do the command now..."; emit startStatusChanged(ProgrammerInitialized); curState = nextState; sendByte(nextSendByte); break; // TODO: Otherwise, raise an error? } break; // IDENTIFICATION STATE HANDLERS case IdentificationAwaitingOKReply: if (c == CommandReplyOK) { // Good to go, now waiting for identification data emit identificationStatusChanged(IdentificationStarting); curState = IdentificationWaitingData; identificationCounter = 0; } else { emit identificationStatusChanged(IdentificationError); curState = WaitingForNextCommand; } break; case IdentificationWaitingData: if (identificationCounter & 1) // device ID? { chipDeviceIDs[identificationCounter/2] = c; } else // manufacturer ID? { chipManufacturerIDs[identificationCounter/2] = c; } // All done? if (++identificationCounter >= 8) { curState = IdentificationAwaitingDoneReply; } break; case IdentificationAwaitingDoneReply: curState = WaitingForNextCommand; closePort(); if (c == ProgrammerIdentifyDone) { emit identificationStatusChanged(IdentificationComplete); } else { emit identificationStatusChanged(IdentificationError); } break; // WRITE BOOTLOADER PROGRAM STATE HANDLERS case BootloaderEraseProgramAwaitingStartOKReply: if (c == CommandReplyOK) { emit firmwareFlashStatusChanged(FirmwareFlashStarting); sendByte(ComputerBootloaderWriteMore); curState = BootloaderEraseProgramWaitingWriteMoreReply; } else { curState = WaitingForNextCommand; closePort(); firmwareFile->close(); emit firmwareFlashStatusChanged(FirmwareFlashError); } break; case BootloaderEraseProgramWaitingFinishReply: if (c == BootloaderWriteOK) { curState = WaitingForNextCommand; closePort(); firmwareFile->close(); emit firmwareFlashStatusChanged(FirmwareFlashComplete); } else { curState = WaitingForNextCommand; closePort(); firmwareFile->close(); emit firmwareFlashStatusChanged(FirmwareFlashError); } break; case BootloaderEraseProgramWaitingWriteMoreReply: if (c == BootloaderWriteOK) { // Send the next chunk of data qDebug() << "Bootloader replied OK to send 1024 bytes of data! Sending..."; int chunkSize = FIRMWARE_CHUNK_SIZE; if (firmwareLenRemaining < FIRMWARE_CHUNK_SIZE) { chunkSize = firmwareLenRemaining; } // Read the chunk from the file! QByteArray thisChunk = firmwareFile->read(chunkSize); // If it isn't FIRMWARE_CHUNK_SIZE, pad the rest with 0xFF // (unprogrammed bytes) for (int x = firmwareLenRemaining; x < FIRMWARE_CHUNK_SIZE; x++) { thisChunk.append(0xFF); } // Write the chunk out (it's asynchronous so will return immediately) serialPort->write(thisChunk); // OK, now we're waiting to hear back from the programmer on the result qDebug() << "Waiting for status reply..."; curState = BootloaderEraseProgramWaitingWriteReply; firmwareLenRemaining -= chunkSize; firmwareLenWritten += chunkSize; emit firmwareFlashCompletionLengthChanged(firmwareLenWritten); } else { curState = WaitingForNextCommand; closePort(); firmwareFile->close(); emit firmwareFlashStatusChanged(FirmwareFlashError); } break; case BootloaderEraseProgramWaitingWriteReply: if (c == CommandReplyOK) { // Either ask to send the next chunk, or send a "finish" response if (firmwareLenRemaining > 0) { sendByte(ComputerBootloaderWriteMore); curState = BootloaderEraseProgramWaitingWriteMoreReply; } else { sendByte(ComputerBootloaderFinish); curState = BootloaderEraseProgramWaitingFinishReply; } } else { curState = WaitingForNextCommand; closePort(); firmwareFile->close(); emit firmwareFlashStatusChanged(FirmwareFlashError); } break; // UNUSED STATE HANDLERS (They are handled elsewhere) case BootloaderStateAwaitingPlug: case BootloaderStateAwaitingUnplug: case BootloaderStateAwaitingPlugToBootloader: case BootloaderStateAwaitingUnplugToBootloader: break; } } void Programmer::runElectricalTest() { startProgrammerCommand(DoElectricalTest, ElectricalTestWaitingStartReply); } QString Programmer::electricalTestPinName(uint8_t index) { if (index <= LAST_ADDRESS_LINE_FAIL_INDEX) { return QString().sprintf("A%d", index - FIRST_ADDRESS_LINE_FAIL_INDEX); } else if (index <= LAST_DATA_LINE_FAIL_INDEX) { // The byte ordering is backwards to the labeling, so I have to fix that. // Reverse the byte ordering so we have the correct number in terms of how // D0 to D31 are labeled... index = index - FIRST_DATA_LINE_FAIL_INDEX; if (index < 8) { index = index + 24; } else if (index < 16) { index = index + 8; } else if (index < 24) { index = index - 8; } else { index = index - 24; } return QString().sprintf("D%d", index); } else if (index == CS_FAIL_INDEX) { return "CS"; } else if (index == OE_FAIL_INDEX) { return "OE"; } else if (index == WE_FAIL_INDEX) { return "WE"; } else if (index == GROUND_FAIL_INDEX) { return "GND"; } else { return "?"; } } void Programmer::identifySIMMChips() { startProgrammerCommand(IdentifyChips, IdentificationAwaitingOKReply); } void Programmer::getChipIdentity(int chipIndex, uint8_t *manufacturer, uint8_t *device) { if ((chipIndex >= 0) && (chipIndex < 4)) { *manufacturer = chipManufacturerIDs[chipIndex]; *device = chipDeviceIDs[chipIndex]; } else { *manufacturer = 0; *device = 0; } } void Programmer::flashFirmware(QString filename) { firmwareFile = new QFile(filename); if (!firmwareFile->open(QFile::ReadOnly)) { curState = WaitingForNextCommand; emit firmwareFlashStatusChanged(FirmwareFlashError); return; } firmwareLenWritten = 0; firmwareLenRemaining = firmwareFile->size(); emit firmwareFlashTotalLengthChanged(firmwareLenRemaining); emit firmwareFlashCompletionLengthChanged(firmwareLenWritten); startBootloaderCommand(BootloaderEraseAndWriteProgram, BootloaderEraseProgramAwaitingStartOKReply); } // Begins a command by opening the serial port, making sure we're in the PROGRAMMER // rather than the bootloader, then sending a command and setting a new command state. // TODO: When it fails, this needs to carry errors over somehow. // newState is really just a ProgrammerCommandState but in order to keep // ProgrammerCommandState private, I did it this way. void Programmer::startProgrammerCommand(uint8_t commandByte, uint32_t newState) { nextState = (ProgrammerCommandState)newState; nextSendByte = commandByte; curState = BootloaderStateAwaitingOKReply; openPort(); sendByte(GetBootloaderState); } // Begins a command by opening the serial port, making sure we're in the BOOTLOADER // rather than the programmer, then sending a command and setting a new command state. // TODO: When it fails, this needs to carry errors over somehow. // newState is really just a ProgrammerCommandState but in order to keep // ProgrammerCommandState private, I did it this way. void Programmer::startBootloaderCommand(uint8_t commandByte, uint32_t newState) { nextState = (ProgrammerCommandState)newState; nextSendByte = commandByte; curState = BootloaderStateAwaitingOKReplyToBootloader; openPort(); sendByte(GetBootloaderState); } #include void Programmer::portDiscovered(const QextPortInfo &info) { if ((foundState == ProgrammerBoardNotFound) && (info.vendorID == PROGRAMMER_USB_VENDOR_ID) && (info.productID == PROGRAMMER_USB_DEVICE_ID) && (info.portName != "")) { // Note: I check that portName != "" because QextSerialEnumerator seems to give me // 2 notifications that match the vendor ID -- one is the real deal, and the other // has a blank port name. If I match on the blank port name one, it breaks. #ifdef Q_WS_WIN programmerBoardPortName = "\\\\.\\" + info.portName; #else programmerBoardPortName = info.portName; #endif foundState = ProgrammerBoardFound; // I create a temporary timer here because opening it immediately seems to crash // Mac OS X in my limited testing. Don't worry about a memory leak -- the // portDiscovered_internal() slot will delete the newly-allocated QTimer. QTimer *t = new QTimer(); connect(t, SIGNAL(timeout()), SLOT(portDiscovered_internal())); t->setInterval(50); t->setSingleShot(true); t->start(); } } void Programmer::portDiscovered_internal() { // Delete the QTimer that sent us this signal. Ugly, but it works... sender()->deleteLater(); closePort(); serialPort->setPortName(programmerBoardPortName); // Don't show the "control" screen if we intentionally // reconnected the USB port because we are changing from bootloader // to programmer mode or vice-versa. if (curState == BootloaderStateAwaitingPlug) { openPort(); curState = nextState; sendByte(nextSendByte); // Special case: Send out notification we are starting an erase command. // I don't have any hooks into the process between now and the erase reply. if (nextSendByte == EraseChips) { emit writeStatusChanged(WriteErasing); } } else if (curState == BootloaderStateAwaitingPlugToBootloader) { openPort(); curState = nextState; sendByte(nextSendByte); } else { emit programmerBoardConnected(); } } void Programmer::portRemoved(const QextPortInfo &info) { if ((info.vendorID == PROGRAMMER_USB_VENDOR_ID) && (info.productID == PROGRAMMER_USB_DEVICE_ID) && (foundState == ProgrammerBoardFound)) { programmerBoardPortName = ""; foundState = ProgrammerBoardNotFound; // Don't show the "no programmer connected" screen if we intentionally // disconnected the USB port because we are changing from bootloader // to programmer mode or vice-versa. if (curState == BootloaderStateAwaitingUnplug) { curState = BootloaderStateAwaitingPlug; } else if (curState == BootloaderStateAwaitingUnplugToBootloader) { curState = BootloaderStateAwaitingPlugToBootloader; } else { closePort(); if (curState != WaitingForNextCommand) { // This means they unplugged while we were in the middle // of an operation. Reset state, and let them know. curState = WaitingForNextCommand; emit programmerBoardDisconnectedDuringOperation(); } else { emit programmerBoardDisconnected(); } } } } void Programmer::startCheckingPorts() { QextSerialEnumerator *p = new QextSerialEnumerator(); connect(p, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(portDiscovered(QextPortInfo))); connect(p, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(portRemoved(QextPortInfo))); p->setUpNotifications(); } void Programmer::openPort() { serialPort->open(QextSerialPort::ReadWrite); } void Programmer::closePort() { serialPort->close(); } void Programmer::setSIMMCapacity(uint32_t bytes) { _simmCapacity = bytes; } uint32_t Programmer::SIMMCapacity() { return _simmCapacity; }