diff --git a/Desktop_Interface/genericusbdriver.cpp b/Desktop_Interface/genericusbdriver.cpp index 2afc7ecb..f76665ed 100644 --- a/Desktop_Interface/genericusbdriver.cpp +++ b/Desktop_Interface/genericusbdriver.cpp @@ -69,7 +69,8 @@ genericUsbDriver::genericUsbDriver(QWidget *parent) : QLabel(parent) //qDebug() << k; //qDebug() << NUM_ISO_ENDPOINTS; pipeID[k] = 0x81+k; - qDebug() << "pipeID" << k << "=" << pipeID[k]; + + qDebug() << "pipeID" << k << "=" << static_cast(pipeID[k]); } connectTimer = new QTimer(); @@ -376,7 +377,7 @@ void genericUsbDriver::setGain(double newGain){ */ qDebug("newGain = %f", newGain); qDebug("gainMask = %x", gainMask); - usbSendControl(0x40, 0xa5, (deviceMode == 5 ? 0 : 5), gainMask, 0, NULL); + usbSendControl(0x40, 0xa5, deviceMode, gainMask, 0, NULL); } void genericUsbDriver::avrDebug(void){ diff --git a/Desktop_Interface/genericusbdriver.h b/Desktop_Interface/genericusbdriver.h index 95dcc7a5..17f2b059 100644 --- a/Desktop_Interface/genericusbdriver.h +++ b/Desktop_Interface/genericusbdriver.h @@ -93,7 +93,11 @@ class genericUsbDriver : public QLabel //Generic Vars //bufferControl *bufferPtr = NULL; QTimer *psuTimer = nullptr; +#ifdef PLATFORM_WINDOWS + unsigned char pipeID[NUM_ISO_ENDPOINTS]; +#else unsigned char pipeID[3]; +#endif QTimer *isoTimer = nullptr; QTimer *connectTimer = nullptr; QTimer *recoveryTimer; diff --git a/Desktop_Interface/mainwindow.cpp b/Desktop_Interface/mainwindow.cpp index f101b134..4b62895c 100644 --- a/Desktop_Interface/mainwindow.cpp +++ b/Desktop_Interface/mainwindow.cpp @@ -13,7 +13,6 @@ #endif #include -#include #define DO_QUOTE(X) #X #define QUOTE(X) DO_QUOTE(X) @@ -166,10 +165,6 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->debugButton1, SIGNAL(clicked()), ui->controller_iso->driver, SLOT(avrDebug())); connect(ui->psuSlider, SIGNAL(voltageChanged(double)), ui->controller_iso->driver, SLOT(setPsu(double))); connect(ui->controller_iso, SIGNAL(setGain(double)), ui->controller_iso->driver, SLOT(setGain(double))); - connect(ui->controller_iso, &isoDriver::setGain, this, [this](){ - // Force a trigger refresh when gain changes (issue #233) - ui->controller_iso->setTriggerLevel(ui->triggerLevelValue->value()); - }); connect(ui->controller_fg, &functionGenControl::functionGenToUpdate, ui->controller_iso->driver, &genericUsbDriver::setFunctionGen); connect(ui->bufferDisplay, SIGNAL(modeChange(int)), ui->controller_iso->driver, SLOT(setDeviceMode(int))); connect(ui->bufferDisplay, &bufferControl::modeChange, this, [this](){ @@ -405,26 +400,26 @@ MainWindow::MainWindow(QWidget *parent) : void MainWindow::closeEvent(QCloseEvent *event) { - writeSettings("ScopeTopRange", ui->controller_iso->display->topRange); - writeSettings("ScopeBotRange", ui->controller_iso->display->botRange); - writeSettings("ScopeTimeWindow", ui->controller_iso->display->window); - writeSettings("ScopeDelay", ui->controller_iso->display->delay); + settings->setValue("ScopeTopRange", ui->controller_iso->display->topRange); + settings->setValue("ScopeBotRange", ui->controller_iso->display->botRange); + settings->setValue("ScopeTimeWindow", ui->controller_iso->display->window); + settings->setValue("ScopeDelay", ui->controller_iso->display->delay); #ifndef PLATFORM_ANDROID - writeSettings("ScopeOffsetCH1", ui->offsetSpinBox_CH1->value()); - writeSettings("ScopeOffsetCH2", ui->offsetSpinBox_CH2->value()); - writeSettings("LAOffsetCH1", ui->laOffsetSpinBox_CH1->value()); - writeSettings("LAOffsetCH2", ui->laOffsetSpinBox_CH2->value()); - writeSettings("WidgetScopeGroup_CH1", ui->scopeGroup_CH1->isChecked()); - writeSettings("WidgetScopeGroup_CH2", ui->scopeGroup_CH2->isChecked()); - writeSettings("WidgetMultimeterGroup", ui->multimeterGroup->isChecked()); - writeSettings("WidgetBusSnifferGroup_CH1", ui->busSnifferGroup_CH1->isChecked()); - writeSettings("WidgetBusSnifferGroup_CH2", ui->busSnifferGroup_CH2->isChecked()); - writeSettings("WidgetDoubleSample", ui->doubleSampleLabel->isChecked()); - writeSettings("HideOscilloscope", ui->actionHide_Widget_Oscilloscope->isChecked()); - writeSettings("HideSignalGen", ui->actionHide_Widget_SignalGen->isChecked()); - writeSettings("HideMultimeter", ui->actionHide_Widget_Multimeter->isChecked()); - writeSettings("HidePowerSupply", ui->actionHide_Widget_PowerSupply->isChecked()); - writeSettings("HideLogicAnalyzer", ui->actionHide_Widget_LogicAnalyzer->isChecked()); + settings->setValue("ScopeOffsetCH1", ui->offsetSpinBox_CH1->value()); + settings->setValue("ScopeOffsetCH2", ui->offsetSpinBox_CH2->value()); + settings->setValue("LAOffsetCH1", ui->laOffsetSpinBox_CH1->value()); + settings->setValue("LAOffsetCH2", ui->laOffsetSpinBox_CH2->value()); + settings->setValue("WidgetScopeGroup_CH1", ui->scopeGroup_CH1->isChecked()); + settings->setValue("WidgetScopeGroup_CH2", ui->scopeGroup_CH2->isChecked()); + settings->setValue("WidgetMultimeterGroup", ui->multimeterGroup->isChecked()); + settings->setValue("WidgetBusSnifferGroup_CH1", ui->busSnifferGroup_CH1->isChecked()); + settings->setValue("WidgetBusSnifferGroup_CH2", ui->busSnifferGroup_CH2->isChecked()); + settings->setValue("WidgetDoubleSample", ui->doubleSampleLabel->isChecked()); + settings->setValue("HideOscilloscope", ui->actionHide_Widget_Oscilloscope->isChecked()); + settings->setValue("HideSignalGen", ui->actionHide_Widget_SignalGen->isChecked()); + settings->setValue("HideMultimeter", ui->actionHide_Widget_Multimeter->isChecked()); + settings->setValue("HidePowerSupply", ui->actionHide_Widget_PowerSupply->isChecked()); + settings->setValue("HideLogicAnalyzer", ui->actionHide_Widget_LogicAnalyzer->isChecked()); #endif QMainWindow::closeEvent(event); } @@ -1486,36 +1481,23 @@ void MainWindow::on_actionEnter_Manually_triggered() dialog.exec(); } -#ifdef PLATFORM_WINDOWS -#define MAKE_SETTINGS QSettings settings( \ - QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/labrador.ini", \ - QSettings::IniFormat) -#else -#define MAKE_SETTINGS QSettings settings -#endif - -void MainWindow::writeSettings(const QString &key, const QVariant &value){ - MAKE_SETTINGS; - settings.setValue(key, value); -} - #define QSETTINGS_DEFAULT_RETURN 42069 void MainWindow::readSettingsFile(){ - MAKE_SETTINGS; - int connectionType = settings.value("ConnectionType", QSETTINGS_DEFAULT_RETURN).toInt(); - double calibrate_vref_ch1 = settings.value("CalibrateVrefCH1", 1.65).toDouble(); - double calibrate_vref_ch2 = settings.value("CalibrateVrefCH2", 1.65).toDouble(); - double calibrate_gain_ch1 = settings.value("CalibrateGainCH1", R4/(R3+R4)).toDouble(); - double calibrate_gain_ch2 = settings.value("CalibrateGainCH2", R4/(R3+R4)).toDouble(); - psu_voltage_calibration_offset = settings.value("CalibratePsu", 0).toDouble(); - - daq_num_to_average = settings.value("daq_defaultAverage", 1).toInt(); - daq_max_file_size = settings.value("daq_defaultFileSize", 2048000000).toULongLong(); - - double savedTopRange = settings.value("ScopeTopRange", 2.5).toDouble(); - double savedBotRange = settings.value("ScopeBotRange", -0.5).toDouble(); - double savedTimeWindow = settings.value("ScopeTimeWindow", 0.1).toDouble(); - double savedDelay = settings.value("ScopeDelay", 0.0).toDouble(); + settings = new QSettings(); + int connectionType = settings->value("ConnectionType", QSETTINGS_DEFAULT_RETURN).toInt(); + double calibrate_vref_ch1 = settings->value("CalibrateVrefCH1", 1.65).toDouble(); + double calibrate_vref_ch2 = settings->value("CalibrateVrefCH2", 1.65).toDouble(); + double calibrate_gain_ch1 = settings->value("CalibrateGainCH1", R4/(R3+R4)).toDouble(); + double calibrate_gain_ch2 = settings->value("CalibrateGainCH2", R4/(R3+R4)).toDouble(); + psu_voltage_calibration_offset = settings->value("CalibratePsu", 0).toDouble(); + + daq_num_to_average = settings->value("daq_defaultAverage", 1).toInt(); + daq_max_file_size = settings->value("daq_defaultFileSize", 2048000000).toULongLong(); + + double savedTopRange = settings->value("ScopeTopRange", 2.5).toDouble(); + double savedBotRange = settings->value("ScopeBotRange", -0.5).toDouble(); + double savedTimeWindow = settings->value("ScopeTimeWindow", 0.1).toDouble(); + double savedDelay = settings->value("ScopeDelay", 0.0).toDouble(); bool voltageRangeValid = savedTopRange > savedBotRange && savedTopRange >= -20.0 && savedTopRange <= 20.0 @@ -1530,61 +1512,61 @@ void MainWindow::readSettingsFile(){ ui->controller_iso->display->delay = savedDelay; #ifndef PLATFORM_ANDROID - double savedOffsetCH1 = settings.value("ScopeOffsetCH1", 0.0).toDouble(); - double savedOffsetCH2 = settings.value("ScopeOffsetCH2", 0.0).toDouble(); + double savedOffsetCH1 = settings->value("ScopeOffsetCH1", 0.0).toDouble(); + double savedOffsetCH2 = settings->value("ScopeOffsetCH2", 0.0).toDouble(); if (savedOffsetCH1 >= -20.0 && savedOffsetCH1 <= 20.0) ui->offsetSpinBox_CH1->setValue(savedOffsetCH1); if (savedOffsetCH2 >= -20.0 && savedOffsetCH2 <= 20.0) ui->offsetSpinBox_CH2->setValue(savedOffsetCH2); - double savedLAOffsetCH1 = settings.value("LAOffsetCH1", 0.0).toDouble(); - double savedLAOffsetCH2 = settings.value("LAOffsetCH2", 0.0).toDouble(); + double savedLAOffsetCH1 = settings->value("LAOffsetCH1", 0.0).toDouble(); + double savedLAOffsetCH2 = settings->value("LAOffsetCH2", 0.0).toDouble(); if (savedLAOffsetCH1 >= -20.0 && savedLAOffsetCH1 <= 20.0) ui->laOffsetSpinBox_CH1->setValue(savedLAOffsetCH1); if (savedLAOffsetCH2 >= -20.0 && savedLAOffsetCH2 <= 20.0) ui->laOffsetSpinBox_CH2->setValue(savedLAOffsetCH2); - if (settings.value("ShowRangeDialog").toBool()) + if (settings->value("ShowRangeDialog").toBool()) { qDebug() << "ShowRangeDialog setting true"; ui->actionShow_Range_Dialog_on_Main_Page->setChecked(true); on_actionShow_Range_Dialog_on_Main_Page_triggered(true); } - if(settings.value("DarkModeEnabled").toBool()) + if(settings->value("DarkModeEnabled").toBool()) { ui->actionDark_Mode->setChecked(true); setDarkMode(true); } - ui->scopeGroup_CH1->setChecked(settings.value("WidgetScopeGroup_CH1", true).toBool()); - ui->scopeGroup_CH2->setChecked(settings.value("WidgetScopeGroup_CH2", false).toBool()); - ui->multimeterGroup->setChecked(settings.value("WidgetMultimeterGroup", false).toBool()); - ui->busSnifferGroup_CH1->setChecked(settings.value("WidgetBusSnifferGroup_CH1", false).toBool()); - ui->busSnifferGroup_CH2->setChecked(settings.value("WidgetBusSnifferGroup_CH2", false).toBool()); - ui->doubleSampleLabel->setChecked(settings.value("WidgetDoubleSample", false).toBool()); + ui->scopeGroup_CH1->setChecked(settings->value("WidgetScopeGroup_CH1", true).toBool()); + ui->scopeGroup_CH2->setChecked(settings->value("WidgetScopeGroup_CH2", false).toBool()); + ui->multimeterGroup->setChecked(settings->value("WidgetMultimeterGroup", false).toBool()); + ui->busSnifferGroup_CH1->setChecked(settings->value("WidgetBusSnifferGroup_CH1", false).toBool()); + ui->busSnifferGroup_CH2->setChecked(settings->value("WidgetBusSnifferGroup_CH2", false).toBool()); + ui->doubleSampleLabel->setChecked(settings->value("WidgetDoubleSample", false).toBool()); - if (settings.value("HideOscilloscope").toBool()) + if (settings->value("HideOscilloscope").toBool()) { ui->actionHide_Widget_Oscilloscope->setChecked(true); on_actionHide_Widget_Oscilloscope_triggered(true); } - if (settings.value("HideSignalGen").toBool()) + if (settings->value("HideSignalGen").toBool()) { ui->actionHide_Widget_SignalGen->setChecked(true); on_actionHide_Widget_SignalGen_triggered(true); } - if (settings.value("HideMultimeter").toBool()) + if (settings->value("HideMultimeter").toBool()) { ui->actionHide_Widget_Multimeter->setChecked(true); on_actionHide_Widget_Multimeter_triggered(true); } - if (settings.value("HidePowerSupply").toBool()) + if (settings->value("HidePowerSupply").toBool()) { ui->actionHide_Widget_PowerSupply->setChecked(true); on_actionHide_Widget_PowerSupply_triggered(true); } - if (settings.value("HideLogicAnalyzer").toBool()) + if (settings->value("HideLogicAnalyzer").toBool()) { ui->actionHide_Widget_LogicAnalyzer->setChecked(true); on_actionHide_Widget_LogicAnalyzer_triggered(true); @@ -1637,7 +1619,13 @@ void MainWindow::reinitUsb(void){ ui->controller_iso->driver->saveState(&reinitdeviceMode, &reinitScopeGain, &reinitCurrentPsuVoltage, &reinitDigitalPinState); #ifdef PLATFORM_WINDOWS - reinitUsbStage2(); + if(!(ui->controller_iso->driver->connected)){ + reinitUsbStage2(); + } else{ + connect(ui->controller_iso->driver, SIGNAL(shutdownComplete()), + this, SLOT(reinitUsbStage2()), Qt::UniqueConnection); + ui->controller_iso->driver->shutdownProcedure(); + } #else if(!(ui->controller_iso->driver->connected)){ reinitUsbStage2(); @@ -1668,10 +1656,6 @@ void MainWindow::reinitUsbStage2(void){ connect(ui->debugButton3, SIGNAL(clicked()), ui->controller_iso->driver, SLOT(bootloaderJump())); connect(ui->psuSlider, SIGNAL(voltageChanged(double)), ui->controller_iso->driver, SLOT(setPsu(double))); connect(ui->controller_iso, SIGNAL(setGain(double)), ui->controller_iso->driver, SLOT(setGain(double))); - connect(ui->controller_iso, &isoDriver::setGain, this, [this](){ - // Force a trigger refresh when gain changes (issue #233) - ui->controller_iso->setTriggerLevel(ui->triggerLevelValue->value()); - }); connect(ui->controller_fg, &functionGenControl::functionGenToUpdate, ui->controller_iso->driver, &genericUsbDriver::setFunctionGen); connect(ui->bufferDisplay, SIGNAL(modeChange(int)), ui->controller_iso->driver, SLOT(setDeviceMode(int))); connect(ui->bufferDisplay, &bufferControl::modeChange, this, [this](){ @@ -2078,10 +2062,10 @@ void MainWindow::on_actionCalibrate_triggered() ui->controller_iso->internalBuffer750->m_frontendGain = R4/(R3+R4); ui->controller_iso->internalBuffer375_CH2->m_frontendGain = R4/(R3+R4); - writeSettings("CalibrateVrefCH1", 1.65); - writeSettings("CalibrateVrefCH2", 1.65); - writeSettings("CalibrateGainCH1", R4/(R3+R4)); - writeSettings("CalibrateGainCH2", R4/(R3+R4)); + settings->setValue("CalibrateVrefCH1", 1.65); + settings->setValue("CalibrateVrefCH2", 1.65); + settings->setValue("CalibrateGainCH1", R4/(R3+R4)); + settings->setValue("CalibrateGainCH2", R4/(R3+R4)); qDebug() << "Calibration routine beginning!"; calibrationMessages = new QMessageBox(); @@ -2120,8 +2104,8 @@ void MainWindow::calibrateStage2(){ ui->controller_iso->internalBuffer750->m_voltage_ref = 3.3 - vref_CH1; ui->controller_iso->internalBuffer375_CH2->m_voltage_ref = 3.3 - vref_CH2; - writeSettings("CalibrateVrefCH1", vref_CH1); - writeSettings("CalibrateVrefCH2", vref_CH2); + settings->setValue("CalibrateVrefCH1", vref_CH1); + settings->setValue("CalibrateVrefCH2", vref_CH2); calibrationMessages = new QMessageBox(); calibrationMessages->setAttribute(Qt::WA_DeleteOnClose); @@ -2165,8 +2149,8 @@ void MainWindow::calibrateStage3(){ ui->controller_iso->internalBuffer375_CH1->m_frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; ui->controller_iso->internalBuffer750->m_frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; ui->controller_iso->internalBuffer375_CH2->m_frontendGain = (vref_CH2 - vMeasured_CH2)*(ui->controller_iso->frontendGain_CH2)/vref_CH2; - writeSettings("CalibrateGainCH1", ui->controller_iso->frontendGain_CH1); - writeSettings("CalibrateGainCH2", ui->controller_iso->frontendGain_CH2); + settings->setValue("CalibrateGainCH1", ui->controller_iso->frontendGain_CH1); + settings->setValue("CalibrateGainCH2", ui->controller_iso->frontendGain_CH2); calibrationMessages = new QMessageBox(); calibrationMessages->setAttribute(Qt::WA_DeleteOnClose); connect(ui->controller_iso->driver, SIGNAL(killMe()), calibrationMessages, SLOT(reject())); @@ -2235,21 +2219,21 @@ void MainWindow::multimeterStateChange(bool enabled){ void MainWindow::on_actionLo_bw_triggered() { expected_variant = 1; - writeSettings("ConnectionType", 0); + settings->setValue("ConnectionType", 0); if(ui->controller_iso->driver->connected) reinitUsb(); } void MainWindow::on_actionSingle_ep_msync_triggered() { expected_variant = 2; - writeSettings("ConnectionType", 1); + settings->setValue("ConnectionType", 1); if(ui->controller_iso->driver->connected) reinitUsb(); } void MainWindow::on_actionSingle_ep_async_triggered() { expected_variant = 2; - writeSettings("ConnectionType", 2); + settings->setValue("ConnectionType", 2); if(ui->controller_iso->driver->connected) reinitUsb(); } @@ -2503,8 +2487,8 @@ void MainWindow::daq_updatedMaxFileSize(qulonglong newVal){ void MainWindow::daq_saveButtonPressed(){ qDebug() << "MainWindow::daq_saveButtonPressed"; - writeSettings("daq_defaultAverage", daq_num_to_average); - writeSettings("daq_defaultFileSize", daq_max_file_size); + settings->setValue("daq_defaultAverage", daq_num_to_average); + settings->setValue("daq_defaultFileSize", daq_max_file_size); } void MainWindow::on_actionAbout_triggered() @@ -2688,7 +2672,7 @@ void MainWindow::calibrate_psu_stage3() } psu_voltage_calibration_offset = ((PSU5 - 5) + (PSU10 - 10)) / 2.0; - writeSettings("CalibratePsu", psu_voltage_calibration_offset); + settings->setValue("CalibratePsu", psu_voltage_calibration_offset); ui->controller_iso->driver->psu_offset = psu_voltage_calibration_offset; calibrationMessages = new QMessageBox(); @@ -2730,7 +2714,7 @@ void MainWindow::on_actionShow_Range_Dialog_on_Main_Page_triggered(bool checked) connect(ui->controller_iso, SIGNAL(delayUpdated(double)), scopeRangeSwitch, SLOT(delayChanged(double))); } qDebug() << "on_actionShow_Range_Dialog_on_Main_Page_triggered" << checked; - writeSettings("ShowRangeDialog", checked); + settings->setValue("ShowRangeDialog", checked); scopeRangeSwitch->setVisible(checked); #endif @@ -2884,7 +2868,8 @@ void MainWindow::setDarkMode(bool dark) qApp->setPalette(defaultPalette); } - writeSettings("DarkModeEnabled", dark); + QSettings settings; + settings.setValue("DarkModeEnabled", dark); } diff --git a/Desktop_Interface/mainwindow.h b/Desktop_Interface/mainwindow.h index 87cb618b..8ab46bb8 100644 --- a/Desktop_Interface/mainwindow.h +++ b/Desktop_Interface/mainwindow.h @@ -254,6 +254,7 @@ private slots: int defaultNumberPrecision = 6; QString defaultNumberFormat; + QSettings *settings; bool calibrationCanceled = false; QPalette defaultPalette; @@ -266,7 +267,6 @@ private slots: void menuSetup(); void initShortcuts(); void readSettingsFile(); - void writeSettings(const QString &key, const QVariant &value); void setDarkMode(bool dark); std::vector uartEncode(const QString& line, UartParity parity); std::vector resampler(std::vector, int input_baudRate, int output_baudRate); diff --git a/Desktop_Interface/winusbdriver.cpp b/Desktop_Interface/winusbdriver.cpp index f4b1af33..b3c840a5 100644 --- a/Desktop_Interface/winusbdriver.cpp +++ b/Desktop_Interface/winusbdriver.cpp @@ -1,498 +1,805 @@ #include "winusbdriver.h" #include -#include +#include #include +#include #define SLEEP_DIVIDER 16 -winUsbDriver::winUsbDriver(QWidget *parent) : genericUsbDriver(parent) +// #define WINUSB_PHASE_DIAGNOSTICS + +#ifndef WINUSB_PHASE_RESYNC_STREAK +#define WINUSB_PHASE_RESYNC_STREAK 2 +#endif + +winUsbDriver::winUsbDriver(QWidget* parent) + : genericUsbDriver(parent) { } -winUsbDriver::~winUsbDriver(void){ - - //Like any decent destructor, this just frees resources - - qDebug() << "\n\nwinUsbDriver destructor ran!"; - for (unsigned char k=0; k 10)) strcpy(subString, "th"); - - //qDebug("\n\nThis is the %d%s Tick!", timerCount, subString); - - bool success; - int n, earliest = MAX_OVERLAP; - unsigned int minFrame = 4294967295; - unsigned int dataBufferOffset; - unsigned int packetLength = 0; - - //Getting earliest transfer number. - for (n=0; nStartFrame < minFrame){ - minFrame = isoCtx[0][n]->StartFrame; - earliest = n; - } - } - } - - //qDebug() << n << "is the earliest!"; - - if (earliest == MAX_OVERLAP){ - return; - } - - //Copy the tranfer data into buffer - for(int i=0;iNumberOfPackets;i++){ - for(unsigned char k=0; kIsoPackets[i].Offset; - memcpy(&(outBuffers[currentWriteBuffer][packetLength]), &dataBuffer[k][earliest][dataBufferOffset], isoCtx[k][earliest]->IsoPackets[i].Length); - packetLength += isoCtx[k][earliest]->IsoPackets[i].Length; - } - } - - //Get the data for isoRead() ready and swap buffers - bufferLengths[currentWriteBuffer] = packetLength; - currentWriteBuffer = !currentWriteBuffer; - - - //Check for incorrect setup and kill if that were the case. - UINT ep0frame = isoCtx[0][earliest]->StartFrame; - UINT epkframe = isoCtx[NUM_ISO_ENDPOINTS-1][earliest]->StartFrame; - UINT framePhaseError = epkframe - ep0frame; - if(framePhaseError){ - #ifndef WINDOWS_32_BIT - qDebug("Frame phase error of %d", framePhaseError); - killMe(); - #endif - } - - - //Setup transfer for resubmission - for(unsigned char k=0; kStartFrame; - success = IsoK_ReUse(isoCtx[k][earliest]); - if(!success){ - DWORD errorCode = GetLastError(); - qDebug() << "IsoK_Init failed with error code" << errorCode; - qDebug() << "n =" << n; - return; - } - isoCtx[k][earliest]->StartFrame = 0; - - success = OvlK_ReUse(ovlkHandle[k][earliest]); - if(!success){ - DWORD errorCode = GetLastError(); - qDebug() << "OvlK_ReUse failed with error code" << errorCode; - qDebug() << "n =" << n; - return; - }*/ - //Resubmit the transfer - success = UsbK_IsoReadPipe(handle, pipeID[k], dataBuffer[k][earliest], sizeof(dataBuffer[k][earliest]), (LPOVERLAPPED) ovlkHandle[k][earliest], isoCtx[k][earliest]); - } - //qDebug() << "Resubmitted Ctx #"<< earliest; - //Signal to isoDriver that it can draw a new frame. - upTick(); - return; +void winUsbDriver::isoTimerTick(void) +{ + // This function is called every ISO_TIMER_PERIOD milliseconds, after usbIsoInit() has + // run. It should check if a transfer is complete, then copy the ___earliest___ + // transfer into the appropriate outBuffer, as well as set appropriate bufferLengths. + // Once this is complete, it should resubmit the transfer that it read the data from. + // Finally, it should signal upTick() so that isoDriver knows it can draw a new frame. + + timerCount++; + + if (shutdownRequested) + { + return; + } + + char subString[3] = "th"; + if (timerCount % 10 == 1) + strcpy(subString, "st"); + if (timerCount % 10 == 2) + strcpy(subString, "nd"); + if (timerCount % 10 == 3) + strcpy(subString, "rd"); + if ((timerCount < 20) && (timerCount > 10)) + strcpy(subString, "th"); + + // qDebug("\n\nThis is the %d%s Tick!", timerCount, subString); + + // bool success; + int n, earliest = MAX_OVERLAP; + unsigned int minFrame = 4294967295; + unsigned int dataBufferOffset; + unsigned int packetLength = 0; + + // Getting earliest transfer number. + for (n = 0; n < NUM_FUTURE_CTX; n++) + { + if (allEndpointsComplete(n)) + { + if (isoCtx[0][n]->StartFrame < minFrame) + { + minFrame = isoCtx[0][n]->StartFrame; + earliest = n; + } + } + } + + // qDebug() << n << "is the earliest!"; + + if (earliest == MAX_OVERLAP) + { + return; + } + UINT ep0frame = isoCtx[0][earliest]->StartFrame; + UINT epkframe = isoCtx[NUM_ISO_ENDPOINTS - 1][earliest]->StartFrame; + UINT framePhaseError = epkframe - ep0frame; + UINT frames[NUM_ISO_ENDPOINTS] = {}; + static unsigned int alignedStreak = 0; + if (framePhaseError == 0) + { + alignedStreak++; + } + else + { + alignedStreak = 0; + } + bool publishAlignedBuffer = (alignedStreak >= WINUSB_PHASE_RESYNC_STREAK); + +#ifdef WINUSB_PHASE_DIAGNOSTICS + // DIAGNOSTIC CODE + static bool lastPatternValid = false; + static bool lastPatternHadError = false; + static UINT lastFrames[NUM_ISO_ENDPOINTS] = {}; + static long long lastDeltas[NUM_ISO_ENDPOINTS] = {}; + static unsigned int repeatedPatternCount = 0; + + long long deltas[NUM_ISO_ENDPOINTS] = {}; + bool patternChanged + = !lastPatternValid || (lastPatternHadError != (framePhaseError != 0)); + + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; ++k) + { + frames[k] = isoCtx[k][earliest]->StartFrame; + deltas[k] = static_cast(frames[k]) - static_cast(ep0frame); + if (lastPatternValid && (frames[k] != lastFrames[k] || deltas[k] != lastDeltas[k])) + { + patternChanged = true; + } + } + + if (framePhaseError && patternChanged) + { + if (lastPatternValid && lastPatternHadError && repeatedPatternCount) + { + qDebug("Frame phase pattern repeated %u additional times", repeatedPatternCount); + } + qDebug("Frame phase mismatch: ctx=%d packets=%d ep0=%u epLast=%u phase=%u", earliest, + isoCtx[0][earliest]->NumberOfPackets, ep0frame, epkframe, framePhaseError); + qDebug(" StartFrame: ep0=%u ep1=%u ep2=%u ep3=%u ep4=%u ep5=%u", frames[0], + frames[1], frames[2], frames[3], frames[4], frames[5]); + qDebug(" Delta from ep0: ep0=%lld ep1=%lld ep2=%lld ep3=%lld ep4=%lld ep5=%lld", + deltas[0], deltas[1], deltas[2], deltas[3], deltas[4], deltas[5]); + } + else if (!framePhaseError && lastPatternValid && lastPatternHadError) + { + if (repeatedPatternCount) + { + qDebug("Frame phase pattern repeated %u additional times before resync", + repeatedPatternCount); + } + qDebug("Frame phase resynchronized: ctx=%d packets=%d ep0=%u", earliest, + isoCtx[0][earliest]->NumberOfPackets, ep0frame); + } + else if (framePhaseError) + { + repeatedPatternCount++; + } + + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; ++k) + { + lastFrames[k] = frames[k]; + lastDeltas[k] = deltas[k]; + } + lastPatternValid = true; + lastPatternHadError = (framePhaseError != 0); + if (!framePhaseError || patternChanged) + repeatedPatternCount = 0; +// END DIAGNOSTIC CODE +#endif + if (publishAlignedBuffer) + { + // Copy the tranfer data into buffer + for (int i = 0; i < isoCtx[0][earliest]->NumberOfPackets; i++) + { + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; k++) + { + dataBufferOffset = isoCtx[k][earliest]->IsoPackets[i].Offset; + memcpy(&(outBuffers[currentWriteBuffer][packetLength]), + &dataBuffer[k][earliest][dataBufferOffset], + isoCtx[k][earliest]->IsoPackets[i].Length); + packetLength += isoCtx[k][earliest]->IsoPackets[i].Length; + } + } + + // Get the data for isoRead() ready and swap buffers + bufferLengths[currentWriteBuffer] = packetLength; + currentWriteBuffer = !currentWriteBuffer; + } + + // Check for incorrect setup and kill if that were the case. + + if (framePhaseError) + { +#ifndef WINDOWS_32_BIT + qDebug("Frame phase error of %d", framePhaseError); +#endif + } + + // Setup transfer for resubmission + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; k++) + { + if (shutdownRequested) + { + continue; + } + // Apparently reusing before resubmitting is a bad idea??? + + /*UINT oldStart = isoCtx[k][earliest]->StartFrame; + success = IsoK_ReUse(isoCtx[k][earliest]); + if(!success){ + DWORD errorCode = GetLastError(); + qDebug() << "IsoK_Init failed with error code" << errorCode; + qDebug() << "n =" << n; + return; + } + isoCtx[k][earliest]->StartFrame = 0; + + success = OvlK_ReUse(ovlkHandle[k][earliest]); + if(!success){ + DWORD errorCode = GetLastError(); + qDebug() << "OvlK_ReUse failed with error code" << errorCode; + qDebug() << "n =" << n; + return; + } + // Resubmit the transfer + success = UsbK_IsoReadPipe(handle, pipeID[k], dataBuffer[k][earliest], + sizeof(dataBuffer[k][earliest]), (LPOVERLAPPED)ovlkHandle[k][earliest], + isoCtx[k][earliest]); + */ + if (!UsbK_IsoReadPipe(handle, pipeID[k], dataBuffer[k][earliest], + sizeof(dataBuffer[k][earliest]), (LPOVERLAPPED)ovlkHandle[k][earliest], + isoCtx[k][earliest])) + { + DWORD errorCode = GetLastError(); + qDebug() << "UsbK_IsoReadPipe failed with error code" << errorCode; + } + } + // qDebug() << "Resubmitted Ctx #"<< earliest; + // Signal to isoDriver that it can draw a new frame. + if (publishAlignedBuffer) + { + upTick(); + } + return; } -char *winUsbDriver::isoRead(unsigned int *newLength){ - //This will be called almost immediately after the upTick() signal is sent. Make sure bufferLengths[] abd outBuffers[] are ready! - *(newLength) = bufferLengths[!currentWriteBuffer]; - return (char*) outBuffers[(unsigned char) !currentWriteBuffer]; +char* winUsbDriver::isoRead(unsigned int* newLength) +{ + // This will be called almost immediately after the upTick() signal is sent. Make + // sure bufferLengths[] abd outBuffers[] are ready! + *(newLength) = bufferLengths[!currentWriteBuffer]; + return (char*)outBuffers[(unsigned char)!currentWriteBuffer]; } -bool winUsbDriver::allEndpointsComplete(int n){ - //Just tells you if transfers have completed on _all_ iso endpoints for a given value of n. - for (unsigned char k=0;kstop(); + } + if (recoveryTimer) + { + recoveryTimer->stop(); + } + + if (handle) + { + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; k++) + { + UsbK_AbortPipe(handle, pipeID[k]); + UsbK_FlushPipe(handle, pipeID[k]); + } + } + + // Drain outstanding overlapped requests. + QElapsedTimer drainTimer; + drainTimer.start(); + constexpr qint64 kDrainTimeoutMs = 3000; + bool allComplete = false; + while (!allComplete && (drainTimer.elapsed() < kDrainTimeoutMs)) + { + allComplete = true; + for (unsigned char k = 0; k < NUM_ISO_ENDPOINTS; k++) + { + for (int n = 0; n < NUM_FUTURE_CTX; n++) + { + if (ovlkHandle[k][n] && !OvlK_IsComplete(ovlkHandle[k][n])) + { + allComplete = false; + break; + } + } + if (!allComplete) + { + break; + } + } + if (!allComplete) + { + QThread::msleep(10); + } + } + + if (!allComplete) + { + qDebug() << "winUsbDriver::shutdownProcedure timed out waiting for overlapped " + "completion"; + } + + if (!shutdownCompleteSent) + { + shutdownCompleteSent = true; + connected = false; + connectedStatus(false); + emit shutdownComplete(); + } + return; } -int winUsbDriver::flashFirmware(void){ - qDebug() << "\n\n\n\n\n\n\n\nFIRMWARE MISMATCH!!!! FLASHING....\n\n\n\n\n\n\n"; - - signalFirmwareFlash(); - QApplication::processEvents(); - - //Go to bootloader mode - bootloaderJump(); - - //Get location of firmware file - QString firmware_path = QCoreApplication::applicationDirPath(); - firmware_path.append(QString::asprintf("/firmware/labrafirm_%04x_%02x.hex", EXPECTED_FIRMWARE_VERSION, DEFINED_EXPECTED_VARIANT)); - qDebug() << "FLASHING " << firmware_path; - - //Set up interface to dfuprog - QString dfuprog_path = QCoreApplication::applicationDirPath(); - dfuprog_path.append("/firmware/dfu-programmer"); - QProcess dfu_exe; - QStringList args_stage1; - args_stage1 << "atxmega32a4u" << "erase" << "--force"; - QStringList args_stage2; - args_stage2 << "atxmega32a4u" << "flash" << firmware_path; - QStringList args_stage3; - args_stage3 << "atxmega32a4u" << "launch"; - QStringList args_stage4; - args_stage4 << "atxmega32a4u" << "launch"; - - //Run stage 1, until there's a success - do { - QThread::msleep(200); - dfu_exe.start(dfuprog_path, args_stage1); - dfu_exe.waitForFinished(-1); - qDebug() << "stdio_stage1" << dfu_exe.readAllStandardOutput(); - qDebug() << "sterr_stage1" << dfu_exe.readAllStandardError(); - qDebug() << "EXIT_CODE stage1" << dfu_exe.exitCode(); - /*if(dfu_exe.exitCode()){ - return dfu_exe.exitCode()+100; - }*/ - QApplication::processEvents(); - } while (dfu_exe.exitCode()); - - //Run stage 2 - dfu_exe.start(dfuprog_path, args_stage2); - dfu_exe.waitForFinished(-1); - qDebug() << "stdio_stage2" << dfu_exe.readAllStandardOutput(); - qDebug() << "sterr_stage2" << dfu_exe.readAllStandardError(); - qDebug() << "EXIT_CODE stage2" << dfu_exe.exitCode(); - if (dfu_exe.exitCode()) { - return dfu_exe.exitCode()+200; - } - - //Run stage 3 - dfu_exe.start(dfuprog_path, args_stage3); - dfu_exe.waitForFinished(-1); - qDebug() << "stdio_stage3" << dfu_exe.readAllStandardOutput(); - qDebug() << "sterr_stage3" << dfu_exe.readAllStandardError(); - qDebug() << "EXIT_CODE stage3" << dfu_exe.exitCode(); - if (dfu_exe.exitCode()) { - return dfu_exe.exitCode()+300; - } - - //Run stage 4 - double launch to clear the eeprom flag from bootloaderJump. - //Connect back to labrador - do { - QThread::msleep(200); - dfu_exe.start(dfuprog_path, args_stage4); - dfu_exe.waitForFinished(-1); - qDebug() << "stdio_stage4" << dfu_exe.readAllStandardOutput(); - qDebug() << "sterr_stage4" << dfu_exe.readAllStandardError(); - qDebug() << "EXIT_CODE stage4" << dfu_exe.exitCode(); - QApplication::processEvents(); - } while (dfu_exe.exitCode()); - - return 0; +int winUsbDriver::flashFirmware(void) +{ + qDebug() << "\n\n\n\n\n\n\n\nFIRMWARE MISMATCH!!!! FLASHING....\n\n\n\n\n\n\n"; + + signalFirmwareFlash(); + QApplication::processEvents(); + + // Go to bootloader mode + bootloaderJump(); + + // Get location of firmware file + QString firmware_path = QCoreApplication::applicationDirPath(); + firmware_path.append(QString::asprintf("/firmware/labrafirm_%04x_%02x.hex", + EXPECTED_FIRMWARE_VERSION, DEFINED_EXPECTED_VARIANT)); + qDebug() << "FLASHING " << firmware_path; + + // Set up interface to dfuprog + QString dfuprog_path = QCoreApplication::applicationDirPath(); + dfuprog_path.append("/firmware/dfu-programmer"); + QProcess dfu_exe; + QStringList args_stage1; + args_stage1 << "atxmega32a4u" << "erase" << "--force"; + QStringList args_stage2; + args_stage2 << "atxmega32a4u" << "flash" << firmware_path; + QStringList args_stage3; + args_stage3 << "atxmega32a4u" << "launch"; + QStringList args_stage4; + args_stage4 << "atxmega32a4u" << "launch"; + + // Run stage 1, until there's a success + do + { + QThread::msleep(200); + dfu_exe.start(dfuprog_path, args_stage1); + dfu_exe.waitForFinished(-1); + qDebug() << "stdio_stage1" << dfu_exe.readAllStandardOutput(); + qDebug() << "sterr_stage1" << dfu_exe.readAllStandardError(); + qDebug() << "EXIT_CODE stage1" << dfu_exe.exitCode(); + /*if(dfu_exe.exitCode()){ + return dfu_exe.exitCode()+100; + }*/ + QApplication::processEvents(); + } while (dfu_exe.exitCode()); + + // Run stage 2 + dfu_exe.start(dfuprog_path, args_stage2); + dfu_exe.waitForFinished(-1); + qDebug() << "stdio_stage2" << dfu_exe.readAllStandardOutput(); + qDebug() << "sterr_stage2" << dfu_exe.readAllStandardError(); + qDebug() << "EXIT_CODE stage2" << dfu_exe.exitCode(); + if (dfu_exe.exitCode()) + { + return dfu_exe.exitCode() + 200; + } + + // Run stage 3 + dfu_exe.start(dfuprog_path, args_stage3); + dfu_exe.waitForFinished(-1); + qDebug() << "stdio_stage3" << dfu_exe.readAllStandardOutput(); + qDebug() << "sterr_stage3" << dfu_exe.readAllStandardError(); + qDebug() << "EXIT_CODE stage3" << dfu_exe.exitCode(); + if (dfu_exe.exitCode()) + { + return dfu_exe.exitCode() + 300; + } + + // Run stage 4 - double launch to clear the eeprom flag from bootloaderJump. + // Connect back to labrador + do + { + QThread::msleep(200); + dfu_exe.start(dfuprog_path, args_stage4); + dfu_exe.waitForFinished(-1); + qDebug() << "stdio_stage4" << dfu_exe.readAllStandardOutput(); + qDebug() << "sterr_stage4" << dfu_exe.readAllStandardError(); + qDebug() << "EXIT_CODE stage4" << dfu_exe.exitCode(); + QApplication::processEvents(); + } while (dfu_exe.exitCode()); + + return 0; } -void winUsbDriver::manualFirmwareRecovery(void){ - //Get location of firmware file - QString firmware_path = QCoreApplication::applicationDirPath(); - firmware_path.append(QString::asprintf("/firmware/labrafirm_%04x_%02x.hex", EXPECTED_FIRMWARE_VERSION, DEFINED_EXPECTED_VARIANT)); - - //Set up interface to dfuprog - QString dfuprog_path = QCoreApplication::applicationDirPath(); - dfuprog_path.append("/firmware/dfu-programmer"); - QProcess dfu_exe; - QStringList leaveBootloaderCommand; - leaveBootloaderCommand << "atxmega32a4u" << "launch"; - QStringList eraseCommand; - eraseCommand << "atxmega32a4u" << "erase" << "--force"; - QStringList flashCommand; - flashCommand << "atxmega32a4u" << "flash" << firmware_path; - - //Intro - QMessageBox manualFirmwareMessages; - manualFirmwareMessages.setText("Welcome to the firmware recovery wizard.\nThis tool will attempt various steps to troubleshoot a board with connection issues.\n\nPress OK to continue."); - manualFirmwareMessages.exec(); - - //Hello, this is IT, can you try turning it off and on again? - manualFirmwareMessages.setText("Before continuing, please disconnect and reconnect your Labrador board, then wait 10 seconds.\n\nAlso ensure that there are no other instances of the Labrador software running on this machine."); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setStandardButtons(QMessageBox::Yes|QMessageBox::No); - manualFirmwareMessages.setText("Did that fix things?"); - int messageBoxReturn = manualFirmwareMessages.exec(); - manualFirmwareMessages.setStandardButtons(QMessageBox::Ok); - if (messageBoxReturn == QMessageBox::Yes) { - manualFirmwareMessages.setText("Awesome! Have fun!"); - manualFirmwareMessages.exec(); - return; - } - - //Real troubleshooting begins here..... - - //USB Problems. - if (connected) { - manualFirmwareMessages.setText("It seems like your board is already connected and configured correctly.\n\nIf your board is not functioning correctly, this indicates that there is an issue with the USB driver.\n\nLet's go through some manual troubleshooting steps."); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setText("There are two main possibilities:\n\n - Your USB Controller does not support Isochronous mode at USB 2.0 FS\n - Another device is competing with Labrador for bandwidth."); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setText("If Labrador is connected to a USB 2.0 port, unplug it and connect it to a USB3 port.\n\nIf it's in a USB3 port, try connecting it to a USB 2.0 port.\n\nIf you have other USB devices, such as a keyboard and mouse, ensure they're connected to a port of a different type than Labrador (e.g., if your mouse and keyboard are in USB 2.0 ports, try putting Labrador in a USB 3.0 port).\n\nIf you're not sure which is which, USB3 ports are usually blue on the inside."); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setText("If you have a spare USB hub, connect Labrador (and only Labrador) to the hub.\n\nThis will usually result in a huge reduction in bandwidth required to communicate with Labrador, since a Hi-Speed hub reads from Labrador at 12MHz, but transmits upstream to the host at 480MHz.\n\n(If the host is connected to Labrador directly, it is slowed down to 12MHz!) "); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setText("If it's still not working, please disconnect all USB devices from your machine, then one by one, insert Labrador into each USB port on your machine until it starts working."); - manualFirmwareMessages.exec(); - manualFirmwareMessages.setText("If that doesn't fix it, please open an issue on github.com/espotek-org/labrador, or contact me at admin@espotek.com."); - manualFirmwareMessages.exec(); - return; - } else { - qDebug() << "Attempting to leave bootloader!"; - dfu_exe.start(dfuprog_path, leaveBootloaderCommand); - dfu_exe.waitForFinished(-1); - manualFirmwareMessages.setText("No Labrador board could be detected.\n\nIt's possible that you're stuck in booloader mode.\n\nI've attempted to launch the firmware manually."); - manualFirmwareMessages.exec(); - if (dfu_exe.exitCode()) { - qDebug() << "Exit code =" << dfu_exe.exitCode(); - manualFirmwareMessages.setText("Command failed. This usually means that no device is detected.\n\nPlease Ensure that the cable you're using can carry data (for example, by using it to transfer data to your phone).\n\nSome cables are for charging only, and not physically contain data lines.\n\nAlso note that the red light on the Labrador board is a power indicator for the PSU output pins.\nIt will turn on even if no data lines are present."); - manualFirmwareMessages.exec(); - return; - } - //Firmware launch failed, but bootloader preset - if (!connected) { - qDebug() << "Attempting to erase!"; - dfu_exe.start(dfuprog_path, eraseCommand); - dfu_exe.waitForFinished(-1); - int exit_code = dfu_exe.exitCode(); - - qDebug() << "Exit code for erase =" << dfu_exe.exitCode(); - - qDebug() << "Attempting to flash file" << firmware_path; - dfu_exe.start(dfuprog_path, flashCommand); - dfu_exe.waitForFinished(-1); - exit_code += dfu_exe.exitCode(); - - qDebug() << "Exit code for flash =" << dfu_exe.exitCode(); - - manualFirmwareMessages.setText("The bootloader is present, but firmware launch failed. I've attempted to reprogram it."); - manualFirmwareMessages.exec(); - - if (!exit_code) { //Reprogramming was successful, but board is still in bootloader mode. - dfu_exe.start(dfuprog_path, leaveBootloaderCommand); - dfu_exe.waitForFinished(-1); - manualFirmwareMessages.setText("Reprogramming was successful! Attempting to launch the board.\n\nIf it does not start working immediately, please wait 10 seconds and then reconnect the board."); - manualFirmwareMessages.exec(); - } else { //Programming failed. - manualFirmwareMessages.setText("Automatic Reprogramming failed.\n\nPlease try again, making sure you read every message carefully and slowly before pushing 'OK'.\nWindows can take several seconds to detect USB events, so this is sometimes necessary.\n\nIf it's still not programming properly, please contact me at admin@espotek.com for further support."); - manualFirmwareMessages.exec(); - } - - return; - } - } +void winUsbDriver::manualFirmwareRecovery(void) +{ + // Get location of firmware file + QString firmware_path = QCoreApplication::applicationDirPath(); + firmware_path.append(QString::asprintf("/firmware/labrafirm_%04x_%02x.hex", + EXPECTED_FIRMWARE_VERSION, DEFINED_EXPECTED_VARIANT)); + + // Set up interface to dfuprog + QString dfuprog_path = QCoreApplication::applicationDirPath(); + dfuprog_path.append("/firmware/dfu-programmer"); + QProcess dfu_exe; + QStringList leaveBootloaderCommand; + leaveBootloaderCommand << "atxmega32a4u" << "launch"; + QStringList eraseCommand; + eraseCommand << "atxmega32a4u" << "erase" << "--force"; + QStringList flashCommand; + flashCommand << "atxmega32a4u" << "flash" << firmware_path; + + // Intro + QMessageBox manualFirmwareMessages; + manualFirmwareMessages.setText( + "Welcome to the firmware recovery wizard.\nThis tool will attempt various steps " + "to troubleshoot a board with connection issues.\n\nPress OK to continue."); + manualFirmwareMessages.exec(); + + // Hello, this is IT, can you try turning it off and on again? + manualFirmwareMessages.setText( + "Before continuing, please disconnect and reconnect your Labrador board, then " + "wait 10 seconds.\n\nAlso ensure that there are no other instances of the " + "Labrador software running on this machine."); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + manualFirmwareMessages.setText("Did that fix things?"); + int messageBoxReturn = manualFirmwareMessages.exec(); + manualFirmwareMessages.setStandardButtons(QMessageBox::Ok); + if (messageBoxReturn == QMessageBox::Yes) + { + manualFirmwareMessages.setText("Awesome! Have fun!"); + manualFirmwareMessages.exec(); + return; + } + + // Real troubleshooting begins here..... + + // USB Problems. + if (connected) + { + manualFirmwareMessages.setText( + "It seems like your board is already connected and configured " + "correctly.\n\nIf your board is not functioning correctly, this indicates " + "that there is an issue with the USB driver.\n\nLet's go through some manual " + "troubleshooting steps."); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setText( + "There are two main possibilities:\n\n - Your USB Controller does not " + "support Isochronous mode at USB 2.0 FS\n - Another device is competing with " + "Labrador for bandwidth."); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setText( + "If Labrador is connected to a USB 2.0 port, unplug it and connect it to a " + "USB3 port.\n\nIf it's in a USB3 port, try connecting it to a USB 2.0 " + "port.\n\nIf you have other USB devices, such as a keyboard and mouse, " + "ensure they're connected to a port of a different type than Labrador (e.g., " + "if your mouse and keyboard are in USB 2.0 ports, try putting Labrador in a " + "USB 3.0 port).\n\nIf you're not sure which is which, USB3 ports are usually " + "blue on the inside."); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setText( + "If you have a spare USB hub, connect Labrador (and only Labrador) to the " + "hub.\n\nThis will usually result in a huge reduction in bandwidth required " + "to communicate with Labrador, since a Hi-Speed hub reads from Labrador at " + "12MHz, but transmits upstream to the host at 480MHz.\n\n(If the host is " + "connected to Labrador directly, it is slowed down to 12MHz!) "); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setText( + "If it's still not working, please disconnect all USB devices from your " + "machine, then one by one, insert Labrador into each USB port on your " + "machine until it starts working."); + manualFirmwareMessages.exec(); + manualFirmwareMessages.setText( + "If that doesn't fix it, please open an issue on " + "github.com/espotek-org/labrador, or contact me at admin@espotek.com."); + manualFirmwareMessages.exec(); + return; + } + else + { + qDebug() << "Attempting to leave bootloader!"; + dfu_exe.start(dfuprog_path, leaveBootloaderCommand); + dfu_exe.waitForFinished(-1); + manualFirmwareMessages.setText( + "No Labrador board could be detected.\n\nIt's possible that you're stuck in " + "booloader mode.\n\nI've attempted to launch the firmware manually."); + manualFirmwareMessages.exec(); + if (dfu_exe.exitCode()) + { + qDebug() << "Exit code =" << dfu_exe.exitCode(); + manualFirmwareMessages.setText( + "Command failed. This usually means that no device is " + "detected.\n\nPlease Ensure that the cable you're using can carry data " + "(for example, by using it to transfer data to your phone).\n\nSome " + "cables are for charging only, and not physically contain data " + "lines.\n\nAlso note that the red light on the Labrador board is a power " + "indicator for the PSU output pins.\nIt will turn on even if no data " + "lines are present."); + manualFirmwareMessages.exec(); + return; + } + // Firmware launch failed, but bootloader preset + if (!connected) + { + qDebug() << "Attempting to erase!"; + dfu_exe.start(dfuprog_path, eraseCommand); + dfu_exe.waitForFinished(-1); + int exit_code = dfu_exe.exitCode(); + + qDebug() << "Exit code for erase =" << dfu_exe.exitCode(); + + qDebug() << "Attempting to flash file" << firmware_path; + dfu_exe.start(dfuprog_path, flashCommand); + dfu_exe.waitForFinished(-1); + exit_code += dfu_exe.exitCode(); + + qDebug() << "Exit code for flash =" << dfu_exe.exitCode(); + + manualFirmwareMessages.setText( + "The bootloader is present, but firmware launch failed. I've attempted " + "to reprogram it."); + manualFirmwareMessages.exec(); + + if (!exit_code) + { // Reprogramming was successful, but board is still in bootloader mode. + dfu_exe.start(dfuprog_path, leaveBootloaderCommand); + dfu_exe.waitForFinished(-1); + manualFirmwareMessages.setText( + "Reprogramming was successful! Attempting to launch the " + "board.\n\nIf it does not start working immediately, please wait 10 " + "seconds and then reconnect the board."); + manualFirmwareMessages.exec(); + } + else + { // Programming failed. + manualFirmwareMessages.setText( + "Automatic Reprogramming failed.\n\nPlease try again, making sure " + "you read every message carefully and slowly before pushing " + "'OK'.\nWindows can take several seconds to detect USB events, so " + "this is sometimes necessary.\n\nIf it's still not programming " + "properly, please contact me at admin@espotek.com for further " + "support."); + manualFirmwareMessages.exec(); + } + + return; + } + } } diff --git a/Desktop_Interface/winusbdriver.h b/Desktop_Interface/winusbdriver.h index 259f2f68..3437e6ee 100644 --- a/Desktop_Interface/winusbdriver.h +++ b/Desktop_Interface/winusbdriver.h @@ -35,7 +35,15 @@ class winUsbDriver : public genericUsbDriver int usbIsoInit(void); int flashFirmware(void); bool allEndpointsComplete(int n); + bool shutdownRequested = false; + bool shutdownCompleteSent = false; + bool transferPending[NUM_ISO_ENDPOINTS][NUM_FUTURE_CTX] = {}; + QElapsedTimer shutdownElapsed; + signals: + void shutdownComplete(); + + public slots: void isoTimerTick(void); void recoveryTick();