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_9X_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
, CPN_STR_TTL_ERROR
,
103 QCoreApplication::translate("RadioInterface", "Cannot write file %1:\n%2.").arg(tclFilename
).arg(tclFile
.errorString()));
107 QTextStream
outputStream(&tclFile
);
110 burnConfigDialog bcd
;
111 result
<< bcd
.getSambaPort() << bcd
.getArmMCU() << tclFilename
;
115 QStringList
getReadEEpromCmd(const QString
& filename
)
118 EEPROMInterface
*eepromInterface
= getCurrentEEpromInterface();
119 if (IS_STM32(eepromInterface
->getBoard())) {
122 else if (IS_SKY9X(eepromInterface
->getBoard())) {
123 result
= getSambaArgs(QString("SERIALFLASH::Init 0\n") + "receive_file {SerialFlash AT25} \"" + filename
+ "\" 0x0 0x80000 0\n");
126 result
= getAvrdudeArgs("eeprom:r:", filename
);
131 QStringList
getWriteEEpromCmd(const QString
& filename
)
133 Board::Type board
= getCurrentBoard();
134 if (IS_STM32(board
)) {
136 return QStringList();
138 else if (IS_SKY9X(board
)) {
139 return getSambaArgs(QString("SERIALFLASH::Init 0\n") + "send_file {SerialFlash AT25} \"" + filename
+ "\" 0x0 0\n");
142 return getAvrdudeArgs("eeprom:w:", filename
);
146 QStringList
getWriteFirmwareArgs(const QString
& filename
)
148 Board::Type board
= getCurrentBoard();
149 if (IS_STM32(board
)) {
150 return getDfuArgs("-D", filename
);
152 else if (board
== Board::BOARD_SKY9X
) {
153 return getSambaArgs(QString("send_file {Flash} \"") + filename
+ "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n");
155 else if (board
== Board::BOARD_9XRPRO
|| board
== Board::BOARD_AR9X
) {
156 return getSambaArgs(QString("send_file {Flash} \"") + filename
+ "\" 0x400000 0\n" + "FLASH::ScriptGPNMV 2\n");
159 return getAvrdudeArgs("flash:w:", filename
);
163 QStringList
getReadFirmwareArgs(const QString
& filename
)
165 Board::Type board
= getCurrentBoard();
166 if (IS_STM32(board
)) {
167 return getDfuArgs("-U", filename
);
169 else if (board
== Board::BOARD_SKY9X
) {
170 return getSambaArgs(QString("receive_file {Flash} \"") + filename
+ "\" 0x400000 0x40000 0\n");
172 else if (board
== Board::BOARD_9XRPRO
) {
173 return getSambaArgs(QString("receive_file {Flash} \"") + filename
+ "\" 0x400000 0x80000 0\n");
176 return getAvrdudeArgs("flash:r:", filename
);
180 void readAvrdudeFuses(ProgressWidget
* progress
)
182 burnConfigDialog bcd
;
184 args
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << bcd
.getAvrdudeArgs() << "-U" << "lfuse:r:-:i" << "-U" << "hfuse:r:-:i" << "-U" << "efuse:r:-:i";
185 FlashProcess
flashProcess(bcd
.getAVRDUDE(), args
, progress
);
189 void resetAvrdudeFuses(bool eepromProtect
, ProgressWidget
* progress
)
192 //avrdude -c usbasp -p m64 -U lfuse:w:<0x0E>:m
193 //avrdude -c usbasp -p m64 -U hfuse:w:<0x89>:m 0x81 for eeprom protection
194 //avrdude -c usbasp -p m64 -U efuse:w:<0xFF>:m
196 burnConfigDialog bcd
;
197 QMessageBox::StandardButton ret
= QMessageBox::No
;
198 ret
= QMessageBox::warning(NULL
, CPN_STR_APP_NAME
,
199 QCoreApplication::translate("RadioInterface",
200 "<b><u>WARNING!</u></b>" \
201 "<br>This will reset the fuses of %1 to the factory settings.<br>"
202 "Writing fuses can mess up your radio.<br>Do this only if you are sure they are wrong!<br>"
203 "Are you sure you want to continue?").arg(bcd
.getMCU()),
204 QMessageBox::Yes
| QMessageBox::No
);
205 if (ret
== QMessageBox::Yes
) {
206 QStringList args
= bcd
.getAvrdudeArgs();
208 if (bcd
.getMCU() == "m2560") {
210 QString erStr
= eepromProtect
? "hfuse:w:0x51:m" : "hfuse:w:0x59:m";
211 str
<< "-U" << "lfuse:w:0xD7:m" << "-U" << erStr
<< "-U" << "efuse:w:0xFC:m";
212 //use hfuse = 0x81 to prevent eeprom being erased with every flashing
216 QString tempFile
= generateProcessUniqueTempFileName("ftemp.bin");
218 argread
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << args
<<"-U" << "lfuse:r:"+tempFile
+":r";
219 FlashProcess
flashProcess(bcd
.getAVRDUDE(), argread
, progress
);
221 QFile
file(tempFile
);
222 if (file
.exists() && file
.size()==1) {
223 file
.open(QIODevice::ReadOnly
);
225 file
.read(bin_flash
, 1);
226 if (bin_flash
[0]==0x0E) {
227 lfuses
= "lfuse:w:0x0E:m";
230 lfuses
= "lfuse:w:0x3F:m";
236 lfuses
= "lfuse:w:0x3F:m";
239 QString erStr
= eepromProtect
? "hfuse:w:0xC1:m" : "hfuse:w:0xC9:m";
240 str
<< "-U" << lfuses
<< "-U" << erStr
<< "-U" << "efuse:w:0xFF:m";
241 //use hfuse = 0x81 to prevent eeprom being erased with every flashing
244 QStringList arguments
;
245 if (bcd
.getMCU() == "m2560") {
246 arguments
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << args
<< "-u" << str
;
249 arguments
<< "-c" << bcd
.getProgrammer() << "-p" << bcd
.getMCU() << args
<< "-B" << "100" << "-u" << str
;
251 FlashProcess
flashProcess(bcd
.getAVRDUDE(), arguments
, progress
);
257 bool readFirmware(const QString
& filename
, ProgressWidget
* progress
)
261 QFile
file(filename
);
262 if (file
.exists() && !file
.remove()) {
263 QMessageBox::warning(NULL
, CPN_STR_TTL_ERROR
,
264 QCoreApplication::translate("RadioInterface", "Could not delete temporary file: %1").arg(filename
));
268 if (IS_ARM(getCurrentBoard())) {
269 QString path
= findMassstoragePath("FIRMWARE.BIN");
270 if (!path
.isEmpty()) {
271 qDebug() << "readFirmware: reading" << path
<< "into" << filename
;
272 CopyProcess
copyProcess(path
, filename
, progress
);
273 result
= copyProcess
.run();
277 if (result
== false) {
278 qDebug() << "readFirmware: reading" << filename
<< "with" << getRadioInterfaceCmd() << getReadFirmwareArgs(filename
);
279 FlashProcess
flashProcess(getRadioInterfaceCmd(), getReadFirmwareArgs(filename
), progress
);
280 result
= flashProcess
.run();
283 if (!QFileInfo(filename
).exists()) {
290 bool writeFirmware(const QString
& filename
, ProgressWidget
* progress
)
292 if (IS_ARM(getCurrentBoard())) {
293 QString path
= findMassstoragePath("FIRMWARE.BIN");
294 if (!path
.isEmpty()) {
295 qDebug() << "writeFirmware: writing" << path
<< "from" << filename
;
296 CopyProcess
copyProcess(filename
, path
, progress
);
297 return copyProcess
.run();
301 qDebug() << "writeFirmware: writing" << filename
<< "with" << getRadioInterfaceCmd() << getWriteFirmwareArgs(filename
);
302 FlashProcess
flashProcess(getRadioInterfaceCmd(), getWriteFirmwareArgs(filename
), progress
);
303 return flashProcess
.run();
306 bool readEeprom(const QString
& filename
, ProgressWidget
* progress
)
308 Board::Type board
= getCurrentBoard();
310 QFile
file(filename
);
311 if (file
.exists() && !file
.remove()) {
312 QMessageBox::warning(NULL
, CPN_STR_TTL_ERROR
,
313 QCoreApplication::translate("RadioInterface", "Could not delete temporary file: %1").arg(filename
));
317 if (IS_HORUS(board
)) {
318 QString radioPath
= findMassstoragePath("RADIO", true);
319 qDebug() << "Searching for SD card, found" << radioPath
;
320 if (radioPath
.isEmpty()) {
321 QMessageBox::critical(progress
, CPN_STR_TTL_ERROR
,
322 QCoreApplication::translate("RadioInterface", "Unable to find Horus radio SD card!"));
326 Storage
inputStorage(radioPath
);
327 if (!inputStorage
.load(radioData
)) {
328 QMessageBox::critical(progress
, CPN_STR_TTL_ERROR
, inputStorage
.error());
331 Storage
outputStorage(filename
);
332 if (!outputStorage
.write(radioData
)) {
333 QMessageBox::critical(progress
, CPN_STR_TTL_ERROR
, outputStorage
.error());
337 if (getCurrentBoard() == Board::BOARD_JUMPER_T16
&& inputStorage
.getBoard() == Board::BOARD_X10
) {
338 if (displayT16ImportWarning() == false)
344 QString path
= findMassstoragePath("EEPROM.BIN");
345 if (path
.isEmpty()) {
346 // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
347 path
= findMassstoragePath("TARANIS.BIN");
349 if (path
.isEmpty()) {
350 // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
351 path
= findMassstoragePath("ERSKY9X.BIN");
353 if (path
.isEmpty()) {
354 RadioNotFoundDialog
dialog(progress
);
358 CopyProcess
copyProcess(path
, filename
, progress
);
359 if (!copyProcess
.run()) {
364 if (!IS_STM32(board
)) {
365 FlashProcess
flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename
), progress
);
366 if (!flashProcess
.run()) {
372 return QFileInfo(filename
).exists();
375 bool writeEeprom(const QString
& filename
, ProgressWidget
* progress
)
377 Board::Type board
= getCurrentBoard();
380 QString path
= findMassstoragePath("EEPROM.BIN");
381 if (path
.isEmpty()) {
382 // On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
383 path
= findMassstoragePath("TARANIS.BIN");
385 if (path
.isEmpty()) {
386 // Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
387 path
= findMassstoragePath("ERSKY9X.BIN");
389 if (!path
.isEmpty()) {
390 CopyProcess
copyProcess(filename
, path
, progress
);
391 return copyProcess
.run();
395 if (!IS_TARANIS(board
)) {
396 FlashProcess
flashProcess(getRadioInterfaceCmd(), getWriteEEpromCmd(filename
), progress
);
397 return flashProcess
.run();
401 RadioNotFoundDialog
dialog(progress
);
408 #if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
409 #if defined WIN32 || !defined __GNUC__
411 bool isRemovableMedia(const QString
& vol
)
413 char szDosDeviceName
[MAX_PATH
];
414 QString volume
= vol
;
415 UINT driveType
= GetDriveType(volume
.replace("/", "\\").toLatin1());
416 if (driveType
!= DRIVE_REMOVABLE
)
418 QueryDosDevice(volume
.replace("/", "").toLatin1(), szDosDeviceName
, MAX_PATH
);
419 if (strstr(szDosDeviceName
, "\\Floppy") != NULL
) { // it's a floppy
425 #include "mountlist.h"
426 #endif // defined WIN32 || !defined __GNUC__
427 #endif // (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
429 QString
findMassstoragePath(const QString
& filename
, bool onlyPath
)
434 QRegularExpression
fstypeRe("^(v?fat|msdos)", QRegularExpression::CaseInsensitiveOption
); // Linux: "vfat"; macOS: "msdos"; Win: "FAT32"
435 #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
436 foreach(const QStorageInfo
& si
, QStorageInfo::mountedVolumes()) {
437 //qDebug() << si.rootPath() << si.name() << si.device() << si.displayName() << si.fileSystemType() << si.isReady();
438 if (!si
.isReady() || !QString(si
.fileSystemType()).contains(fstypeRe
))
440 temppath
= si
.rootPath();
441 eepromfile
= temppath
% "/" % filename
;
442 qDebug() << "Searching for" << eepromfile
;
443 if (QFile::exists(eepromfile
)) {
448 #elif defined WIN32 || !defined __GNUC__
449 static QStringList blacklist
;
450 foreach(const QFileInfo
& drive
, QDir::drives()) {
451 temppath
= drive
.absolutePath();
452 if (blacklist
.contains(temppath
))
454 if (!isRemovableMedia(temppath
)) {
455 blacklist
.append(temppath
);
458 WCHAR szVolumeName
[256] ;
459 WCHAR szFileSystemName
[256];
460 DWORD dwSerialNumber
= 0;
461 DWORD dwMaxFileNameLength
=256;
462 DWORD dwFileSystemFlags
=0;
463 if (!GetVolumeInformationW( (WCHAR
*) drive
.absolutePath().utf16(),szVolumeName
,256,&dwSerialNumber
,&dwMaxFileNameLength
,&dwFileSystemFlags
,szFileSystemName
,256))
465 eepromfile
= temppath
% "/" % filename
;
466 qDebug() << "Searching for" << eepromfile
;
467 if (QFile::exists(eepromfile
)) {
473 const static QStringList blacklist
= QStringList() << "/" << "/net" << "/proc" << "/run";
475 struct mount_entry
*entry
;
476 struct mount_entry
*firstEntry
;
477 firstEntry
= entry
= read_file_system_list(true);
478 while (entry
!= NULL
) {
479 temppath
= entry
->me_mountdir
;
480 if (!drives
.contains(entry
->me_devname
) && !blacklist
.contains(temppath
) && QString(entry
->me_type
).contains(fstypeRe
)) {
481 drives
.append(entry
->me_devname
);
482 eepromfile
= temppath
% "/" % filename
;
483 qDebug() << "Searching for" << eepromfile
;
484 if (QFile::exists(eepromfile
)) {
489 entry
= entry
->me_next
;
491 free_file_system_list(firstEntry
);
495 return onlyPath
? temppath
: eepromfile
;