mirror of
https://github.com/radiomanV/TL866.git
synced 2025-06-18 14:25:38 -04:00
830 lines
26 KiB
C++
830 lines
26 KiB
C++
/* Class MainWindow
|
|
*
|
|
* This file is part of the TL866 updater project.
|
|
*
|
|
* Copyright (C) radioman 2014
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
|
* USA.
|
|
*/
|
|
|
|
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
#include "editdialog.h"
|
|
#include "hexwriter.h"
|
|
#include "crc16.h"
|
|
#include <QWidget>
|
|
#include <QFileDialog>
|
|
#include <QMessageBox>
|
|
#include <QResource>
|
|
#include <QFuture>
|
|
|
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
|
QMainWindow(parent),
|
|
ui(new Ui::MainWindow)
|
|
{
|
|
//setup UI
|
|
ui->setupUi(this);
|
|
setFixedSize(size());
|
|
|
|
//initialise object pointers
|
|
advdlg = new AdvDialog(this);
|
|
usbNotifier = new Notifier();
|
|
usb_device = new USB();
|
|
|
|
//initialise used signals
|
|
connect(usbNotifier,SIGNAL(deviceChange(bool)),this,SLOT(DeviceChanged(bool)));
|
|
connect(this, SIGNAL(update_gui(QString, bool, bool)),this,SLOT(gui_updated(QString, bool, bool)));
|
|
connect(this, SIGNAL(reflash_status(bool)), this, SLOT(reflash_finished(bool)));
|
|
connect(this, SIGNAL(dump_status(QString)), this, SLOT(dump_finished(QString)));
|
|
connect(this, SIGNAL(update_progress(int)),ui->progressBar,SLOT(setValue(int)));
|
|
|
|
//set used properties
|
|
this->setProperty("device_code", "");
|
|
this->setProperty("serial_number", "");
|
|
this->setProperty("device_type", 0);
|
|
|
|
//initialise main ui
|
|
ui->btnAdvanced->setEnabled(false);
|
|
ui->btnDump->setEnabled(false);
|
|
leds_off();
|
|
reset_flag=false;
|
|
DeviceChanged(true);
|
|
}
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
if(watcher.isRunning())
|
|
{
|
|
watcher.cancel();
|
|
watcher.waitForFinished();
|
|
}
|
|
delete usb_device;
|
|
delete usbNotifier;
|
|
delete advdlg;
|
|
delete ui;
|
|
}
|
|
|
|
|
|
//Turn off all leds
|
|
void MainWindow::leds_off()
|
|
{
|
|
QPalette pal;
|
|
pal.setColor(QPalette::Background, QColor::fromRgb(0,64,0));
|
|
ui->LedNorm->setPalette(pal);
|
|
ui->LedBoot->setPalette(pal);
|
|
pal.setColor(QPalette::Background, QColor::fromRgb(64,64,0));
|
|
ui->LedErase->setPalette(pal);
|
|
pal.setColor(QPalette::Background, QColor::fromRgb(64,0,0));
|
|
ui->LedWrite->setPalette(pal);
|
|
}
|
|
|
|
/* LEDs on/off toggle routines */
|
|
void MainWindow::setNled(bool state)
|
|
{
|
|
QPalette pal;
|
|
pal.setColor(QPalette::Background,state ? QColor::fromRgb(0,255,0) : QColor::fromRgb(0,64,0));
|
|
ui->LedNorm->setPalette(pal);
|
|
}
|
|
|
|
void MainWindow::setBled(bool state)
|
|
{
|
|
QPalette pal;
|
|
pal.setColor(QPalette::Background,state ? QColor::fromRgb(0,255,0) : QColor::fromRgb(0,64,0));
|
|
ui->LedBoot->setPalette(pal);
|
|
}
|
|
|
|
void MainWindow::setEled(bool state)
|
|
{
|
|
QPalette pal;
|
|
pal.setColor(QPalette::Background,state ? QColor::fromRgb(255,255,0) : QColor::fromRgb(64,64,0));
|
|
ui->LedErase->setPalette(pal);
|
|
}
|
|
|
|
void MainWindow::setWled(bool state)
|
|
{
|
|
QPalette pal;
|
|
pal.setColor(QPalette::Background,state ? QColor::fromRgb(255,0,0) : QColor::fromRgb(64,0,0));
|
|
ui->LedWrite->setPalette(pal);
|
|
}
|
|
|
|
|
|
//simple wait routine
|
|
void MainWindow::wait_ms(unsigned long time)
|
|
{
|
|
QWaitCondition wc;
|
|
QMutex mutex;
|
|
QMutexLocker locker(&mutex);
|
|
wc.wait(&mutex, time);
|
|
}
|
|
|
|
|
|
//browse for update.dat file
|
|
void MainWindow::on_btnInput_clicked()
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(this, "Update.dat" ,NULL, "dat files (*.dat);;All files (*.*)");
|
|
if(fileName.isEmpty())
|
|
return;
|
|
|
|
int ret=firmware.open(fileName);
|
|
if(ret == Firmware::NoError)
|
|
{
|
|
ui->txtInput->setText(fileName);
|
|
ui->lblVersion->setText(QString("[V:%1]").arg(firmware.GetFirmwareVersion()));
|
|
return;
|
|
}
|
|
|
|
switch(ret)
|
|
{
|
|
case Firmware::OpenError:
|
|
QMessageBox::warning(this,"TL866",QString("Cannot read file %1").arg(fileName));
|
|
break;
|
|
case Firmware::FilesizeError:
|
|
QMessageBox::warning(this,"TL866",QString("%1\n\nFilesize error!").arg(fileName));
|
|
break;
|
|
case Firmware::CRCError:
|
|
QMessageBox::warning(this,"TL866",QString("%1\n\nData CRC error!").arg(fileName));
|
|
break;
|
|
case Firmware::DecryptionError:
|
|
QMessageBox::warning(this,"TL866","Firmware decryption error!");
|
|
break;
|
|
}
|
|
ui->lblVersion->clear();
|
|
|
|
}
|
|
|
|
|
|
//show advanced dialog
|
|
void MainWindow::on_btnAdvanced_clicked()
|
|
{
|
|
advdlg->show();
|
|
}
|
|
|
|
|
|
//show edit device code and serial number dialog
|
|
void MainWindow::on_btnEdit_clicked()
|
|
{
|
|
EditDialog* dlg=new EditDialog(this);
|
|
QString devcode =ui->txtDevcode->text();
|
|
QString serial =ui->txtSerial->text();
|
|
dlg->SetText(devcode,serial);
|
|
if(dlg->exec()==QDialog::Accepted)
|
|
{
|
|
dlg->GetResult(&devcode, &serial);
|
|
ui->txtDevcode->setText(devcode);
|
|
ui->txtSerial->setText(serial);
|
|
}
|
|
}
|
|
|
|
|
|
//set default device code and serial number
|
|
void MainWindow::on_btnDefault_clicked()
|
|
{
|
|
set_default(ui->txtDevcode, ui->txtSerial);
|
|
}
|
|
|
|
//public helper function to set default serial and device code
|
|
void MainWindow::set_default(QLineEdit *devcode, QLineEdit *serial)
|
|
{
|
|
unsigned char key[BLOCK_SIZE];
|
|
QByteArray res = get_resource(ui->radiofA->isChecked() ? A_FIRMWARE_RESOURCE : CS_FIRMWARE_RESOURCE, FLASH_SIZE);
|
|
memcpy(key,res.data()+SERIAL_OFFSET,BLOCK_SIZE);
|
|
firmware.decrypt_serial(key, (uchar*)res.data());
|
|
devcode->setText(QString::fromAscii((const char*)&key[0],8));
|
|
serial->setText(QString::fromAscii((const char*)&key[8],24));
|
|
}
|
|
|
|
|
|
//Read device info
|
|
void MainWindow::Refresh()
|
|
{
|
|
reset_flag=false;
|
|
DeviceChanged(true);
|
|
}
|
|
|
|
|
|
//clone serial and device code from connected device
|
|
void MainWindow::on_btnClone_clicked()
|
|
{
|
|
Refresh();
|
|
ui->txtDevcode->setText(this->property("device_code").toString());
|
|
ui->txtSerial->setText(this->property("serial_number").toString());
|
|
}
|
|
|
|
|
|
//reset device button
|
|
void MainWindow::on_btnReset_clicked()
|
|
{
|
|
|
|
if(watcher.isRunning())
|
|
return;
|
|
if(!CheckDevices(this))
|
|
return;
|
|
|
|
if(usb_device->open_device(0))
|
|
{
|
|
reset();
|
|
usb_device->close_device();
|
|
}
|
|
}
|
|
|
|
|
|
//reflash device button
|
|
void MainWindow::on_btnReflash_clicked()
|
|
{
|
|
if(watcher.isRunning())
|
|
return;
|
|
|
|
if(!CheckDevices(this))
|
|
return;
|
|
|
|
if(!firmware.isValid())
|
|
{
|
|
QMessageBox::warning(this, "TL866", "No firmware file loaded!\nPlease load the update.dat file.");
|
|
return;
|
|
}
|
|
|
|
if(QMessageBox::warning(this, "TL866", "Warning! this operation will reflash the device.\nDo you want to continue?",
|
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
|
|
return;
|
|
|
|
int index=-1;
|
|
if(ui->radioA->isChecked())
|
|
index=0;
|
|
if(ui->radioCS->isChecked())
|
|
index=1;
|
|
if(ui->radioDump->isChecked())
|
|
index=2;
|
|
if(index == -1)
|
|
return;
|
|
job_list.clear();
|
|
job_list.append(REFLASH);
|
|
watcher.setProperty("firmware_version", index);
|
|
ui->progressBar->setMaximum(ENCRYPTED_FIRMWARE_SIZE/BLOCK_SIZE-1);
|
|
watcher.setFuture(QtConcurrent::map(job_list, WorkerWrapper(this)));
|
|
}
|
|
|
|
|
|
//dump device button
|
|
void MainWindow::on_btnDump_clicked()
|
|
{
|
|
if(!watcher.isRunning())
|
|
{
|
|
if(!firmware.isValid())
|
|
{
|
|
QMessageBox::warning(this, "TL866", "No firmware file loaded!\nPlease load the update.dat file.");
|
|
return;
|
|
}
|
|
|
|
QString fileName=QFileDialog::getSaveFileName(this,"Save firmware hex file",NULL,"hex files (*.hex);;All files (*.*)");
|
|
if(!fileName.isEmpty())
|
|
{
|
|
job_list.clear();
|
|
job_list.append(DUMP);
|
|
watcher.setProperty("hex_path", fileName);
|
|
ui->progressBar->setMaximum(FLASH_SIZE - 1);
|
|
watcher.setFuture(QtConcurrent::map(job_list, WorkerWrapper(this)));
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//save hex button
|
|
void MainWindow::on_btnSave_clicked()
|
|
{
|
|
QString fileName=QFileDialog::getSaveFileName(this,"Save firmware hex file",NULL,"hex files (*.hex);;All files (*.*)");
|
|
if(!fileName.isEmpty())
|
|
{
|
|
if(!fileName.endsWith(".hex", Qt::CaseInsensitive))
|
|
fileName += ".hex";
|
|
QFile file(fileName);
|
|
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
{
|
|
QMessageBox::critical(this,"TL866",QString("Error creating file %1\n%2.").arg(fileName).arg(file.errorString()));
|
|
return;
|
|
}
|
|
QTextStream fileStream(&file);
|
|
|
|
QByteArray b =get_resource(ui->radiofA->isChecked() ? A_FIRMWARE_RESOURCE : CS_FIRMWARE_RESOURCE, FLASH_SIZE);
|
|
|
|
uchar *temp = new uchar[FLASH_SIZE];//128K byte array
|
|
memcpy(temp,b.data(),FLASH_SIZE);//copy entire firmware to array
|
|
|
|
if(ui->optionBoot->isChecked())
|
|
memset(temp+BOOTLOADER_SIZE, 0xFF, UNENCRYPTED_FIRMWARE_SIZE);//if the option bootloader only is selected then clear the main firmware area(0x1800-0x1FBFF)
|
|
|
|
uchar *key = new uchar[BLOCK_SIZE];//for holding serial and dev code
|
|
firmware.decrypt_serial(key, temp);//decrypt the serial key from temp array to key array
|
|
memset(key,' ',32);//add trailing spaces
|
|
|
|
memcpy(key, ui->txtDevcode->text().toAscii().data(), ui->txtDevcode->text().size());//copy devcode to key array
|
|
memcpy(key+8, ui->txtSerial->text().toAscii().data(), ui->txtSerial->text().size());//copy serial to key array
|
|
|
|
firmware.encrypt_serial(key, temp);//encrypt the devcode and serial
|
|
memcpy(temp + SERIAL_OFFSET ,key,BLOCK_SIZE);//copy the new devcode and serial to temp array
|
|
|
|
HexWriter *hexwriter = new HexWriter;
|
|
hexwriter->WriteHex(fileStream,temp,FLASH_SIZE);//write temp array to fileStream in Intel hex format
|
|
delete hexwriter;
|
|
delete key;
|
|
delete temp;
|
|
file.close();//done!
|
|
}
|
|
}
|
|
|
|
//Helper function to get a binary resource
|
|
QByteArray MainWindow::get_resource(QString resource_path, int size)
|
|
{
|
|
QResource res(resource_path);
|
|
QByteArray ba;
|
|
ba = (res.isCompressed() ? qUncompress(res.data(), size) : QByteArray((const char*)res.data(), size));
|
|
return ba;
|
|
}
|
|
|
|
|
|
//Background worker dispatch routine. Notice that this function is executed in a separate thread.
|
|
void MainWindow::DoWork(WorkerJob job)
|
|
{
|
|
switch(job)
|
|
{
|
|
case REFLASH:
|
|
if(usb_device->open_device(0))
|
|
{
|
|
bool success =reflash();
|
|
usb_device->close_device();
|
|
emit reflash_status(success);
|
|
}
|
|
break;
|
|
|
|
case DUMP:
|
|
if(usb_device->open_device(0))
|
|
{
|
|
QString result = dump();
|
|
usb_device->close_device();
|
|
emit dump_status(result);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//Send the Reset command to the TL866.
|
|
void MainWindow::reset()
|
|
{
|
|
|
|
uchar data[4] = {RESET_COMMAND, 0, 0, 0};
|
|
reset_flag=true;
|
|
usb_device->usb_write(data, 4);
|
|
}
|
|
|
|
//Reflash function. This routine is executed in a separate thread.
|
|
bool MainWindow::reflash()
|
|
{
|
|
uchar buffer[BLOCK_SIZE+7];
|
|
uchar data[ENCRYPTED_FIRMWARE_SIZE];
|
|
|
|
TL866_REPORT report;
|
|
|
|
//read the device to determine his satus
|
|
memset((uchar*)&report,0, sizeof(TL866_REPORT));
|
|
report.echo = REPORT_COMMAND;//0 anyway
|
|
usb_device->usb_write((uchar *)&report, 5);
|
|
usb_device->usb_read((uchar*)&report, sizeof(TL866_REPORT));
|
|
if(report.device_status == NORMAL_MODE)//if the device is not in bootloader mode reset it.
|
|
{
|
|
reset();
|
|
wait_ms(2500);
|
|
}
|
|
|
|
//read the device again to see the true device version as reported by the bootloader
|
|
memset((uchar*)&report,0, sizeof(TL866_REPORT));
|
|
report.echo = REPORT_COMMAND;//0 anyway
|
|
usb_device->usb_write((uchar *)&report, 5);
|
|
usb_device->usb_read((uchar*)&report, sizeof(TL866_REPORT));
|
|
int device_version = report.device_version;
|
|
|
|
|
|
//Erase device first
|
|
memset(buffer,0,20);
|
|
buffer[0]=ERASE_COMMAND;
|
|
buffer[7]=firmware.GetEraseParammeter(device_version);
|
|
emit update_gui(QString("<erasing...>"), true, false);
|
|
usb_device->usb_write(buffer, 20);
|
|
|
|
usb_device->usb_read(data, 32);
|
|
if(data[0] != ERASE_COMMAND)
|
|
return false;//erase failed
|
|
|
|
//Write device.
|
|
emit update_gui(QString("<erasing...>"), false, false);
|
|
wait_ms(1000);
|
|
emit update_gui(QString("<writing...>"), false, true);
|
|
|
|
|
|
//Get the encrypted firmware.
|
|
switch(watcher.property("firmware_version").toInt())
|
|
{
|
|
case FIRMWARE_A:
|
|
default:
|
|
firmware.get_firmware(data, device_version, Firmware::A_KEY);
|
|
break;
|
|
case FIRMWARE_CS:
|
|
firmware.get_firmware(data, device_version, Firmware::CS_KEY);
|
|
break;
|
|
case FIRMWARE_CUSTOM:
|
|
QByteArray b = get_resource(DUMPER_RESOURCE, UNENCRYPTED_FIRMWARE_SIZE);
|
|
firmware.encrypt_firmware((const uchar*)b.data(), data, device_version);
|
|
}
|
|
|
|
|
|
/* prepare data by adding 7 bytes header on each 80 bytes data block and send it over the usb.
|
|
*
|
|
* 7 bytes header+80 bytes data like this: | Command | lenght | address | data |
|
|
* 2bytes 2bytes 3bytes 80bytes
|
|
*
|
|
*/
|
|
|
|
quint32 address = BOOTLOADER_SIZE;
|
|
for (int i = 0; i<ENCRYPTED_FIRMWARE_SIZE; i += BLOCK_SIZE)
|
|
{
|
|
buffer[0] = WRITE_COMMAND;//command LSB
|
|
buffer[1] = 0x00;//command MSB
|
|
buffer[2] = BLOCK_SIZE;//Block size without header(LSB)
|
|
buffer[3] = 0x00;//Block size MSB
|
|
buffer[4] = address & 0xff;//24 bit address which will be written (3 bytes in little endian order)
|
|
buffer[5] = (address & 0xff00)>>8;
|
|
buffer[6] = (address & 0xff0000)>>16;
|
|
memcpy(&buffer[7], &data[i], BLOCK_SIZE);
|
|
|
|
if (usb_device->usb_write(buffer, sizeof(buffer)) != sizeof(buffer))
|
|
return false;//write failed
|
|
address+=64;//next data block
|
|
emit update_progress(i/BLOCK_SIZE);
|
|
}
|
|
|
|
//Reset the device back in normal working mode
|
|
emit update_gui(QString("<writing...>"), false, false);
|
|
wait_ms(500);
|
|
emit update_gui(QString("<resetting...>"), false, false);
|
|
reset();
|
|
wait_ms(1500);
|
|
return true;//reflash ok
|
|
|
|
}
|
|
|
|
//Dump function. This function is executed in separate thread.
|
|
QString MainWindow::dump()
|
|
{
|
|
uchar temp[FLASH_SIZE];//128Kbyte buffer
|
|
uchar w[5];
|
|
QFile file(watcher.property("hex_path").toString());
|
|
|
|
if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
|
|
return file.errorString();
|
|
|
|
QTextStream fileStream(&file);
|
|
|
|
for(int i = 0; i < FLASH_SIZE; i += 64)
|
|
{
|
|
w[0] = DUMPER_READ_FLASH;
|
|
w[1] = 64;//packet size
|
|
w[2] = i & 0xff;//24bit address in little endian order
|
|
w[3] = (i & 0xff00)>>8;
|
|
w[4] = (i & 0xff0000)>>16;
|
|
|
|
usb_device->usb_write(w, sizeof(w));
|
|
if(usb_device->usb_read(&temp[i],64) != 64)
|
|
return QString("USB read error.");
|
|
emit update_progress(i);
|
|
}
|
|
|
|
firmware.decrypt_firmware(&temp[BOOTLOADER_SIZE], this->property("device_type").toInt());
|
|
|
|
HexWriter *hexwriter = new HexWriter;
|
|
hexwriter->WriteHex(fileStream,temp,sizeof(temp));//write temp array to fileStream in Intel hex format.
|
|
delete hexwriter;
|
|
file.close();
|
|
return QString("");
|
|
}
|
|
|
|
|
|
|
|
//Reflash finished SLOT
|
|
void MainWindow::reflash_finished(bool success)
|
|
{
|
|
if(success)
|
|
QMessageBox::information(this, "TL866", "Reflash OK!");
|
|
else
|
|
QMessageBox::critical(this, "TL866", "Reflash Failed!");
|
|
emit update_progress(0);
|
|
}
|
|
|
|
|
|
//Dump finished SLOT
|
|
void MainWindow::dump_finished(QString result)
|
|
{
|
|
if(result.isEmpty())
|
|
QMessageBox::information(this, "TL866", "Firmware dump complete!");
|
|
else
|
|
QMessageBox::critical(this, "TL866", QString("Error creating dump.hex file\n%1.").arg(result));
|
|
emit update_progress(0);
|
|
}
|
|
|
|
|
|
//Gui update SLOT
|
|
void MainWindow::gui_updated(QString message, bool eraseLed, bool writeLed)
|
|
{
|
|
setEled(eraseLed);
|
|
setWled(writeLed);
|
|
QStringList list(ui->txtInfo->toPlainText().split("\n"));
|
|
list.removeAt(1);
|
|
list.insert(1,"Device status: Bootloader mode " + message);
|
|
ui->txtInfo->clear();
|
|
ui->txtInfo->append(list.join("\n"));
|
|
}
|
|
|
|
|
|
//This procedure is called automatically by the usb device change. Call this function to refresh the info.
|
|
void MainWindow::DeviceChanged(bool arrived)
|
|
{
|
|
if(!arrived && reset_flag)//ignore unplug event if the device was resetted by us.
|
|
return;
|
|
|
|
reset_flag=false;
|
|
ui->txtInfo->clear();
|
|
int devtype = 0;
|
|
int count=usb_device->get_devices_count();
|
|
this->setWindowTitle(QString("TL866 firmware updater (%1 %2 connected)").arg(count).arg(count == 1 ? "device" : "devices"));
|
|
|
|
if(count)
|
|
{
|
|
TL866_REPORT report;
|
|
if(usb_device->open_device(0))
|
|
{
|
|
memset((uchar*)&report,0, sizeof(TL866_REPORT));
|
|
report.echo = REPORT_COMMAND;//0 anyway
|
|
usb_device->usb_write((uchar *)&report, 5);
|
|
usb_device->usb_read((uchar*)&report, sizeof(TL866_REPORT));
|
|
|
|
switch(report.device_version)
|
|
{
|
|
case VERSION_TL866A:
|
|
ui->txtInfo->append("Device version: TL866A");
|
|
devtype = VERSION_TL866A;
|
|
break;
|
|
case VERSION_TL866CS:
|
|
ui->txtInfo->append("Device version: TL866CS");
|
|
devtype = VERSION_TL866CS;
|
|
break;
|
|
default:
|
|
ui->txtInfo->append("Device version: Unknown");
|
|
devtype = 0;
|
|
}
|
|
|
|
|
|
switch(report.device_status)
|
|
{
|
|
case NORMAL_MODE:
|
|
setNled(true);
|
|
setBled(false);
|
|
ui->txtInfo->append("Device status: Normal working mode.");
|
|
break;
|
|
case BOOTLOADER_MODE:
|
|
setNled(false);
|
|
setBled(true);
|
|
ui->txtInfo->append("Device status: Bootloader mode <waiting for update.>");
|
|
break;
|
|
default:
|
|
setNled(false);
|
|
setBled(false);
|
|
ui->txtInfo->append("Device status: Unknown.");
|
|
}
|
|
QString s_devcode = (QString::fromAscii((const char*)&report.device_code,8));
|
|
QString s_serial = (QString::fromAscii((const char*)&report.serial_number,24));
|
|
bool isDumperActive = (s_devcode.toLower() == "codedump" && s_serial == "000000000000000000000000");
|
|
|
|
if(isDumperActive)
|
|
{
|
|
|
|
DUMPER_REPORT dumper_report;
|
|
uchar b[] = {DUMPER_INFO};
|
|
usb_device->usb_write(b, 1);
|
|
usb_device->usb_read((uchar*)&dumper_report, sizeof(DUMPER_REPORT));
|
|
devtype = dumper_report.bootloader_version;
|
|
|
|
s_devcode = (QString::fromAscii((const char*)&dumper_report.device_code,8));
|
|
s_serial = (QString::fromAscii((const char*)&dumper_report.serial_number,24));
|
|
|
|
advdlg->SetSerial(s_devcode, s_serial);
|
|
|
|
QString info;
|
|
info.append(QString("Device code: %1\n").arg(s_devcode));
|
|
info.append(QString("Serial number: %1\n").arg(s_serial));
|
|
info.append(QString("Bootloader version: %1\n").arg((devtype == VERSION_TL866A) ? "A" : "CS"));
|
|
info.append(QString("Code Protection bit: %1\n").arg(dumper_report.cp_bit ? "No" : "Yes"));
|
|
|
|
advdlg->SetInfo(info);
|
|
advdlg->SetUi(dumper_report.cp_bit == 0, devtype);
|
|
|
|
ui->btnAdvanced->setEnabled(true);
|
|
ui->btnDump->setEnabled(true);
|
|
}
|
|
else//dumper is not active
|
|
{
|
|
ui->btnAdvanced->setEnabled(false);
|
|
ui->btnDump->setEnabled(false);
|
|
advdlg->SetSerial("", "");
|
|
advdlg->SetInfo("");
|
|
advdlg->hide();
|
|
}
|
|
|
|
ui->txtInfo->append("Device code: " + s_devcode);
|
|
ui->txtInfo->append("Serial number: " + s_serial);
|
|
this->setProperty("device_code", s_devcode);
|
|
this->setProperty("serial_number", s_serial);
|
|
ui->txtInfo->append(isDumperActive ? "Firmware version: Firmware dumper" :
|
|
report.device_status == NORMAL_MODE ? QString("Firmware version: %1.%2.%3")
|
|
.arg(report.hardware_version)
|
|
.arg(report.firmware_version_major)
|
|
.arg(report.firmware_version_minor):
|
|
"Firmware version: Bootloader");
|
|
|
|
if(!watcher.isRunning())
|
|
usb_device->close_device();//do not close device if an upgrade is in progress.
|
|
}
|
|
else//error oppening device
|
|
SetBlank();
|
|
|
|
}
|
|
else//no device connected
|
|
SetBlank();
|
|
|
|
this->setProperty("device_type", devtype);//save global property for later usage.
|
|
}
|
|
|
|
|
|
//Helper function
|
|
void MainWindow::SetBlank()
|
|
{
|
|
leds_off();
|
|
ui->btnAdvanced->setEnabled(false);
|
|
ui->btnDump->setEnabled(false);
|
|
//ui->txtDevcode->setText("");
|
|
//ui->txtSerial->setText("");
|
|
this->setProperty("device_code", "");
|
|
this->setProperty("serial_number", "");
|
|
advdlg->SetSerial("", "");
|
|
advdlg->SetInfo("");
|
|
advdlg->hide();
|
|
}
|
|
|
|
//Helper function
|
|
bool MainWindow::CheckDevices(QWidget *parent)
|
|
{
|
|
if (usb_device->get_devices_count() == 0)
|
|
{
|
|
QMessageBox::warning(parent, "TL866", "No device detected!\nPlease connect one and try again.");
|
|
return false;
|
|
}
|
|
if (usb_device->get_devices_count() > 1)
|
|
{
|
|
QMessageBox::warning(parent, "TL866", "Multiple devices detected!\nPlease connect only one device.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*Advanced functions*/
|
|
|
|
|
|
//Write bootloader
|
|
void MainWindow::WriteBootloader(BootloaderType type)
|
|
{
|
|
if(!CheckDevices(advdlg))
|
|
return;
|
|
if(!AdvQuestion())
|
|
return;
|
|
if(usb_device->open_device(0))
|
|
{
|
|
ushort crc = BootloaderCRC();
|
|
if(!((crc == A_BOOTLOADER_CRC) || (crc == CS_BOOTLOADER_CRC)))
|
|
{
|
|
usb_device->close_device();
|
|
QMessageBox::warning(advdlg, "TL866",
|
|
"The bootloader CRC of your device version doesn't match!\nAs a safety measure, nothing will be written.");
|
|
return;
|
|
}
|
|
uchar b[2]={DUMPER_WRITE_BOOTLOADER, type == A_BOOTLOADER ? VERSION_TL866A : VERSION_TL866CS};
|
|
usb_device->usb_write(b, 2);
|
|
b[0] = 0;
|
|
usb_device->usb_read(b, 1);
|
|
usb_device->close_device();
|
|
Refresh();
|
|
if(b[0] == DUMPER_WRITE_BOOTLOADER)
|
|
QMessageBox::information(advdlg, "TL866", "Bootloader was successfully written.");
|
|
else
|
|
QMessageBox::critical(advdlg, "TL866", "Bootloader writing failed.");
|
|
}
|
|
}
|
|
|
|
|
|
//write copy protect bit
|
|
void MainWindow::WriteConfig(bool copy_protect)
|
|
{
|
|
if(!CheckDevices(advdlg))
|
|
return;
|
|
if(!AdvQuestion())
|
|
return;
|
|
if(usb_device->open_device(0))
|
|
{
|
|
uchar b[2]={DUMPER_WRITE_CONFIG, (uchar)(copy_protect ? 1 : 0)};
|
|
usb_device->usb_write(b, 2);
|
|
b[0] = 0;
|
|
usb_device->usb_read(b, 1);
|
|
usb_device->close_device();
|
|
Refresh();
|
|
if(b[0] == DUMPER_WRITE_CONFIG)
|
|
QMessageBox::information(advdlg, "TL866", "Code protection bit was successfully written.");
|
|
else
|
|
QMessageBox::critical(advdlg, "TL866", "Writing code protect bit failed.");
|
|
}
|
|
|
|
}
|
|
|
|
//write serial number and device code
|
|
void MainWindow::WriteInfo(QString device_code, QString serial_number)
|
|
{
|
|
if(!CheckDevices(advdlg))
|
|
return;
|
|
if(!AdvQuestion())
|
|
return;
|
|
if(usb_device->open_device(0))
|
|
{
|
|
uchar b[34];
|
|
memset(b,' ',34);//add trailing spaces
|
|
b[0] = DUMPER_WRITE_INFO;
|
|
memcpy(b+1, device_code.toAscii().data(), device_code.size());//copy devcode to b array
|
|
memcpy(b+9, serial_number.toAscii().data(), serial_number.size());//copy serial to key array
|
|
usb_device->usb_write(b, 34);
|
|
b[0] = 0;
|
|
usb_device->usb_read(b, 1);
|
|
usb_device->close_device();
|
|
Refresh();
|
|
if(b[0] == DUMPER_WRITE_INFO)
|
|
QMessageBox::information(advdlg, "TL866", "Device info was successfully written.");
|
|
else
|
|
QMessageBox::critical(advdlg, "TL866", "Writing device info failed.");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//read bootloader and compute crc16
|
|
ushort MainWindow::BootloaderCRC()
|
|
{
|
|
uchar buffer[BOOTLOADER_SIZE];//6Kbyte
|
|
uchar w[5];
|
|
|
|
for(int i=0;i<BOOTLOADER_SIZE;i+=64)
|
|
{
|
|
w[0]=DUMPER_READ_FLASH;
|
|
w[1]=64;//packet size
|
|
w[2]=i & 0xff;//24bit address in little endian order
|
|
w[3]=(i & 0xff00)>>8;
|
|
w[4]=(i & 0xff0000)>>16;
|
|
usb_device->usb_write(w, sizeof(w));
|
|
if(usb_device->usb_read(&buffer[i],64) != 64)
|
|
{
|
|
QMessageBox::warning(advdlg, "TL866", "USB read error!");
|
|
return 0;
|
|
}
|
|
}
|
|
return CRC16::crc16(buffer, sizeof(buffer));
|
|
}
|
|
|
|
|
|
//helper function
|
|
bool MainWindow::AdvQuestion()
|
|
{
|
|
return(QMessageBox::warning(advdlg, "TL866", "Warning! this operation may brick your device.\nDo you want to continue?",
|
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes);
|
|
}
|