// _progressDialog->setError("Unexpected data after preset write"); // _machine->postEvent(new DataXfer_NACKEvent()); // } // } //} //------------------------------------------------------------------------- void DcXferMachine::xferTimeout() { _watchdog.stop(); if( _progressDialog->cancled() ) { DCLOG() << (_isWriteMachine ? "Write Preset" : "Read Preset") << " cancled"; _machine->postEvent( new DataXfer_CancledEvent() ); } else { DCLOG() << (_isWriteMachine ? "Write Preset" : "Read Preset") << " Transfer Timeout"; if( --_retryCount < 0 ) { DCLOG() << "No more retries, notify user"; _progressDialog->setError( "Unable to communicate with the device." ); _machine->postEvent( new DataXfer_TimeoutEvent() ); } else { QThread::msleep( 100 ); if( !_midiOut->isSafeMode() ) { DCLOG() << "Throttling back MIDI out data rate"; _midiOut->setSafeMode(); _progressDialog->setIoHealth( 1 ); } _midiOut->dataOutThrottled( _activeCmd ); // Restart watchdog _watchdog.start( _timeout ); } } }
//------------------------------------------------------------------------- bool DcBootControl::activateBank( int bankNumber ) { bool rtval = false; // Parameter Checking if(bankNumber != 0 && bankNumber != 1) { DCLOG() << "Invalid bank number specified"; return false; } // Verify system state if(!isBootcode()) { DCLOG() << "not in boot code, can't activate a bank"; return false; } // Do it if(0 == bankNumber) { QRtAutoTrigger mtrigger(DCBC_ACTIVATE_BANK0_SUCCESS,_pMidiIn); _pMidiOut->dataOut(DCBC_ACTIVATE_BANK0); if(mtrigger.wait(1000)) { mtrigger.setPattern(DCBC_DEACTIVATE_BANK1_SUCCESS); _pMidiOut->dataOut(DCBC_DEACTIVATE_BANK1); if(mtrigger.wait(1000)) { rtval = true; } } } else { QRtAutoTrigger mtrigger(DCBC_ACTIVATE_BANK1_SUCCESS,_pMidiIn); _pMidiOut->dataOut(DCBC_ACTIVATE_BANK1); if(mtrigger.wait(1000)) { mtrigger.setPattern(DCBC_DEACTIVATE_BANK0_SUCCESS); _pMidiOut->dataOut(DCBC_DEACTIVATE_BANK0); if(mtrigger.wait(1000)) { rtval = true; } } } return rtval; }
bool DcBootControl::identify(DcMidiDevIdent* id /*=0 */) { bool rtval = false; DcAutoTrigger autotc(_blindMode ? "F0 7E .. 06" : "F0 7E .. 06 02 00 01 55",_pMidiIn ); _pMidiOut->dataOut( "F0 7E 7F 06 01 F7" ); // Wait for the response data, or timeout after 300ms if( autotc.wait( 3000 ) ) { if( id ) { DcMidiData md; if( autotc.dequeue( md ) ) { if( _blindMode && _pDevDetails && !_pDevDetails->isEmpty()) { DcMidiDevIdent* dcid = static_cast<DcMidiDevIdent*>(_pDevDetails); if( dcid ) { *id = *dcid; DCLOG() << "BootControl 'blind mode' Id Result: " << id->toString() << "\n"; rtval = true; } else { DCLOG() << "BootControl 'blind mode' FAIL\n"; } } else { id->fromIdentData( md ); DCLOG() << "BootControl Id Result: " << id->toString() << "\n"; rtval = true; } } } else { rtval = true; } } else { _lastErrorMsg << "Timeout waiting for identity response in BootControl"; } return rtval; }
bool DcBootControl::writeFirmwareUpdateMsg(QRtMidiData& msg,int timeOutMs /*= 2000*/) { bool rtval = false; QRtAutoTrigger autotc(kFUResponcePattern,_pMidiIn); // Magic number 8 is the response control flags, a 3 will // deliver status. msg[8] = 0x03; _pMidiOut->dataOutSplit(msg,_maxDataOut,_delayBetweenDataOut); QRtMidiData md; // Wait for the response data, or timeout after 300ms if(autotc.wait(timeOutMs)) { if(autotc.dequeue(md)) { if(md == kFUGood) { rtval = true; } else if(md == kFUBad) { DCLOG() << "kFUBad"; _lastErrorMsg << "Device reject firmware command - BAD packet."; } else if(md == kFUFailed) { DCLOG() << "kFUFailed"; _lastErrorMsg << "Device failed firmware command."; } else { DCLOG() << "Unknown response: " << md.toString(' ') << "\n"; _lastErrorMsg << "Firmware write generated an unknown response from the device."; } } } else { DCLOG() << "Timeout waiting on " << msg.toString(' ') << "\n"; _lastErrorMsg << "Firmware update failure - timeout after write command.\n" << msg.toString(' ').mid(15,38); } return rtval; }
//------------------------------------------------------------------------- bool DcBootControl::identify(QRtMidiDevIdent* id /*=0 */) { bool rtval = false; QRtAutoTrigger autotc("F0 7E .. 06 02 00 01 55",_pMidiIn); _pMidiOut->dataOut("F0 7E 7F 06 01 F7"); // Wait for the response data, or timeout after 300ms if(autotc.wait(3000)) { if(id) { QRtMidiData md; if(autotc.dequeue(md)) { id->fromIdentData(md); DCLOG() << id->toString() << "\n"; rtval = true; } } else { rtval = true; } } else { _lastErrorMsg << "Timeout waiting for identity response"; } return rtval; }
//------------------------------------------------------------------------- bool DcXferMachine::verifyPresetData( const DcMidiData &data, IoProgressDialog* progDialog, const DcDeviceDetails* devinfo ) { // Verify Data transfer: bool rtval = false; // Verify: data must end with a EOX (F7) if( data.at(data.length()-1) != 0xF7 ) { // Incomplete data response DCLOG() << "Data transfer IN - incomplete preset received, never say EOX"; progDialog->setError("Received incomplete preset data"); } else { if( data.length() != devinfo->PresetSize ) { DCLOG() << "Data transfer IN - packet size mismatch: expected " << devinfo->PresetSize << " got " << data.length(); DCLOG() << "Bad Packet: " << data.toString(); if(devinfo->PresetSize > data.length()) { progDialog->setError("The preset data received is corrupt and too small"); } else { progDialog->setError("The preset data received is too large"); } } else { // Verify the checksum int sum = data.sumOfSection(devinfo->PresetStartOfDataOffset,devinfo->PresetDataLength); int dataVal = data.at(devinfo->PresetChkSumOffset); if(sum != dataVal) { DCLOG() << "The preset data received is corrupt, checksum Error."; DCLOG() << data.toString(); } else { rtval = true; } } } return rtval; }
//------------------------------------------------------------------------- QRtMidiData DcBootControl::makePrivateResetCmd() { QRtMidiDevIdent id; QRtMidiData priRst; // Verify we can ID the connected device if(false == identify(&id)) { DCLOG() << "No response from identity request"; } else { // Build private reset command using device product ID. priRst.setData(kPrivateResetPartial,id.getFamilyByte(),id.getProductByte()); } return priRst; }
//------------------------------------------------------------------------- void DcXferMachine::replySlotForDataIn( const DcMidiData &data ) { // Expect to see sysex coming from the currently attached device. // This check will prevent other legal messages from blowing up // the transfer - like controller messages, etc... if(data.contains(_devDetails->SOXHdr)) { if(data == _activeCmd) { // If the data coming back is the same as what was sent, // it was echoed back to us, just ignore as it was probably due // to a device with MIDI "soft" THRU (midi-merge) return; } // cancel the watchdog timer _watchdog.stop(); // Check for a Negative Acknowledgment of the data in request if( data.match(_devDetails->PresetRd_NAK,true) ) { DCLOG() << "Preset read NAK detected, notify user and bail"; _progressDialog->setError("Device Rejected Command"); _machine->postEvent(new DataXfer_NACKEvent()); } else if( data.match(_devDetails->PresetRd_ACK) ) { DcMidiData recompinded; if(gUseAltPresetSize) { int chnksz = data.get14bit(9+2); recompinded = data.mid(0,9); recompinded[6] = 0x62; recompinded.append(DcMidiData(data.mid(9+6,chnksz))); recompinded.append(DcMidiData("18191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132337F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F")); recompinded.append(DcMidiData(data.mid(9+6+chnksz))); } else { recompinded = data; } if( verifyPresetData(recompinded,_progressDialog,_devDetails) == true ) { _progressDialog->inc(); _midiDataList.append(recompinded); _machine->postEvent(new DataXfer_ACKEvent()); } else { _machine->postEvent(new DataXfer_NACKEvent()); } } else { DCLOG() << "Unexpected data received after preset read"; DCLOG() << data.toString(); _progressDialog->setError("Unexpected data received after requesting the preset."); _machine->postEvent(new DataXfer_NACKEvent()); } } }
//------------------------------------------------------------------------- void DcXferMachine::replySlotForDataOut( const DcMidiData &data ) { // Expect to see sysex coming from the currently attached device. // This check will prevent other legal messages from blowing up // out preset transfer - like controller messages if(data.contains(_devDetails->SOXHdr)) { bool NAK = data.match(_devDetails->PresetWr_NAK); bool ACK = data.match(_devDetails->PresetWr_ACK); // If it's not an ACK or NAK, then just ignore the response data if(!(NAK || ACK)) { // Check for a strymon MIDITH if(data == _activeCmd) { return; } // This is an unexpected Sysex message, it has a valid strymon HDR, // but the payload was unexpected. DCLOG() << "Ignoring unexpected/invalid Strymon Sysex message"; DCLOG() << " Sent: " << _activeCmd.toString(); DCLOG() << "Received: " << data.toString(); return; } // This is the response we were looking for, cancel the transfer timeout watchdog _watchdog.stop(); // Check for write preset Negative Acknowledgment if(NAK) { if(--_retryCount < 0) { DCLOG() << "NAK - retries exhausted - notify user and bail"; _progressDialog->setError("Device Rejected Write Command"); _machine->postEvent(new DataXfer_NACKEvent()); } else { // Retry the Write Command QThread::msleep(100); // Will this be the last try? Also, if midiOut is not in 'safe mode' // throttle back the output rate to work around troubled MIDI host adapters. if(!_midiOut->isSafeMode()) { DCLOG() << "Throttling back MIDI output rate"; _midiOut->setSafeMode(); _progressDialog->setIoHealth( 1 ); } DCLOG() << "NAK - retry count at " << _retryCount; _midiOut->dataOutThrottled(_activeCmd); // Restart watchdog _watchdog.start(_timeout); } } else if(ACK) { _progressDialog->inc(); _progressDialog->setError(""); QApplication::processEvents(); if( _retryCount < _numRetries ) { DCLOG() << QString("Success after %1 retries").arg(_numRetries - _retryCount); } _writeSuccessList.append(_activeCmd); _machine->postEvent(new DataXfer_ACKEvent()); } else { // Getting here means an unexpected message was received. DCLOG() << "Unexpected data received after preset write"; DCLOG() << " Sent: " << _activeCmd.toString(); DCLOG() << "Received: " << data.toString(); _progressDialog->setError("Unexpected data after preset write"); _machine->postEvent(new DataXfer_NACKEvent()); } } }
//------------------------------------------------------------------------- bool DcBootControl::enableBootcode( ) { QRtMidiData md; QRtMidiData responceData; // The device is in boot mode if it responses to the Echo command. if(isBootcode()) { return true; } QRtMidiData priRst = makePrivateResetCmd(); if(0 == priRst.length()) return false; QRtMidiTrigger tc(RESPONCE_ENABLE_RECOVERY_ANY); QRtAutoTrigger autoch(&tc,_pMidiIn); // Issue a private reset _pMidiOut->dataOut(priRst); QThread::msleep(100); // Issue "enable recovery" no more than 300ms after reset to keep the device in boot code. for (int idx = 0; idx < 40 ; idx++) { _pMidiOut->dataOut(CMD_ENABLE_RECOVERY); QThread::msleep(20); if(tc.dequeue(responceData)) { break; } } bool rtval = false; if(responceData.match(RESPONCE_ENABLE_RECOVERY_ACK)) { // Verify device is in boot code if(!isBootcode()) { DCLOG() << "Failed to verify device is in boot code"; } else { DCLOG() << "Device is running boot code"; rtval = true; } } else if(responceData.match(RESPONCE_ENABLE_RECOVERY_REJECTED)) { DCLOG() << "The device has rejected the enable recovery command"; } else if(responceData.match(RESPONCE_ENABLE_RECOVERY_FAILED)) { DCLOG() << "Device has failed the enabled recovery command"; } else { DCLOG() << "Timeout entering boot code"; } return rtval; }
bool DcBootControl::enableBootcode( ) { DcMidiData md; DcMidiData responceData; // The device is in boot mode if it responses to the Echo command. if(isBootcode()) { return true; } DcMidiData priRst = makePrivateResetCmd(); if(0 == priRst.length()) { return false; } DcMidiTrigger tc( _blindMode ? "F0 00 01 55" : RESPONCE_ENABLE_RECOVERY_ANY ); DcAutoTrigger autoch(&tc,_pMidiIn); if( _blindMode ) { DCLOG() << "Attempting to enable boot code in blind mode"; } // Issue a private reset _pMidiOut->dataOut(priRst); QThread::msleep(100); // Issue "enable recovery" no more than 300ms after reset to keep the device in boot code. for (int idx = 0; idx < 40 ; idx++) { _pMidiOut->dataOut(CMD_ENABLE_RECOVERY); QThread::msleep(20); if(tc.dequeue(responceData)) { break; } } bool rtval = false; if(_blindMode ) { DCLOG() << "Device is running 'blind mode' boot code"; rtval = true; } else { if( responceData.match( RESPONCE_ENABLE_RECOVERY_ACK ) ) { // Verify device is in boot code if( !isBootcode() ) { DCLOG() << "Failed to verify device is in boot code"; // Just send a reset in case status was lost _pMidiOut->dataOut( "F0 00 01 55 42 01 F7" ); } else { DCLOG() << "Device is running boot code"; rtval = true; } } else if( responceData.match( RESPONCE_ENABLE_RECOVERY_REJECTED ) ) { DCLOG() << "The device has rejected the enable recovery command"; } else if( responceData.match( RESPONCE_ENABLE_RECOVERY_FAILED ) ) { DCLOG() << "Device has failed the enabled recovery command"; } else { // Never saw the requested responce from the device // Who knows what's going on now - incase the device is in boot-mode // Send a reset command. _pMidiOut->dataOut( "F0 00 01 55 42 01 F7" ); DCLOG() << "Timeout entering boot code"; } } return rtval; }
bool DcBootControl::writeFirmwareUpdateMsg(DcMidiData& msg,int timeOutMs /*= 2000*/) { bool rtval = false; DcAutoTrigger autotc( _blindMode ? "F0 00 01 55" : kFUResponcePattern ,_pMidiIn ); // Magic number 8 is the response control flags, a 3 will deliver status. msg[8] = 0x03; // if(_blindMode ) // { // DCLOG() << "SEND: " << msg.toString(' '); // } _pMidiOut->dataOut(msg); DcMidiData md; // Wait for the response data, or timeout after 300ms if(autotc.wait(timeOutMs)) { if(autotc.dequeue(md)) { if(_blindMode ) { if(md.match("F0 00 01 55 42 00") ) { md = kFUGood; } else if(md.match("F0 00 01 55 42 01") ) { DCLOG() << "RECVD: " << md.toString(' '); md = kFUBad; } else if(md.match("F0 00 01 55 42 02")) { DCLOG() << "RECVD: " << md.toString(' '); md = kFUFailed; } } if( md == kFUGood ) { rtval = true; } else if( md == kFUBad ) { DCLOG() << "kFUBad"; _lastErrorMsg << "Device reject firmware command - BAD packet."; } else if( md == kFUFailed ) { DCLOG() << "kFUFailed"; _lastErrorMsg << "Device failed firmware command."; } else { DCLOG() << "Unknown response: " << md.toString( ' ' ) << "\n"; _lastErrorMsg << "Firmware write generated an unknown response from the device."; } } } else { DCLOG() << "Timeout waiting on " << msg.toString(' ') << "\n"; _lastErrorMsg << "Firmware update failure - timeout after write command.\n" << msg.toString(' ').mid(15,38); } return rtval; }