// Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Based on PREPRO.CPP and ROMBUILD.CPP
//
//
#define WIN32_LEAN_AND_MEAN
#pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union
#include <windows.h>
#include <winbase.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "h_utl.h"
#include "h_ver.h"
// need to pretend we've done e32image.h, to avoid clashes with winnt.h
#define __E32IMAGE_H__
struct E32ImageFile
{
};
class CBytePair;
#include "r_rom.h"
#define READ_BUFFER_SIZE 0x1000 // allows for exuberant T_REPRO without delays in it
#define WRITE_BUFFER_SIZE 0x1000
TRomLoad TheRomHeader;
TUint ImageDataSize=0;
TUint FileSize=0;
TText PortNumber='1';
TUint BaudRate=115200;
TBool Kick=EFalse;
TBool UseHex=EFalse;
TBool Verbose=EFalse;
TBool RawImage=EFalse;
TText* BootstrapName=NULL;
const TUint KReproWrapperSize = 0x100; // REPRO protocol assumes a wrapper size of 256 bytes
HANDLE comPort;
TUint32 BytesWritten;
TText *processCommandLine(int argc, char *argv[])
//
// Process the command line arguments, printing a helpful message if none are supplied
//
{
char HelpText[] =
"* Syntax: W32REPRO [options] filename[.bin]\n"
"* \n"
"* Option: -P<n> port number, defaults to COM1,\n"
"* Option: -K kick the other end, to force another repro attempt\n"
"* Option: -B<rate> baud rate, minimum 9600, defaults to 115200\n"
"* Option: -RAW raw image with no header\n"
"* Option: -BOOT <file> bootstrap with <file> transmitted at 9600 baud\n"
"* Option: -HEX use base 16 (for use with ReproC)\n"
"* Option: -V display raw protocol messages\n"
"* \n"
"* All messages from W32REPRO begin with '*', every thing else comes from the\n"
"* machine being reprogrammed.\n";
TText *filename=NULL;
if (argc == 1)
{
cout << HelpText;
return NULL;
}
for (int i=1; i<argc; i++)
{
strupr(argv[i]);
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
if (strcmp(argv[i],"-RAW")==0)
{
RawImage=ETrue;
}
else if (strcmp(argv[i],"-HEX")==0)
{
UseHex=ETrue;
}
else if (strcmp(argv[i],"-BOOT")==0)
{
if (++i==argc)
{
cout << "**** Missing argument for -BOOT\n*\n";
cout << HelpText;
return NULL;
}
BootstrapName=(TText*)argv[i];
}
else if (argv[i][1] == 'P')
{
PortNumber=argv[i][2];
}
else if (argv[i][1] == 'K')
{
Kick=ETrue;
}
else if (argv[i][1] == 'V')
{
Verbose=ETrue;
}
else if (argv[i][1] == 'B')
{
TInt rate=atoi(argv[i]+2);
if (rate>=9600)
{
BaudRate=rate;
}
else
{
cout << "**** Invalid baud rate: " << argv[i] << "\n*\n";
cout << HelpText;
return NULL;
}
}
else if (argv[i][1] == '?')
{
cout << HelpText;
return NULL;
}
else
{
cout << "**** Unrecognised argument " << argv[i] << "\n*\n";
cout << HelpText;
return NULL;
}
}
else // Must be the image filename
filename=(TText *)argv[i];
}
if (filename==NULL)
{
cout << "**** Missing image filename\n*\n";
cout << HelpText;
}
return filename;
}
TInt openComPort()
//
// Open the com port and configure it
//
{
char port[5]="COM1";
port[3]=PortNumber;
comPort=CreateFile(port,GENERIC_READ+GENERIC_WRITE,0,0,OPEN_EXISTING,0,NULL);
if (comPort==INVALID_HANDLE_VALUE)
return Print(EError,"* Cannot open %s\n",port);
DCB settings;
if (!GetCommState(comPort,&settings))
return Print(EError,"* Cannot read settings for %s\n",port);
if (!SetupComm(comPort,READ_BUFFER_SIZE,WRITE_BUFFER_SIZE))
return Print(EError,"* Cannot set buffer sizes for %s\n",port);
settings.fBinary=TRUE;
settings.fParity=FALSE;
settings.fAbortOnError=TRUE; // overrides EV_ERR
settings.BaudRate=BaudRate;
settings.ByteSize=8;
settings.Parity=NOPARITY;
settings.StopBits=ONESTOPBIT;
settings.fRtsControl=RTS_CONTROL_ENABLE;
settings.fDtrControl=DTR_CONTROL_ENABLE;
settings.fOutxCtsFlow=FALSE;
settings.fOutxDsrFlow=FALSE;
settings.fDsrSensitivity=FALSE;
settings.fOutX=FALSE; // no XON/XOFF for transmission
settings.fInX=FALSE; // no XON/XOFF for reception
settings.fNull=FALSE; // don't discard null bytes
settings.EvtChar='\001'; // REPRO command separator
if (!SetCommState(comPort,&settings))
return Print(EError,"* Cannot configure %s\n",port);
if (!SetCommMask(comPort,EV_RXFLAG+EV_ERR))
return Print(EError,"* Cannot set CommMask for %s\n",port);
COMMTIMEOUTS timeouts = {
//20,0,0, // allow up to 20 milliseconds between characters, i.e. buffer them properly
MAXDWORD,0,0, // return immediately
0,0 // no write timeouts
};
if (!SetCommTimeouts(comPort,&timeouts))
return Print(EError,"* Cannot set timeouts for %s\n",port);
if (!SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST))
Print(EError,"* Failed to raise priority of this thread err=%d\n",GetLastError());
if (!SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS))
Print(EError,"* Failed to raise priority of this process err=%d\n",GetLastError());
Print(EScreen,"* Using %s at %d baud\n\n",port,BaudRate);
return KErrNone;
}
BOOL WriteToComPort(char* data, DWORD length, char* comment)
{
if (Verbose)
{
if (comment==NULL)
Print(EScreen, "* TX=%*s\n", length, data);
else
Print(EScreen, "* TX <%d bytes of %s>\n", length, comment);
}
return WriteFile(comPort, data, length, &BytesWritten, NULL);
}
TInt Bootstrap9600()
{
DCB settings;
if (!GetCommState(comPort,&settings))
return Print(EError,"* Cannot read COM settings\n");
settings.BaudRate=9600;
if (!SetCommState(comPort,&settings))
return Print(EError,"* Cannot reconfigure to 9600 baud\n");
FILE* bootstrapFile=fopen((const char*)BootstrapName,"rb");
if (bootstrapFile==NULL)
return Print(EError,"* Cannot open bootstrap file %s for input (errno=%d)\n",BootstrapName,errno);
Print(EScreen,"* Sending bootstrap %s at 9600 baud\n",BootstrapName,BaudRate);
char bootdata[WRITE_BUFFER_SIZE];
TUint32 imageBytes=0;
while (!feof(bootstrapFile))
{
imageBytes=fread(bootdata,1,WRITE_BUFFER_SIZE,bootstrapFile);
if (imageBytes==0 && feof(bootstrapFile))
break;
if (imageBytes!=WRITE_BUFFER_SIZE && ferror(bootstrapFile))
{
return Print(ESevereError,"* Read only %d bytes of bootstrap err=%d\n",imageBytes,ferror(bootstrapFile));
}
if (!WriteToComPort(bootdata,imageBytes,"bootstrap data"))
return Print(ESevereError,"* Wrote only %d of %d bytes of bootstrap err=%d\n",
BytesWritten,imageBytes,GetLastError());
}
fclose(bootstrapFile);
settings.BaudRate=BaudRate;
if (!SetCommState(comPort,&settings))
return Print(EError,"* Cannot reconfigure to %d baud\n",BaudRate);
Print(EScreen,"* Bootstrap downloaded\n\n");
return KErrNone;
}
TInt main(int argc, char *argv[])
{
TInt err=KErrNone;
Print(EScreen,"\n* W32REPRO - Win32 version of PREPRO");
Print(EScreen," V%02d.%02d (Build %03d)\n",MajorVersion,MinorVersion,Build);
Print(EScreen,"* %s",Copyright);
TText *imageFileName = processCommandLine(argc, argv);
if (imageFileName==NULL)
return KErrGeneral;
FILE* romFile=fopen((const char*)imageFileName,"rb");
if (romFile==NULL)
return Print(EError,"* Cannot open ROM Image file %s for input (errno=%d)\n",imageFileName,errno);
if (RawImage)
TheRomHeader.wrapSize=0;
else
{
if (fread(&TheRomHeader,sizeof(TheRomHeader),1,romFile)!=1)
return Print(EError,"* Cannot read ROM Image header\n");
if (TheRomHeader.wrapSize!=KRomWrapperSize)
return Print(EError,"* Incorrect ROM header - wrong wrapper size\n");
}
if (fseek(romFile,0,SEEK_END)!=0)
return Print(EError,"* Cannot seek in ROM Image file\n");
FileSize=ftell(romFile);
ImageDataSize=FileSize-TheRomHeader.wrapSize;
Print(EAlways,"\n* ROM Image %s - 0x%06x bytes\n",imageFileName,ImageDataSize);
err=openComPort();
if (err!=KErrNone)
return err;
if (BootstrapName != NULL)
{
err=Bootstrap9600();
if (err!=KErrNone)
return err;
}
char romdata[WRITE_BUFFER_SIZE];
if (Kick)
{
memset(romdata,'!',64); // string of non-numeric characters, won't harm old REPRO
WriteToComPort(romdata,64,NULL);
}
//
// Wait around for REPRO on the other end to send us commands
//
char command[READ_BUFFER_SIZE+1];
char* cp=command;
TInt length=READ_BUFFER_SIZE;
TUint expectedOffset=0;
TInt done=0;
while (!done)
{
TUint32 bytesRead=0,imageBytes=0,offset=0;
TUint32 event;
if (!WaitCommEvent(comPort,&event,NULL))
{
if (GetLastError()!=ERROR_OPERATION_ABORTED)
Print(EAlways,"\n* Unexpected WaitCommEvent failure %d event %x\n",GetLastError(),event);
TUint32 commError;
if (!ClearCommError(comPort,&commError,NULL))
{
Print(ESevereError,"\n* Failed to clear CommError - give up now!\n");
return KErrGeneral;
}
if (commError!=CE_OVERRUN)
Print(EAlways,"\n* Unexpected comms error %x\n",commError);
}
if (!ReadFile(comPort,cp,length,&bytesRead,NULL))
{
if (GetLastError()!=ERROR_OPERATION_ABORTED)
Print(EAlways,"\n* Unexpected ReadFile failure %d bytes %d\n",GetLastError(),bytesRead);
}
if (bytesRead==0)
continue;
char* next;
char* end = cp+bytesRead;
*end='\0'; // stick a terminator on the end, just in case
for (cp=(char*)command; (next=(char*)memchr(cp,'\001',end-cp))!=NULL ;cp=next+1)
{
*next='\0'; // drop the terminator
if (Verbose)
Print(EScreen, " * RX=%s\n", cp);
switch (cp[0])
{
case 'D': // disconnect after successful REPRO
Print(EScreen,"* Disconnect\n");
done=1;
break;
case 'M': // print message
Print(EScreen,"%s",cp+1);
break;
case 'P': // panic
Print(ESevereError,"%s",cp+1);
break;
case 'R': // request for data from the image at specified address
if (end-next>1)
break; // must be the last command in the buffer
offset=strtoul(cp+1,NULL,UseHex?16:10)-KReproWrapperSize; // REPRO assumes wrapSize=256
if ((offset&4095)!=0)
{
Print(ESevereError,"* Image offset %x not a multiple of 4k (%s)\n", offset,cp);
break;
}
if (offset>expectedOffset)
{
Print(ESevereError,"* Image offset %x should have been %x\n", offset,expectedOffset);
break;
}
Print(EScreen,"%x \r",offset); // in case we lost the message
expectedOffset=offset+WRITE_BUFFER_SIZE; // what we expect next time
offset+=TheRomHeader.wrapSize; // offset into the file
if (fseek(romFile,offset,SEEK_SET)!=0)
{
Print(ESevereError,"* Can't seek to file offset %x", offset);
break;
}
memset(romdata,0xff,WRITE_BUFFER_SIZE);
imageBytes=fread(romdata,1,WRITE_BUFFER_SIZE,romFile);
if (imageBytes!=WRITE_BUFFER_SIZE && offset+imageBytes!=FileSize)
{
Print(ESevereError,"* Read only %d bytes of image data err=%d\n",imageBytes,ferror(romFile));
break;
}
if (!WriteToComPort(romdata,WRITE_BUFFER_SIZE,"image data"))
Print(ESevereError,"* Wrote only %d bytes of image data err=%x\n",BytesWritten,GetLastError());
break;
case 'S': // request for the size of the image
if (end-next>1)
break; // must be the last command in the buffer
if (next-cp==1)
{
sprintf((char*)romdata,"%010d\n",ImageDataSize+KReproWrapperSize);
if (!WriteToComPort(romdata,strlen(romdata),NULL)
|| BytesWritten!=strlen(romdata))
Print(ESevereError,"* Failed to write file size\n");
expectedOffset=0; // because we are starting again
break;
}
// otherwise fall through
default:
Print(EAlways,"\n* Unrecognised command >%s<\n", cp);
}
}
if (cp<end) // copy trailing characters back to the start of the buffer
memmove(command,cp,end-cp);
cp=command+(end-cp);
length=command+READ_BUFFER_SIZE-cp;
}
return KErrNone;
}