[companion] Adjust GVAR not possible in global functions (fix #5425)
[opentx.git] / companion / src / radiointerface.cpp
blob356d90e1107f1764d66f6da2e979dbd4c12a1055
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "radiointerface.h"
22 #include "appdata.h"
23 #include "eeprominterface.h"
24 #include "process_flash.h"
25 #include "radionotfound.h"
26 #include "burnconfigdialog.h"
27 #include "helpers.h"
28 #include "process_copy.h"
29 #include "storage.h"
30 #include "progresswidget.h"
32 QString getRadioInterfaceCmd()
34 burnConfigDialog bcd;
35 Board::Type board = getCurrentBoard();
36 if (IS_STM32(board)) {
37 return bcd.getDFU();
39 else if (IS_SKY9X(board)) {
40 return bcd.getSAMBA();
42 else {
43 return bcd.getAVRDUDE();
47 QStringList getAvrdudeArgs(const QString & cmd, const QString & filename)
49 QStringList args;
50 burnConfigDialog bcd;
51 QString programmer = bcd.getProgrammer();
52 QString mcu = bcd.getMCU();
53 Board::Type board = getCurrentBoard();
55 args << "-c" << programmer << "-p";
56 if (IS_2560(board))
57 args << "m2560";
58 else if (board == Board::BOARD_M128)
59 args << "m128";
60 else
61 args << mcu;
63 args << bcd.getAvrdudeArgs();
65 QString fullcmd = cmd + filename;
66 if (QFileInfo(filename).suffix().toUpper() == "HEX")
67 fullcmd += ":i";
68 else if (QFileInfo(filename).suffix().toUpper()=="BIN")
69 fullcmd += ":r";
70 else
71 fullcmd += ":a";
73 args << "-U" << fullcmd;
75 return args;
78 QStringList getDfuArgs(const QString & cmd, const QString & filename)
80 QStringList args;
81 burnConfigDialog bcd;
82 args << bcd.getDFUArgs();
83 if (!filename.endsWith(".dfu"))
84 args << "--dfuse-address" << "0x08000000";
85 if (cmd == "-U")
86 args.last().append(":" % QString::number(Boards::getFlashSize(getCurrentBoard())));
87 args << "--device" << "0483:df11";
88 args << "" << cmd % filename;
89 return args;
92 QStringList getSambaArgs(const QString & tcl)
94 QStringList result;
96 QString tclFilename = generateProcessUniqueTempFileName("temp.tcl");
97 if (QFile::exists(tclFilename)) {
98 qunlink(tclFilename);
100 QFile tclFile(tclFilename);
101 if (!tclFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
102 QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Cannot write file %1:\n%2.").arg(tclFilename).arg(tclFile.errorString()));
103 return result;
106 QTextStream outputStream(&tclFile);
107 outputStream << tcl;
109 burnConfigDialog bcd;
110 result << bcd.getSambaPort() << bcd.getArmMCU() << tclFilename ;
111 return result;
114 QStringList getReadEEpromCmd(const QString & filename)
116 QStringList result;
117 EEPROMInterface *eepromInterface = getCurrentEEpromInterface();
118 if (IS_STM32(eepromInterface->getBoard())) {
119 // impossible
121 else if (IS_SKY9X(eepromInterface->getBoard())) {
122 result = getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename + "\" 0x0 0x80000 0\n");
124 else {
125 result = getAvrdudeArgs("eeprom:r:", filename);
127 return result;
130 QStringList getWriteEEpromCmd(const QString & filename)
132 Board::Type board = getCurrentBoard();
133 if (IS_STM32(board)) {
134 // impossible
135 return QStringList();
137 else if (IS_SKY9X(board)) {
138 return getSambaArgs(QString("SERIALFLASH::Init 0\n") + "send_file {SerialFlash AT25} \"" + filename + "\" 0x0 0\n");
140 else {
141 return getAvrdudeArgs("eeprom:w:", filename);
145 QStringList getWriteFirmwareArgs(const QString & filename)
147 Board::Type board = getCurrentBoard();
148 if (IS_STM32(board)) {
149 return getDfuArgs("-D", filename);
151 else if (board == Board::BOARD_SKY9X) {
152 return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n");
154 else if (board == Board::BOARD_9XRPRO || board == Board::BOARD_AR9X) {
155 return getSambaArgs(QString("send_file {Flash} \"") + filename + "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n");
157 else {
158 return getAvrdudeArgs("flash:w:", filename);
162 QStringList getReadFirmwareArgs(const QString & filename)
164 Board::Type board = getCurrentBoard();
165 if (IS_STM32(board)) {
166 return getDfuArgs("-U", filename);
168 else if (board == Board::BOARD_SKY9X) {
169 return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x40000 0\n");
171 else if (board == Board::BOARD_9XRPRO) {
172 return getSambaArgs(QString("receive_file {Flash} \"") + filename + "\" 0x400000 0x80000 0\n");
174 else {
175 return getAvrdudeArgs("flash:r:", filename);
179 void readAvrdudeFuses(ProgressWidget * progress)
181 burnConfigDialog bcd;
182 QStringList args;
183 args << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << bcd.getAvrdudeArgs() << "-U" << "lfuse:r:-:i" << "-U" << "hfuse:r:-:i" << "-U" << "efuse:r:-:i";
184 FlashProcess flashProcess(bcd.getAVRDUDE(), args, progress);
185 flashProcess.run();
188 void resetAvrdudeFuses(bool eepromProtect, ProgressWidget * progress)
190 //fuses
191 //avrdude -c usbasp -p m64 -U lfuse:w:<0x0E>:m
192 //avrdude -c usbasp -p m64 -U hfuse:w:<0x89>:m 0x81 for eeprom protection
193 //avrdude -c usbasp -p m64 -U efuse:w:<0xFF>:m
195 burnConfigDialog bcd;
196 QMessageBox::StandardButton ret = QMessageBox::No;
197 ret = QMessageBox::warning(NULL, QObject::tr("Companion"),
198 QObject::tr("<b><u>WARNING!</u></b><br>This will reset the fuses of %1 to the factory settings.<br>"
199 "Writing fuses can mess up your radio.<br>Do this only if you are sure they are wrong!<br>"
200 "Are you sure you want to continue?").arg(bcd.getMCU()),
201 QMessageBox::Yes | QMessageBox::No);
202 if (ret == QMessageBox::Yes) {
203 QStringList args = bcd.getAvrdudeArgs();
204 QStringList str;
205 if (bcd.getMCU() == "m2560") {
206 args << "-B8";
207 QString erStr = eepromProtect ? "hfuse:w:0x51:m" : "hfuse:w:0x59:m";
208 str << "-U" << "lfuse:w:0xD7:m" << "-U" << erStr << "-U" << "efuse:w:0xFC:m";
209 //use hfuse = 0x81 to prevent eeprom being erased with every flashing
211 else {
212 QString lfuses;
213 QString tempFile = generateProcessUniqueTempFileName("ftemp.bin");
214 QStringList argread;
215 argread << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args <<"-U" << "lfuse:r:"+tempFile+":r";
216 FlashProcess flashProcess(bcd.getAVRDUDE(), argread, progress);
217 flashProcess.run();
218 QFile file(tempFile);
219 if (file.exists() && file.size()==1) {
220 file.open(QIODevice::ReadOnly);
221 char bin_flash[1];
222 file.read(bin_flash, 1);
223 if (bin_flash[0]==0x0E) {
224 lfuses = "lfuse:w:0x0E:m";
226 else {
227 lfuses = "lfuse:w:0x3F:m";
229 file.close();
230 qunlink(tempFile);
232 else {
233 lfuses = "lfuse:w:0x3F:m";
236 QString erStr = eepromProtect ? "hfuse:w:0xC1:m" : "hfuse:w:0xC9:m";
237 str << "-U" << lfuses << "-U" << erStr << "-U" << "efuse:w:0xFF:m";
238 //use hfuse = 0x81 to prevent eeprom being erased with every flashing
241 QStringList arguments;
242 if (bcd.getMCU() == "m2560") {
243 arguments << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args << "-u" << str;
245 else {
246 arguments << "-c" << bcd.getProgrammer() << "-p" << bcd.getMCU() << args << "-B" << "100" << "-u" << str;
248 FlashProcess flashProcess(bcd.getAVRDUDE(), arguments, progress);
249 flashProcess.run();
254 bool readFirmware(const QString & filename, ProgressWidget * progress)
256 bool result = false;
258 QFile file(filename);
259 if (file.exists() && !file.remove()) {
260 QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Could not delete temporary file: %1").arg(filename));
261 return false;
264 if (IS_ARM(getCurrentBoard())) {
265 QString path = findMassstoragePath("FIRMWARE.BIN");
266 if (!path.isEmpty()) {
267 qDebug() << "readFirmware: reading" << path << "into" << filename;
268 CopyProcess copyProcess(path, filename, progress);
269 result = copyProcess.run();
273 if (result == false) {
274 qDebug() << "readFirmware: reading" << filename << "with" << getRadioInterfaceCmd() << getReadFirmwareArgs(filename);
275 FlashProcess flashProcess(getRadioInterfaceCmd(), getReadFirmwareArgs(filename), progress);
276 result = flashProcess.run();
279 if (!QFileInfo(filename).exists()) {
280 result = false;
283 return result;
286 bool writeFirmware(const QString & filename, ProgressWidget * progress)
288 if (IS_ARM(getCurrentBoard())) {
289 QString path = findMassstoragePath("FIRMWARE.BIN");
290 if (!path.isEmpty()) {
291 qDebug() << "writeFirmware: writing" << path << "from" << filename;
292 CopyProcess copyProcess(filename, path, progress);
293 return copyProcess.run();
297 qDebug() << "writeFirmware: writing" << filename << "with" << getRadioInterfaceCmd() << getWriteFirmwareArgs(filename);
298 FlashProcess flashProcess(getRadioInterfaceCmd(), getWriteFirmwareArgs(filename), progress);
299 return flashProcess.run();
302 bool readEeprom(const QString & filename, ProgressWidget * progress)
304 Board::Type board = getCurrentBoard();
306 QFile file(filename);
307 if (file.exists() && !file.remove()) {
308 QMessageBox::warning(NULL,
309 QObject::tr("Error"),
310 QObject::tr("Could not delete temporary file: %1").arg(filename));
311 return false;
314 if (IS_HORUS(board)) {
315 QString radioPath = findMassstoragePath("RADIO", true);
316 qDebug() << "Searching for SD card, found" << radioPath;
317 if (radioPath.isEmpty()) {
318 QMessageBox::critical(progress, QObject::tr("Error"), QObject::tr("Unable to find Horus radio SD card!"));
319 return false;
321 RadioData radioData;
322 Storage inputStorage(radioPath);
323 if (!inputStorage.load(radioData)) {
324 QMessageBox::critical(progress, QObject::tr("Error"), inputStorage.error());
325 return false;
327 Storage outputStorage(filename);
328 if (!outputStorage.write(radioData)) {
329 QMessageBox::critical(progress, QObject::tr("Error"), outputStorage.error());
330 return false;
333 else {
334 if (IS_ARM(board)) {
335 QString path = findMassstoragePath("EEPROM.BIN");
336 if (path.isEmpty()) {
337 // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
338 path = findMassstoragePath("TARANIS.BIN");
340 if (path.isEmpty()) {
341 // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
342 path = findMassstoragePath("ERSKY9X.BIN");
344 if (path.isEmpty()) {
345 RadioNotFoundDialog dialog(progress);
346 dialog.exec();
347 return false;
349 CopyProcess copyProcess(path, filename, progress);
350 if (!copyProcess.run()) {
351 return false;
355 if (!IS_STM32(board)) {
356 FlashProcess flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename), progress);
357 if (!flashProcess.run()) {
358 return false;
363 return QFileInfo(filename).exists();
366 bool writeEeprom(const QString & filename, ProgressWidget * progress)
368 Board::Type board = getCurrentBoard();
370 if (IS_ARM(board)) {
371 QString path = findMassstoragePath("EEPROM.BIN");
372 if (path.isEmpty()) {
373 // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
374 path = findMassstoragePath("TARANIS.BIN");
376 if (path.isEmpty()) {
377 // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
378 path = findMassstoragePath("ERSKY9X.BIN");
380 if (!path.isEmpty()) {
381 CopyProcess copyProcess(filename, path, progress);
382 return copyProcess.run();
386 if (!IS_TARANIS(board)) {
387 FlashProcess flashProcess(getRadioInterfaceCmd(), getWriteEEpromCmd(filename), progress);
388 return flashProcess.run();
391 if (IS_ARM(board)) {
392 RadioNotFoundDialog dialog(progress);
393 dialog.exec();
396 return false;
399 #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
400 #if defined WIN32 || !defined __GNUC__
401 #include <windows.h>
402 bool isRemovableMedia(const QString & vol)
404 char szDosDeviceName[MAX_PATH];
405 QString volume = vol;
406 UINT driveType = GetDriveType(volume.replace("/", "\\").toLatin1());
407 if (driveType != DRIVE_REMOVABLE)
408 return false;
409 QueryDosDevice(volume.replace("/", "").toLatin1(), szDosDeviceName, MAX_PATH);
410 if (strstr(szDosDeviceName, "\\Floppy") != NULL) { // it's a floppy
411 return false;
413 return true;
415 #else
416 #include "mountlist.h"
417 #endif // defined WIN32 || !defined __GNUC__
418 #endif // (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
420 QString findMassstoragePath(const QString & filename, bool onlyPath)
422 QString temppath;
423 QString eepromfile;
424 bool found = false;
425 QRegularExpression fstypeRe("^(v?fat|msdos)", QRegularExpression::CaseInsensitiveOption); // Linux: "vfat"; macOS: "msdos"; Win: "FAT32"
426 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
427 foreach(const QStorageInfo & si, QStorageInfo::mountedVolumes()) {
428 //qDebug() << si.rootPath() << si.name() << si.device() << si.displayName() << si.fileSystemType() << si.isReady();
429 if (!si.isReady() || !QString(si.fileSystemType()).contains(fstypeRe))
430 continue;
431 temppath = si.rootPath();
432 eepromfile = temppath % "/" % filename;
433 qDebug() << "Searching for" << eepromfile;
434 if (QFile::exists(eepromfile)) {
435 found = true;
436 break;
439 #elif defined WIN32 || !defined __GNUC__
440 static QStringList blacklist;
441 foreach(const QFileInfo & drive, QDir::drives()) {
442 temppath = drive.absolutePath();
443 if (blacklist.contains(temppath))
444 continue;
445 if (!isRemovableMedia(temppath)) {
446 blacklist.append(temppath);
447 continue;
449 WCHAR szVolumeName[256] ;
450 WCHAR szFileSystemName[256];
451 DWORD dwSerialNumber = 0;
452 DWORD dwMaxFileNameLength=256;
453 DWORD dwFileSystemFlags=0;
454 if (!GetVolumeInformationW( (WCHAR *) drive.absolutePath().utf16(),szVolumeName,256,&dwSerialNumber,&dwMaxFileNameLength,&dwFileSystemFlags,szFileSystemName,256))
455 continue;
456 eepromfile = temppath % "/" % filename;
457 qDebug() << "Searching for" << eepromfile;
458 if (QFile::exists(eepromfile)) {
459 found = true;
460 break;
463 #else
464 const static QStringList blacklist = QStringList() << "/" << "/net" << "/proc" << "/run";
465 QStringList drives;
466 struct mount_entry *entry;
467 struct mount_entry *firstEntry;
468 firstEntry = entry = read_file_system_list(true);
469 while (entry != NULL) {
470 temppath = entry->me_mountdir;
471 if (!drives.contains(entry->me_devname) && !blacklist.contains(temppath) && QString(entry->me_type).contains(fstypeRe)) {
472 drives.append(entry->me_devname);
473 eepromfile = temppath % "/" % filename;
474 qDebug() << "Searching for" << eepromfile;
475 if (QFile::exists(eepromfile)) {
476 found = true;
477 break;
480 entry = entry->me_next;
482 free_file_system_list(firstEntry);
483 #endif
485 if (found)
486 return onlyPath ? temppath : eepromfile;
487 else
488 return QString();