newrpl/usbselector.cpp

818 lines
22 KiB
C++
Raw Normal View History

2019-04-25 15:30:25 +02:00
#include <QMessageBox>
2017-11-29 00:16:29 +01:00
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QTreeWidgetItemIterator>
2017-11-29 00:16:29 +01:00
#include <QTimer>
#include <QStandardPaths>
#include <QFileDialog>
#include <QCloseEvent>
//#include "string.h"
#include "hidapi.h"
#include "usbselector.h"
#include "ui_usbselector.h"
// ONLY REQUIRED UNDER MINGW
#ifdef DrawText
#undef DrawText
#endif
#define WORD _WORD
2017-11-28 00:54:11 +01:00
extern "C" {
#include "newrpl.h"
#include "libraries.h"
2017-11-30 19:23:09 +01:00
extern hid_device *__usb_curdevice;
extern char __usb_devicepath[8192];
extern volatile int __usb_paused;
int __fwupdate_progress;
int __fwupdate_address;
int __fwupdate_nwords;
BYTEPTR __fwupdate_buffer;
2017-11-30 19:23:09 +01:00
2017-11-28 00:54:11 +01:00
BINT64 rplObjChecksum(WORDPTR object);
}
USBSelector::USBSelector(QWidget *parent) :
QDialog(parent),
2019-05-18 00:00:33 +02:00
update_thread(this),
ui(new Ui::USBSelector)
{
ui->setupUi(this);
2017-11-30 19:23:09 +01:00
SelectedDevicePath.clear();
SelectedDeviceName.clear();
ui->updateFirmware->hide();
ui->updateProgress->hide();
ui->USBtreeWidget->clear();
ui->USBtreeWidget->hideColumn(1);
ui->USBtreeWidget->hideColumn(3);
ui->USBtreeWidget->hideColumn(4);
2019-05-18 00:00:33 +02:00
norefresh=false;
2019-05-10 23:50:19 +02:00
QTimer::singleShot(200,this,SLOT(refresh()));
2017-11-29 00:16:29 +01:00
}
USBSelector::~USBSelector()
{
2017-11-30 00:48:44 +01:00
2017-11-29 00:16:29 +01:00
delete ui;
}
void USBSelector::closeEvent(QCloseEvent *event)
{
if(!update_thread.isRunning()) event->accept();
else event->ignore();
}
void USBSelector::reject()
{
if(!update_thread.isRunning()) QDialog::reject();
}
2017-11-29 00:16:29 +01:00
void USBSelector::on_USBtreeWidget_itemSelectionChanged()
{
QString result;
result.clear();
QTreeWidgetItem *newitem;
if(ui->USBtreeWidget->selectedItems().count()>=1) newitem=ui->USBtreeWidget->selectedItems().first();
else {
return;
}
if(newitem->text(2)==QString("[Device not responding]")) {
ui->buttonBox->setStandardButtons( QDialogButtonBox::Cancel);
SelectedDevicePath.clear();
2017-12-14 19:12:35 +01:00
SelectedDeviceName.clear();
ui->selectedCalc->setText(QString("No device selected."));
ui->updateFirmware->hide();
}
else {
ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
SelectedDevicePath=newitem->text(3);
2017-12-14 19:12:35 +01:00
SelectedDeviceName=newitem->text(0)+QString("[build ")+newitem->text(2).right(4)+QString("]");
ui->selectedCalc->setText(SelectedDeviceName);
ui->updateFirmware->show();
}
2017-11-29 00:16:29 +01:00
}
QString& USBSelector::getSelectedDevicePath()
{
return SelectedDevicePath;
}
2017-11-29 00:16:29 +01:00
2017-12-14 19:12:35 +01:00
QString& USBSelector::getSelectedDeviceName()
{
return SelectedDeviceName;
}
2017-11-29 00:16:29 +01:00
void USBSelector::RefreshList()
{
struct hid_device_info *devs, *cur_dev;
2017-11-29 00:16:29 +01:00
QTreeWidgetItem *newitem;
// MAKE SURE WE CLOSE EVERYTHING AND RESET THE ENTIRE LIBRARY BEFORE ENUMERATION
usb_shutdown();
if (hid_init())
{
2017-11-29 00:16:29 +01:00
ui->USBtreeWidget->clear();
return;
}
devs = hid_enumerate(0x0, 0x0);
if(!devs) {
2017-11-29 00:16:29 +01:00
ui->USBtreeWidget->clear();
return;
}
cur_dev = devs;
QString result;
result.clear();
2017-11-29 00:16:29 +01:00
{
// FIRST DISABLE ALL ITEMS IN THE LIST
QTreeWidgetItemIterator it(ui->USBtreeWidget);
2017-11-29 00:16:29 +01:00
while(*it) {
(*it)->setDisabled(true);
++it;
}
}
QString manuf;
QString tmp;
QString pid;
QString newpath;
while (cur_dev) {
if(cur_dev->manufacturer_string) manuf=QString::fromStdWString(cur_dev->manufacturer_string);
else manuf.clear();
manuf.detach();
2017-11-29 00:16:29 +01:00
if(manuf.startsWith("newRPL")) {
pid=QString::number(cur_dev->vendor_id,16) + ":" + QString::number(cur_dev->product_id,16);
newpath=QString(cur_dev->path);
newpath.detach();
int nitems=ui->USBtreeWidget->topLevelItemCount();
int k;
QTreeWidgetItem *item;
newitem=0;
for(k=0;k<nitems;++k) {
item=ui->USBtreeWidget->topLevelItem(k);
if(item) {
if( (item->text(4)==pid)&&(item->text(3)==newpath))
{
// FOUND THE SAME ITEM AGAIN
newitem=item;
item->setDisabled(false);
break;
}
}
2017-11-29 00:16:29 +01:00
}
if(!newitem) {
newitem=new QTreeWidgetItem();
if(!newitem) return;
2017-11-29 00:16:29 +01:00
newitem->setText(0,"Empty");
newitem->setText(1,"Empty");
newitem->setText(2,"Empty");
newitem->setText(3,"Empty");
newitem->setText(4,"Empty");
2017-11-29 00:16:29 +01:00
ui->USBtreeWidget->addTopLevelItem(newitem);
}
if(cur_dev->product_string) tmp=QString::fromStdWString(cur_dev->product_string);
else tmp="[Unknown]";
if(cur_dev->serial_number) tmp+=QString("|SN=")+QString::fromStdWString(cur_dev->serial_number);
tmp.detach();
newitem->setText(0,tmp);
if(cur_dev->manufacturer_string) tmp=QString::fromStdWString(cur_dev->manufacturer_string);
else tmp="[Unknown]";
tmp.detach();
newitem->setText(1,tmp);
tmp=QString::number(cur_dev->vendor_id,16) + ":" + QString::number(cur_dev->product_id,16);
tmp.detach();
newitem->setText(4,tmp);
newitem->setText(3,newpath);
}
cur_dev = cur_dev->next;
}
hid_free_enumeration(devs);
// NOW ELIMINATE ANY ITEMS THAT ARE NOT ENABLED
{
int restart=1;
while(restart) {
int nitems=ui->USBtreeWidget->topLevelItemCount();
if(nitems==0) break;
int k;
QTreeWidgetItem *item;
for(k=0;k<nitems;++k) {
item=ui->USBtreeWidget->topLevelItem(k);
if(item) {
if(item->isDisabled()) {
if(SelectedDevicePath==item->text(3)) {
ui->buttonBox->setStandardButtons( QDialogButtonBox::Cancel);
SelectedDevicePath.clear();
ui->selectedCalc->setText(QString("No device selected."));
ui->USBtreeWidget->clearSelection();
ui->updateFirmware->hide();
}
QTreeWidgetItem *pparent=item->parent();
if(pparent) pparent->removeChild(item);
delete item;
restart=1;
break;
}
// THE DEVICE IS ACTIVE
tmp=item->text(3);
tmp.detach();
// STOP THE DRIVER AND REINITIALIZE COMPLETELY
__usb_paused=1;
while(__usb_paused>=0);
2019-05-18 00:00:33 +02:00
usb_shutdown();
// SET THE DRIVER TO USE THIS DEVICE AND START THE DRIVER
if(safe_stringcpy(__usb_devicepath,8192,tmp.toUtf8().constData())) __usb_devicepath[0]=0;
__usb_timeout=200; // SET TIMEOUT TO 200 ms FOR QUICK DETECTION
2019-05-18 00:00:33 +02:00
usb_init(0); // FORCE REINITIALIZATION, CLOSE ANY PREVIOUS HANDLES IF THEY EXIST
if(usb_isconnected())
2017-11-28 00:54:11 +01:00
{
2019-04-06 00:38:00 +02:00
unsigned char buffer[1024];
int res;
2019-04-06 00:38:00 +02:00
int available=0;
__usb_paused=0;
2019-04-06 00:38:00 +02:00
do {
2019-05-09 00:57:48 +02:00
usb_sendcontrolpacket(P_TYPE_GETSTATUS);
tmr_t start,end;
// WAIT FOR THE CONTROL PACKET TO BE SENT
start=tmr_ticks();
res=0;
while(__usb_drvstatus&USB_STATUS_TXCTL) {
if((__usb_drvstatus&(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED))!=(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED)) break;
QThread::yieldCurrentThread();
2019-05-10 16:42:17 +02:00
end=tmr_ticks();
if(tmr_ticks2ms(start,end)>__usb_timeout) {
res=-1;
break;
}
}
2019-04-06 00:38:00 +02:00
if(res<0) break;
2019-04-06 00:38:00 +02:00
if(!usb_waitforreport()) { res=-1; break; }
// WAIT FOR A RESPONSE
USB_PACKET *pkt=usb_getreport();
if(P_FILEID(pkt)!=0) {
2019-05-09 19:08:33 +02:00
// REQUEST UNCONDITIONAL ABORT
__usb_fileid=0xffff;
usb_sendcontrolpacket(P_TYPE_ABORT);
__usb_fileid=0;
2019-05-09 00:57:48 +02:00
tmr_t start,end;
2019-05-09 00:57:48 +02:00
// WAIT FOR THE CONTROL PACKET TO BE SENT
start=tmr_ticks();
res=0;
while(__usb_drvstatus&USB_STATUS_TXCTL) {
2019-05-09 00:57:48 +02:00
if((__usb_drvstatus&(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED))!=(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED)) break;
2019-05-09 00:57:48 +02:00
QThread::yieldCurrentThread();
end=tmr_ticks();
if(tmr_ticks2ms(start,end)>__usb_timeout) {
res=-1;
break;
}
}
continue;
2019-05-09 00:57:48 +02:00
}
usb_releasereport();
2019-04-06 00:38:00 +02:00
if(res<0) break;
// GOT AN ANSWER, MAKE SURE REMOTE IS READY TO RECEIVE
if(__usb_drvstatus&(USB_STATUS_HALT|USB_STATUS_ERROR)) { res=-1; break; }
2019-04-06 00:38:00 +02:00
// ATTEMPT TO SEND SOMETHING TO SEE IF IT'S ACTIVELY RESPONDING
uint32_t getversion[6]={
MKPROLOG(SECO,5), // ACTUAL DATA
CMD_VERSION,
CMD_DROP,
CMD_USBSEND,
CMD_DROP,
CMD_QSEMI
};
int fileid;
res=fileid=usb_txfileopen('O');
if(!res) break;
2017-11-28 00:54:11 +01:00
res=usb_filewrite(fileid,(BYTEPTR)getversion,6*sizeof(uint32_t));
2017-11-28 00:54:11 +01:00
if(!res) break;
res=usb_txfileclose(fileid);
if(res<0) break;
2019-04-06 00:38:00 +02:00
// WAIT FOR THE FILE TO ARRIVE
start=tmr_ticks();
res=0;
while(!usb_hasdata()) {
2019-04-06 00:38:00 +02:00
if((__usb_drvstatus&(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED))!=(USB_STATUS_CONFIGURED|USB_STATUS_INIT|USB_STATUS_CONNECTED)) break;
2019-04-06 00:38:00 +02:00
QThread::yieldCurrentThread();
end=tmr_ticks();
if(tmr_ticks2ms(start,end)>__usb_timeout) {
res=-1;
break;
}
}
if(res<0) break;
res=fileid=usb_rxfileopen();
2019-04-06 00:38:00 +02:00
if(res<0) break;
res=usb_fileread(fileid,buffer,1024);
if(res<=0) break;
2019-04-06 00:38:00 +02:00
usb_rxfileclose(fileid);
{
unsigned int strprolog;
strprolog=buffer[0]+(buffer[1]<<8)+(buffer[2]<<16)+(buffer[3]<<24);
int length=rplStrSize(&strprolog);
2019-05-18 00:00:33 +02:00
tmp=QString::fromUtf8((char *)(buffer+4),length);
tmp.detach();
available=1;
}
2019-04-06 00:38:00 +02:00
2019-04-06 00:38:00 +02:00
} while(!available);
__usb_paused=1;
while(__usb_paused>=0);
usb_shutdown();
2019-05-10 23:50:19 +02:00
__usb_curdevice=0;
__usb_timeout=5000; // SET TIMEOUT TO THE DEFAULT 5000ms
if(!available) {
tmp="[Device not responding]";
}
item->setText(2,tmp);
2019-05-10 23:50:19 +02:00
}
restart=0;
}
2017-11-30 19:23:09 +01:00
}
}
__usb_timeout=5000; // MAKE SURE WE LEAVE THE TIMEOUT TO THE DEFAULT VALUE
}
2019-05-18 00:00:33 +02:00
ui->USBtreeWidget->resizeColumnToContents(0);
2017-11-29 00:16:29 +01:00
// DONE, THE LIST WAS REFRESHED
2017-11-29 00:16:29 +01:00
}
2017-11-29 00:16:29 +01:00
void USBSelector::on_USBSelector_accepted()
{
2017-11-30 19:23:09 +01:00
2017-11-29 00:16:29 +01:00
}
2017-11-29 00:16:29 +01:00
void USBSelector::on_USBSelector_rejected()
{
2019-05-18 00:00:33 +02:00
2017-11-29 00:16:29 +01:00
}
2017-11-29 00:16:29 +01:00
void USBSelector::refresh()
{
2019-05-18 00:00:33 +02:00
if(norefresh) return;
2017-11-29 00:16:29 +01:00
RefreshList();
QTimer::singleShot(500,this,SLOT(refresh()));
2019-05-10 23:50:19 +02:00
}
extern "C" int usbremotefwupdatestart();
extern "C" int usbsendtoremote(uint32_t *data,int nwords);
2019-05-18 00:00:33 +02:00
void USBSelector::on_updateFirmware_clicked()
{
// STOP REFRESHING THE LIST
norefresh=true;
QString path;
// THIS IS ONLY FOR 50g/40g/39g HARDWARE
// TODO: IMPROVE ON THIS FOR OTHER HARDWARE PLATFORMS
unsigned int address;
unsigned int nwords;
path=QStandardPaths::locate(QStandardPaths::DocumentsLocation,"newRPL",QStandardPaths::LocateDirectory);
if(path.isEmpty()) path=QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
QString fname=QFileDialog::getOpenFileName(this,"Select firmware file to send to calculator",path,"firmware binary files (*.bin *.* *)");
if(!fname.isEmpty()) {
QFile file(fname);
if(!file.open(QIODevice::ReadOnly)) {
QMessageBox a(QMessageBox::Warning,"Error while opening","Cannot open file "+ fname,QMessageBox::Ok,this);
a.exec();
2019-05-23 00:45:03 +02:00
norefresh=false;
QTimer::singleShot(500,this, SLOT(refresh()));
return;
}
// FILE IS OPEN AND READY FOR READING
filedata=file.readAll();
file.close();
// THIS IS ONLY VALID FOR 50G AND COUSINS, FIX LATER
if((strncmp(filedata.constData(),"KINPOHP39G+IMAGE",16)==0)||
(strncmp(filedata.constData(),"KINPOHP40G+IMAGE",16)==0)||
(strncmp(filedata.constData(),"KINPOUPDATEIMAGE",16)==0)) {
address=0x4000;
nwords=filedata.size()>>2;
filedata.replace(0,16,"Kinposhcopyright");
}
else {
QMessageBox a(QMessageBox::Warning,"Invalid firmware image","Invalid firmware image",QMessageBox::Ok,this);
a.exec();
// START REFRESHING THE LIST AGAIN
2019-05-23 00:45:03 +02:00
norefresh=false;
QTimer::singleShot(500,this, SLOT(refresh()));
return;
}
QMessageBox warn(QMessageBox::Warning,"Firmware update","Firmware on the remote device is about to be updated. Do NOT disconnect the device. OK to proceed?",QMessageBox::Yes | QMessageBox::No,this);
if(warn.exec()==QMessageBox::No) {
// START REFRESHING THE LIST AGAIN
2019-05-23 00:45:03 +02:00
norefresh=false;
QTimer::singleShot(500,this, SLOT(refresh()));
return;
}
} else {
// START REFRESHING THE LIST AGAIN
2019-05-23 00:45:03 +02:00
norefresh=false;
QTimer::singleShot(500,this, SLOT(refresh()));
2019-05-18 00:00:33 +02:00
return;
}
ui->USBtreeWidget->setEnabled(false);
ui->updateFirmware->setEnabled(false);
ui->updateProgress->setRange(0,nwords);
ui->updateProgress->show();
ui->updateProgress->setValue(0);
ui->buttonBox->setEnabled(false);
// CONNECT TO THE USB DEVICE
2019-05-18 00:00:33 +02:00
__usb_paused=1;
while(__usb_paused>=0);
usb_shutdown();
if(safe_stringcpy(__usb_devicepath,8192,SelectedDevicePath.toUtf8().constData())) __usb_devicepath[0]=0;
usb_init(0);
2019-05-23 00:45:03 +02:00
__fwupdate_progress=0;
2019-05-18 00:00:33 +02:00
if(!usb_isconnected()) {
// TODO: ERROR PROCESS
2019-05-18 00:00:33 +02:00
// START REFRESHING THE LIST AGAIN
2019-05-23 00:45:03 +02:00
finishedupdate();
return;
}
__fwupdate_progress=0;
__fwupdate_address=address;
__fwupdate_nwords=nwords;
__fwupdate_buffer=(BYTEPTR)filedata.constData();
connect(&update_thread,SIGNAL(finished()),this,SLOT(finishedupdate()));
update_thread.start();
while(!update_thread.isRunning()); // WAIT FOR THE THREAD TO START
// START REPORTING PROGRESS
QTimer::singleShot(0, this, SLOT(updateprogress()));
}
void USBSelector::finishedupdate()
{
2019-05-18 00:00:33 +02:00
// PUT THE USB DRIVER TO REST
__usb_paused=1;
while(__usb_paused>=0) ;
2019-05-18 00:00:33 +02:00
usb_shutdown();
int result=__fwupdate_address;
if(!result) {
QMessageBox a(QMessageBox::Warning,"Communication error while sending firmware","USB communication error",QMessageBox::Ok,this);
a.exec();
}
2019-05-23 00:45:03 +02:00
//ui->USBtreeWidget->clear();
ui->USBtreeWidget->setEnabled(true);
ui->updateFirmware->setEnabled(true);
ui->updateProgress->hide();
ui->updateProgress->setValue(0);
ui->buttonBox->setEnabled(true);
numberoftries=0;
2019-05-23 00:45:03 +02:00
norefresh=false;
// AND JUST HOPE IT WILL RECONENCT SOME TIME
2019-05-18 00:00:33 +02:00
// START REFRESHING THE LIST AGAIN
2019-05-23 00:45:03 +02:00
QTimer::singleShot(0,this, SLOT(refresh()));
}
void USBSelector::updateprogress()
{
if(!update_thread.isRunning()) return;
ui->updateProgress->setValue(__fwupdate_progress);
QTimer::singleShot(0, this, SLOT(updateprogress()));
}
// ****************************************** USB DRIVER ON A SEPARATE THREAD
FWThread::FWThread(QObject *parent)
: QThread(parent)
{
}
FWThread::~FWThread()
{
}
void FWThread::run()
{
int nwords=__fwupdate_nwords;
__fwupdate_progress=0;
// START USB DRIVER
__usb_paused=0;
2019-04-25 15:30:25 +02:00
{
2019-05-18 00:00:33 +02:00
// WAIT 200ms BEFORE STARTING ANOTHER CONVERSATION WITH THE DEVICE
2019-04-25 15:30:25 +02:00
tmr_t start,end;
start=tmr_ticks();
do end=tmr_ticks(); while(tmr_ticks2ms(start,end)<200);
2019-04-25 15:30:25 +02:00
}
// SEND CMD_USBFWUPDATE TO THE CALC
if(!usbremotefwupdatestart()) {
__fwupdate_address=0;
return;
}
{
// WAIT 500ms BEFORE STARTING ANOTHER CONVERSATION WITH THE DEVICE
tmr_t start,end;
start=tmr_ticks();
2019-05-21 19:12:26 +02:00
do end=tmr_ticks(); while(tmr_ticks2ms(start,end)<500);
}
WORD header[3];
int result=1,offset=0;
2019-05-09 00:57:48 +02:00
int fileid;
while(result && (nwords>1024)) {
2019-05-09 00:57:48 +02:00
if(result) result=fileid=usb_txfileopen('W');
if(!result) {
// TODO: SOME KIND OF ERROR
break;
}
// SEND FIRMWARE BLOCK MARKER
header[0]=TEXT2WORD('F','W','U','P');
header[1]=__fwupdate_address+(offset<<2);
header[2]=1024;
2019-05-09 00:57:48 +02:00
if(result && (!usb_filewrite(fileid,(BYTEPTR)header,3*sizeof(WORD)))) {
// TODO: SOME KIND OF ERROR
result=0;
break;
}
if(result) {
BYTEPTR buffer=__fwupdate_buffer+offset*sizeof(WORD);
2019-05-09 00:57:48 +02:00
if(!usb_filewrite(fileid,buffer,1024*sizeof(WORD))) {
result=0;
break;
2019-04-22 14:12:52 +02:00
}
offset+=1024;
}
2019-04-22 14:12:52 +02:00
2019-05-09 00:57:48 +02:00
if(result && (!usb_txfileclose(fileid))) {
// TODO: SOME KIND OF ERROR
result=0;
break;
}
nwords-=1024;
__fwupdate_progress=offset;
}
if(result && nwords) {
2019-05-09 00:57:48 +02:00
result=fileid=usb_txfileopen('W');
// SEND FIRMWARE BLOCK MARKER
header[0]=TEXT2WORD('F','W','U','P');
header[1]=__fwupdate_address+(offset<<2);
header[2]=nwords;
2019-05-09 00:57:48 +02:00
if(result && (!usb_filewrite(fileid,(BYTEPTR)header,3*sizeof(WORD)))) {
// TODO: SOME KIND OF ERROR
result=0;
}
if(result) {
BYTEPTR buffer=__fwupdate_buffer+offset*sizeof(WORD);
2019-05-09 00:57:48 +02:00
if(!usb_filewrite(fileid,buffer,nwords*sizeof(WORD))) {
result=0;
}
offset+=nwords;
}
2019-05-09 00:57:48 +02:00
if(result && (!usb_txfileclose(fileid))) {
// TODO: SOME KIND OF ERROR
result=0;
}
__fwupdate_progress=offset;
}
// DONE SENDING THE LAST BLOCK
2019-04-22 14:12:52 +02:00
{
// WAIT TWO FULL SECONDS BEFORE STARTING ANOTHER CONVERSATION WITH THE DEVICE
2019-04-22 14:12:52 +02:00
tmr_t start,end;
start=tmr_ticks();
do end=tmr_ticks(); while(tmr_ticks2ms(start,end)<2000);
2019-04-22 14:12:52 +02:00
}
// NOW FINISH THE TEST BY RESETTING
2019-05-09 00:57:48 +02:00
result=fileid=usb_txfileopen('W');
header[0]=TEXT2WORD('F','W','U','P');
header[1]=0xffffffff;
header[2]=0;
2019-05-09 00:57:48 +02:00
if(result && (!usb_filewrite(fileid,(BYTEPTR)header,3*sizeof(WORD)))) {
// TODO: SOME KIND OF ERROR
result=0;
}
2019-05-09 00:57:48 +02:00
if(result && (!usb_txfileclose(fileid))) {
// TODO: SOME KIND OF ERROR
result=0;
}
__fwupdate_address=result;
}