* [Windows][MinGW] Update some WIN32 macro checks in source code to be MSVC-specific (not needed for GCC-based builds).
* [build] CMakeLists updates:
Adjust for Windows MinGW builds: now fully compatible with Qt5.5+ and MinGW-w64 toolchain (included in Qt installs);
Add option to specify path to required libraries/includes (instead of hard-coding `c:\programs`);
Add package check for Python and use found executable name;
Remove custom FindSdl package handler in favor of stock (and improved) CMake one (still works with Windows);
Do not force SIMU_AUDIO and LUA_COMPILER in SIMU builds (leave it up to user);
Add SIMU_LUA_COMPILER option specifically for SIMU builds (default = ON);
* [build][Windows] Rewrite/Fix Windows Companion `install` target, with two options:
a) just copy supporting Qt and c++ DLLs to build folder so programs can be quickly be run from there (this is default and similar to the old behavior);
b) full installation to CMAKE_INSTALL_PREFIX path, including all binaries, supporting DLLs, language files, and OTx utilities.
Option b) can be selected with new WIN_DO_FULL_INSTALL parameter;
Also cleans up the Linux install a bit and consolidates the OS X parts;
* [simulator][Windows] Add option to build Windows simulator with system console/terminal enabled (both standalone and when launched from Companion). Mostly useful for debug as it gives output messages much sooner in the startup process than the current debug console. Also helpful because you don't have to open it manually on every launch, and it remembers last window position and size. Possible candidate for a user-selectable runtime option in the future.
* [Windows] Fix MSVC build issues.
* [build] More build fixes:
Use a customized FindSDL.cmake script again due to bugs and deficiencies in the official version (based on script from CMake v3.7);
Fixes missing SDL DLL error in NSIS installer);
Use string for Timers option (checkbox in GUI is confusing);
Remove an unused variable.
* [simu][Windows] Fix f_getcwd() cutting off too many characters (fixes SD browser interactions).
323 lines
9.2 KiB
C++
323 lines
9.2 KiB
C++
/*
|
|
* Copyright (C) OpenTX
|
|
*
|
|
* Based on code named
|
|
* th9x - http://code.google.com/p/th9x
|
|
* er9x - http://code.google.com/p/er9x
|
|
* gruvin9x - http://code.google.com/p/gruvin9x
|
|
*
|
|
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "process_flash.h"
|
|
#include "progresswidget.h"
|
|
#include <QFile>
|
|
#include <QMessageBox>
|
|
#include <QProcess>
|
|
#include "eeprominterface.h"
|
|
//#include "firmwareinterface.h"
|
|
|
|
#if defined _MSC_VER || !defined __GNUC__
|
|
#include <Windows.h>
|
|
#include <WinBase.h>
|
|
#include <tlhelp32.h>
|
|
#define sleep(x) Sleep(x*1000)
|
|
#else
|
|
#include <unistd.h>
|
|
#include "mountlist.h"
|
|
#endif
|
|
|
|
FlashProcess::FlashProcess(const QString &cmd, const QStringList &args, ProgressWidget *progress):
|
|
progress(progress),
|
|
cmd(cmd),
|
|
args(args),
|
|
process(new QProcess(this)),
|
|
hasErrors(false),
|
|
lfuse(0),
|
|
hfuse(0),
|
|
efuse(0),
|
|
flashPhase(READING)
|
|
#if !__GNUC__
|
|
, killTimer(NULL)
|
|
#endif
|
|
{
|
|
connect(process, SIGNAL(started()),this, SLOT(onStarted()));
|
|
connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput()));
|
|
connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError()));
|
|
connect(process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
|
|
}
|
|
|
|
FlashProcess::~FlashProcess()
|
|
{
|
|
#if !__GNUC__
|
|
delete killTimer;
|
|
#endif
|
|
}
|
|
|
|
bool FlashProcess::run()
|
|
{
|
|
if (!QFile::exists(cmd)) {
|
|
QMessageBox::critical(NULL, "Companion", tr("Executable %1 not found").arg(cmd));
|
|
return false;
|
|
}
|
|
|
|
#if !__GNUC__
|
|
if (cmd.toLower().contains("sam-ba")) {
|
|
killTimer = new QTimer(this);
|
|
connect(killTimer, SIGNAL(timeout()), this, SLOT(onKillTimerElapsed()));
|
|
killTimer->start(2000);
|
|
}
|
|
#endif
|
|
|
|
QEventLoop loop;
|
|
connect(this, SIGNAL(finished()), &loop, SLOT(quit()));
|
|
process->start(cmd, args);
|
|
loop.exec();
|
|
|
|
return true;
|
|
}
|
|
|
|
void FlashProcess::onStarted()
|
|
{
|
|
progress->lock(true);
|
|
progress->addText(cmd + " " + args.join(" "));
|
|
progress->addSeparator();
|
|
}
|
|
|
|
# if !__GNUC__
|
|
bool killProcessByName(const char *szProcessToKill)
|
|
{
|
|
HANDLE hProcessSnap;
|
|
HANDLE hProcess;
|
|
PROCESSENTRY32 pe32;
|
|
DWORD dwPriorityClass;
|
|
|
|
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // Takes a snapshot of all the processes
|
|
|
|
if (hProcessSnap == INVALID_HANDLE_VALUE) {
|
|
return false;
|
|
}
|
|
|
|
pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
|
|
if (!Process32First(hProcessSnap, &pe32)) {
|
|
CloseHandle(hProcessSnap);
|
|
return false;
|
|
}
|
|
|
|
do {
|
|
if (!strcmp(pe32.szExeFile,szProcessToKill)) { // checks if process at current position has the name of to be killed app
|
|
hProcess = OpenProcess(PROCESS_TERMINATE,0, pe32.th32ProcessID); // gets handle to process
|
|
TerminateProcess(hProcess, 0); // Terminate process by handle
|
|
CloseHandle(hProcess); // close the handle
|
|
}
|
|
} while (Process32Next(hProcessSnap, &pe32)); // gets next member of snapshot
|
|
|
|
CloseHandle(hProcessSnap); // closes the snapshot handle
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
void FlashProcess::onKillTimerElapsed()
|
|
{
|
|
# if !__GNUC__
|
|
// trick to accelerate SAM-BA startup
|
|
killProcessByName("tasklist.exe");
|
|
#endif
|
|
}
|
|
|
|
void FlashProcess::analyseStandardOutput(const QString &text)
|
|
{
|
|
currStdoutLine.append(text);
|
|
if (currStdoutLine.contains("size = ")) {
|
|
int pos = currStdoutLine.lastIndexOf("size = ");
|
|
QString temp = currStdoutLine.mid(pos+7);
|
|
pos = temp.lastIndexOf("\n");
|
|
int size = temp.left(pos).toInt();
|
|
progress->setMaximum(size/2048);
|
|
}
|
|
if (currStdoutLine.contains("\n")) {
|
|
int nlPos = currStdoutLine.lastIndexOf("\n");
|
|
currStdoutLine = currStdoutLine.mid(nlPos+1);
|
|
}
|
|
if (!currStdoutLine.isEmpty()) {
|
|
if (currStdoutLine.at(0) == QChar('.')) {
|
|
int pos = currStdoutLine.lastIndexOf(".");
|
|
progress->setValue(pos);
|
|
}
|
|
else if (currStdoutLine.startsWith("Starting upload: [")) {
|
|
int pos = (currStdoutLine.lastIndexOf("#")-19)*100/256;
|
|
progress->setValue(pos);
|
|
}
|
|
}
|
|
|
|
if (text.contains("Complete ")) {
|
|
#if !__GNUC__
|
|
delete killTimer;
|
|
killTimer = NULL;
|
|
#endif
|
|
int start = text.indexOf("Complete ");
|
|
int end = text.indexOf("%");
|
|
if (start > 0) {
|
|
start += 9;
|
|
int value = text.mid(start, end-start).toInt();
|
|
progress->setValue(value);
|
|
}
|
|
}
|
|
|
|
if (text.contains(":010000")) {
|
|
// contains fuse info
|
|
QStringList stl = text.split(":01000000");
|
|
foreach (QString t, stl) {
|
|
bool ok = false;
|
|
if (!lfuse) lfuse = t.left(2).toInt(&ok, 16);
|
|
if (!hfuse && !ok) hfuse = t.left(2).toInt(&ok, 16);
|
|
if (!efuse && !ok) efuse = t.left(2).toInt(&ok, 16);
|
|
}
|
|
}
|
|
|
|
if (text.contains("-E-")) {
|
|
hasErrors = true;
|
|
}
|
|
}
|
|
|
|
void FlashProcess::analyseStandardError(const QString &text)
|
|
{
|
|
currStderrLine.append(text);
|
|
if (currStderrLine.contains("#")) {
|
|
QString avrflashPhase = currStderrLine.left(1).toLower();
|
|
if (avrflashPhase == "w") {
|
|
flashPhase = WRITING;
|
|
progress->setInfo(tr("Writing..."));
|
|
progress->setValue(2 * currStderrLine.count("#"));
|
|
}
|
|
else if (avrflashPhase == "r") {
|
|
if (flashPhase == READING) {
|
|
progress->setInfo(tr("Reading..."));
|
|
}
|
|
else {
|
|
flashPhase = VERIFYING;
|
|
progress->setInfo(tr("Verifying..."));
|
|
}
|
|
progress->setValue(2 * currStderrLine.count("#"));
|
|
}
|
|
}
|
|
|
|
if (currStderrLine.contains("\n")) {
|
|
int nlPos = currStderrLine.lastIndexOf("\n");
|
|
currStderrLine = currStderrLine.mid(nlPos+1);
|
|
}
|
|
|
|
if ((text.contains("-E-") && !text.contains("-E- No receive file name")) ||
|
|
text.contains("No DFU capable USB device found")) {
|
|
hasErrors = true;
|
|
}
|
|
}
|
|
|
|
void FlashProcess::onReadyReadStandardOutput()
|
|
{
|
|
QString text = QString(process->readAllStandardOutput());
|
|
progress->addText(text);
|
|
analyseStandardOutput(text);
|
|
}
|
|
|
|
void FlashProcess::onReadyReadStandardError()
|
|
{
|
|
QString text = QString(process->readAllStandardError());
|
|
progress->addText(text);
|
|
analyseStandardError(text);
|
|
}
|
|
|
|
void FlashProcess::errorWizard()
|
|
{
|
|
QString output = progress->getText();
|
|
|
|
if (output.contains("avrdude: Expected signature for")) { // wrong signature
|
|
int pos = output.indexOf("avrdude: Device signature = ");
|
|
bool fwexist = false;
|
|
QString DeviceStr = tr("unknown");
|
|
QString FwStr = "";
|
|
|
|
if (pos > 0) {
|
|
QString DeviceId = output.mid(pos+28, 8);
|
|
if (DeviceId=="0x1e9602") {
|
|
DeviceStr = "Atmega 64";
|
|
FwStr="\n" + tr("ie: OpenTX for 9X board or OpenTX for 9XR board");
|
|
fwexist = true;
|
|
}
|
|
else if (DeviceId=="0x1e9702") {
|
|
DeviceStr = "Atmega 128";
|
|
FwStr="\n" + tr("ie: OpenTX for M128 / 9X board or OpenTX for 9XR board with M128 chip");
|
|
fwexist = true;
|
|
}
|
|
else if (DeviceId=="0x1e9703") {
|
|
DeviceStr = "Atmega 1280";
|
|
}
|
|
else if (DeviceId=="0x1e9704") {
|
|
DeviceStr = "Atmega 1281";
|
|
}
|
|
else if (DeviceId=="0x1e9801") {
|
|
DeviceStr = "Atmega 2560";
|
|
FwStr="\n" + tr("ie: OpenTX for Gruvin9X board");
|
|
fwexist = true;
|
|
}
|
|
else if (DeviceId=="0x1e9802") {
|
|
DeviceStr = "Atmega 2561";
|
|
}
|
|
}
|
|
if (fwexist==false) {
|
|
QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio uses a %1 CPU!!!\n\nPlease check advanced burn options to set the correct cpu type.").arg(DeviceStr));
|
|
}
|
|
else {
|
|
Firmware *firmware = GetCurrentFirmware();
|
|
QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio uses a %1 CPU!!!\n\nPlease select an appropriate firmware type to program it.").arg(DeviceStr)+FwStr+tr("\nYou are currently using:\n %1").arg(firmware->getName()));
|
|
}
|
|
}
|
|
else if (output.contains("No DFU capable USB device found")){
|
|
#if defined WIN32 || !defined __GNUC__
|
|
QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio does not seem connected to USB or the driver is not installed!!!\n\nPlease use ZADIG to properly install the driver."));
|
|
#else
|
|
QMessageBox::warning(NULL, "Companion - Tip of the day", tr("Your radio does not seem connected to USB or the driver is not initialized!!!."));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FlashProcess::onFinished(int code=0)
|
|
{
|
|
progress->addSeparator();
|
|
if (code==1 && cmd.toLower().contains("sam-ba")) {
|
|
code = 0;
|
|
}
|
|
if (code) {
|
|
progress->setInfo(tr("Flashing done (exit code = %1)").arg(code));
|
|
if (cmd.toLower().contains("avrdude") || cmd.toLower().contains("dfu")) {
|
|
errorWizard();
|
|
}
|
|
}
|
|
else if (hasErrors) {
|
|
progress->setInfo(tr("Flashing done with errors"));
|
|
}
|
|
if (lfuse || hfuse || efuse) {
|
|
addReadFuses();
|
|
}
|
|
progress->setValue(progress->maximum());
|
|
progress->lock(false);
|
|
emit finished();
|
|
}
|
|
|
|
void FlashProcess::addReadFuses()
|
|
{
|
|
progress->addSeparator();
|
|
progress->addText(tr("FUSES: Low=%1 High=%2 Ext=%3").arg(lfuse, 2, 16, QChar('0')).arg(hfuse, 2, 16, QChar('0')).arg(efuse, 2, 16, QChar('0')));
|
|
progress->addSeparator();
|
|
}
|