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"
23 #include "eeprominterface.h"
24 #include "process_flash.h"
25 #include "radionotfound.h"
26 #include "burnconfigdialog.h"
28 #include "process_copy.h"
30 #include "progresswidget.h"
32 QString
getRadioInterfaceCmd()
35 Board::Type board
= getCurrentBoard();
36 if (IS_STM32(board
)) {
39 else if (IS_SKY9X(board
)) {
40 return bcd
.getSAMBA();
43 return bcd
.getAVRDUDE();
47 QStringList
getAvrdudeArgs(const QString
& cmd
, const QString
& filename
)
51 QString programmer
= bcd
.getProgrammer();
52 QString mcu
= bcd
.getMCU();
53 Board::Type board
= getCurrentBoard();
55 args
<< "-c" << programmer
<< "-p";
58 else if (board
== Board::BOARD_M128
)
63 args
<< bcd
.getAvrdudeArgs();
65 QString fullcmd
= cmd
+ filename
;
66 if (QFileInfo(filename
).suffix().toUpper() == "HEX")
68 else if (QFileInfo(filename
).suffix().toUpper()=="BIN")
73 args
<< "-U" << fullcmd
;
78 QStringList
getDfuArgs(const QString
& cmd
, const QString
& filename
)
82 args
<< bcd
.getDFUArgs();
83 if (!filename
.endsWith(".dfu"))
84 args
<< "--dfuse-address" << "0x08000000";
86 args
.last().append(":" % QString::number(Boards::getFlashSize(getCurrentBoard())));
87 args
<< "--device" << "0483:df11";
88 args
<< "" << cmd
% filename
;
92 QStringList
getSambaArgs(const QString
& tcl
)
96 QString tclFilename
= generateProcessUniqueTempFileName("temp.tcl");
97 if (QFile::exists(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()));
106 QTextStream
outputStream(&tclFile
);
109 burnConfigDialog bcd
;
110 result
<< bcd
.getSambaPort() << bcd
.getArmMCU() << tclFilename
;
114 QStringList
getReadEEpromCmd(const QString
& filename
)
117 EEPROMInterface
*eepromInterface
= getCurrentEEpromInterface();
118 if (IS_STM32(eepromInterface
->getBoard())) {
121 else if (IS_SKY9X(eepromInterface
->getBoard())) {
122 result
= getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename
+ "\" 0x0 0x80000 0\n");
125 result
= getAvrdudeArgs("eeprom:r:", filename
);
130 QStringList
getWriteEEpromCmd(const QString
& filename
)
132 Board::Type board
= getCurrentBoard();
133 if (IS_STM32(board
)) {
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");
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");
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");
175 return getAvrdudeArgs("flash:r:", filename
);
179 void readAvrdudeFuses(ProgressWidget
* progress
)
181 burnConfigDialog bcd
;
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
);
188 void resetAvrdudeFuses(bool eepromProtect
, ProgressWidget
* progress
)
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();
205 if (bcd
.getMCU() == "m2560") {
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
213 QString tempFile
= generateProcessUniqueTempFileName("ftemp.bin");
215 argread
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << args
<<"-U" << "lfuse:r:"+tempFile
+":r";
216 FlashProcess
flashProcess(bcd
.getAVRDUDE(), argread
, progress
);
218 QFile
file(tempFile
);
219 if (file
.exists() && file
.size()==1) {
220 file
.open(QIODevice::ReadOnly
);
222 file
.read(bin_flash
, 1);
223 if (bin_flash
[0]==0x0E) {
224 lfuses
= "lfuse:w:0x0E:m";
227 lfuses
= "lfuse:w:0x3F:m";
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
;
246 arguments
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << args
<< "-B" << "100" << "-u" << str
;
248 FlashProcess
flashProcess(bcd
.getAVRDUDE(), arguments
, progress
);
254 bool readFirmware(const QString
& filename
, ProgressWidget
* progress
)
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
));
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()) {
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
));
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!"));
322 Storage
inputStorage(radioPath
);
323 if (!inputStorage
.load(radioData
)) {
324 QMessageBox::critical(progress
, QObject::tr("Error"), inputStorage
.error());
327 Storage
outputStorage(filename
);
328 if (!outputStorage
.write(radioData
)) {
329 QMessageBox::critical(progress
, QObject::tr("Error"), outputStorage
.error());
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
);
349 CopyProcess
copyProcess(path
, filename
, progress
);
350 if (!copyProcess
.run()) {
355 if (!IS_STM32(board
)) {
356 FlashProcess
flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename
), progress
);
357 if (!flashProcess
.run()) {
363 return QFileInfo(filename
).exists();
366 bool writeEeprom(const QString
& filename
, ProgressWidget
* progress
)
368 Board::Type board
= getCurrentBoard();
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();
392 RadioNotFoundDialog
dialog(progress
);
399 #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
400 #if defined WIN32 || !defined __GNUC__
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
)
409 QueryDosDevice(volume
.replace("/", "").toLatin1(), szDosDeviceName
, MAX_PATH
);
410 if (strstr(szDosDeviceName
, "\\Floppy") != NULL
) { // it's a floppy
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
)
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
))
431 temppath
= si
.rootPath();
432 eepromfile
= temppath
% "/" % filename
;
433 qDebug() << "Searching for" << eepromfile
;
434 if (QFile::exists(eepromfile
)) {
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
))
445 if (!isRemovableMedia(temppath
)) {
446 blacklist
.append(temppath
);
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))
456 eepromfile
= temppath
% "/" % filename
;
457 qDebug() << "Searching for" << eepromfile
;
458 if (QFile::exists(eepromfile
)) {
464 const static QStringList blacklist
= QStringList() << "/" << "/net" << "/proc" << "/run";
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
)) {
480 entry
= entry
->me_next
;
482 free_file_system_list(firstEntry
);
486 return onlyPath
? temppath
: eepromfile
;