This example updates the firmware in Rabbit modules via Ethernet from a host computer using TCP/IP instead of HTTP or FTP.
The following contains the important snippets, the support stuff depends on your application.
At first, a failure rate (requiring reload of the firmware via serial cable – NOT GOOD) of 1 in 5 was encountered. Adding a bit of time delay between the writing of the image to flash and the exit(0) command seemed to solve the problem so far . This time delay was accomplished accidentally by adding code to send status messages back to the host.
This was tested for the RCM4200.
---------------------- Dynamic C Code fer da Rabbit----------------------------------
…the define statements and such…
#use RCM42xx.LIB
#define BU_TEMP_USE_SFLASH
#define BU_TEMP_PAGE_OFFSET 0
#use “board_update.lib”
//TCPIP Defines
#define TCPCONFIG 1
#define _PRIMARY_STATIC_IP “169.254.56.11” //this gets changed later
#define _PRIMARY_NETMASK “255.255.0.0”
#define MY_GATEWAY “10.10.6.1”
#define MY_NAMESERVER “10.10.6.1”
#define PORT 23
#define TCP_BUF_SIZE 4096
#use “dcrtcp.lib” //tcpip library
… setup code similar to this…
//this is rough code – add garnish as necessary!
sock_init_or_exit(1);
tcp_listen(&socket,PORT,0,0,NULL,0);
printf("Waiting for connection…
");
while(!sock_established(&socket) && sock_bytesready(&socket)==-1)
tcp_tick(&socket);
printf("Connection received…
");
…
loop here watching the port waiting for the command to do stuff, including
perform a firmware update
aha – code received to do the update, so call
receiveAndInstallNewFirmware(socket, 0);
//receiveAndInstallNewFirmware won’t return – it will exit the program
//on success or fail
…
…the actual Rabbit function code…
// Java code for the host listed farther below
//----------------------------------------------------------------------------
// cleanUpFirmwareInstall
//
// Prints status message and sends it to host, then cleans up any loose ends
// from the firmware install process and exits.
//
int cleanUpFirmwareInstall(tcp_Socket *socket, char *pMsg)
{
printf(pMsg);
sock_flushnext(socket);
sock_write(socket, pMsg, strlen(pMsg));
sock_close(socket);
while(tcp_tick(socket)) {}
while (buCloseFirmware() == -EBUSY);
exit(0);
}//end of cleanUpFirmwareInstall
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// installNewFirmware
//
// Replaces the software running in the Rabbit micro-controller. Verifies the
// new firmware image stored in the temporary location and then installs it
// over the old code.
//
// NOTE: The firmware update process was failing 1 out of 5 times until
// time delay (by adding code sending message to host) was inserted
// before the exit(0) call. Unsure why this solved the problem.
//
// Return Codes:
// Note that this function will either succeed and reboot to new firmware,
// or it will fail with one of the following error codes:
//
// -EILSEQ: Not a valid firmware_info_t struct (bad marker bytes
// or unsupported version of structure).
// -EBADMSG: Bad CRC (structure has been corrupted).
// -ENODATA: Source not open, or firmware info not found in source.
// -EPERM: Firmware was compiled for a different target.
// -EBADDATA: CRC-32 mismatch, firmware image corrupted.
// -EBADMSG: CRC-32 mismatch after installing.
// -ENOMEM: Couldn’t allocate buffer to copy firmware.
//
// Error codes when using a FAT file for temporary storage:
// -EINVAL: Couldn’t parse BU_TEMP_FILE.
// -ENOENT: File BU_TEMP_FILE does not exist.
// -EMFILE: Too many open files.
//
// Error codes when using the serial flash for temporary storage:
// -ENODEV: Can’t find/read the serial flash.
//
// On success, returns 1 regardless of the number of bytes read from the socket.
//
int installNewFirmware(tcp_Socket *socket)
{
char buffer[200];
firmware_info_t fi;
int i;
int result;
int progress;
printf( "Verifying and installing new firmware…
");
result = buOpenFirmwareTemp(BU_FLAG_NONE);
if (result){
sprintf(buffer, "Error %d opening firmware image.
", result);
cleanUpFirmwareInstall(socket, buffer);
}
// buGetInfo is a non-blocking call, and may take multiple attempts
// before the file is completely open.
do {
result = buGetInfo( &fi);
} while ( (result == -EBUSY));
if (result){
sprintf(buffer, "Error %d getting firmware image info.
", result);
cleanUpFirmwareInstall(socket, buffer);
}
printf( "Found %s v%u.%02x…
", fi.program_name,
fi.version >> 8, fi.version & 0xFF);
printf( "Attempting to install new version…
");
progress = 0;
do{
printf( “\r verify %u.%02u%%\r”, progress / 100, progress % 100);
//verify the firmware image
result = buVerifyFirmware(&progress);
} while (result == -EAGAIN);
if (result){
sprintf(buffer, "Error %d while verifying image.
", result);
cleanUpFirmwareInstall(socket, buffer);
}
printf( "Verify complete, installing new firmware…
");
//install the firmware image
result = buInstallFirmware();
if (result){
//put the firmware running in ram back into flash on failure
//buInstallFirmware supposedly does this, but have had issues with that
buRestoreFirmware(0);
sprintf(buffer, "Error %d while installing firmware.
", result);
cleanUpFirmwareInstall(socket, buffer);
}
//send message to host and exit
sprintf(buffer, "Install successful: rebooting.
");
cleanUpFirmwareInstall(socket, buffer);
return result;
}//end of installNewFirmware
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// receiveAndInstallNewFirmware
//
// Receives a Rabbit firmware image file from the host and overwrites the
// existing code.
//
// The image is received in 1024 byte packets, with the last packet being
// smaller as necessary.
//
// NOTE: The firmware update functions alter the Port C configuration.
// Currently, this function will halt the program whether or not
// the update succeeds. This eliminates the need to reset Port C. If
// That behavior is changed, then Port C will need to be reset before
// program execution can continue.
//
// Return Codes:
//
// Return codes:
// Most return codes come from install_firmware (see above).
//
// Additional return codes for tftp_and_install:
// -EPERM: buTempCreate not supported on this hardware.
// -ENODEV: Couldn’t read from serial flash.
// -EBUSY: Timeout waiting for FAT filesystem.
// -ENODATA: Download didn’t contain a valid firmware image for this
// device.
// <0: Error opening FAT file, see fat_Open for full list of
// error codes and their meanings.
//
int receiveAndInstallNewFirmware(tcp_Socket *socket, int pPktID)
{
int bytesRead, offset;
char codeBuffer[CODE_BUFFER_SIZE];
char bufferIn[5], bufferOut[5];
int x,y, z, bufPtr, packetCount;
char shiftReg;
int result;
printf("Loading new firmware…
");
//read the checksum byte and check validity of the command packet
sock_fastread(socket,bufferIn,1);
if ( ((pPktID + bufferIn[0]) & 0xff) != 0) return -1;
packetCount = 0;
// set P2 back to active drive for use with the serial flash
WrPortI(PCDCR, &PCDCRShadow, (PCDCRShadow & 0xfb));
while ( (result = buTempCreate()) == -EBUSY);
if (result){
printf( "Error %d calling buTempCreate!
", result);
//send the error code back as MSB:LSB
send3Bytes(socket, ERROR, (result >> 8) & 0xff, result & 0xff);
return result;
}
//request first data packet from host
send3Bytes(socket, SEND_DATA_CMD, NO_STATUS, NO_STATUS);
printf("Receiving firmware packets…
");
//process packets until exit command packet received from host
do{
//wait for data or exit command packet from host
do{
codeBuffer[0] = NO_ACTION; //clean out the command byte
//wait for data packet from host - last packet is not full sized so
//just assume every packet is complete if a byte is received
if (sock_bytesready(socket) > 0)
bytesRead=sock_fastread(socket, codeBuffer, CODE_BUFFER_SIZE);
} while(tcp_tick(socket) && codeBuffer[0] == NO_ACTION);
// if the packet is data, store it in the temporary storage location
if (codeBuffer[0] == DATA_CMD){
packetCount++;
printf( "Packet #: %d...
", packetCount);
//request next data packet from host while processing the previous one
send3Bytes(socket, SEND_DATA_CMD, NO_STATUS, NO_STATUS);
if (bytesRead > 0){
// buTempWrite is non-blocking, so it may take multiple calls to
// complete the write -- must call it repeatedly while updating the
// offset to make sure it writes the entire buffer
//the first byte is a command byte, the rest are data bytes, so skip
//the command byte at index 0
offset = 1;
while (offset < bytesRead)
{
result = buTempWrite( &codeBuffer[offset], bytesRead - offset);
if (result == -EBUSY){
// resources busy, try again without any changes
}
else if (result < 0){
printf(
"Error %d writing firmware to temp location.
", result);
send3Bytes(
socket, ERROR, (result >> 8) & 0xff, result & 0xff);
return(result);
} else {
offset += result;
}
}
}//if (bytesRead > 0)
}// if (codeBuffer[0] == DATA_CMD)
} while(tcp_tick(socket) && codeBuffer[0] != EXIT_CMD);
//install the newly downloaded firmware
return installNewFirmware(socket);
// if installNewFirmware returned without exiting the program, then the
// install failed – exit the program to force a restart from known state
printf( "Install failed -- exiting program.
");
while (buCloseFirmware() == -EBUSY);
exit(0);
return(result);
}//end of receiveAndInstallNewFirmware
//----------------------------------------------------------------------------
…the actual Java code for the host…
…setup code similar to this…
//ipAddr is that of the Rabbit module
socket = new Socket(ipAddr, 23);
//set amount of time in milliseconds that a read from the socket will
//wait for data - this prevents program lock up when no data is ready
socket.setSoTimeout(250);
…
do stuff here – talk to the Rabbit, give it commands
when user clicks button to upload firmware, do this call:
…actual Java code…
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class InstallFirmwareSettings
//
// This class is used to pass in all necessary settings to the
// installNewRabbitFirmware function.
//
class InstallFirmwareSettings extends Object{
public byte loadFirmwareCmd;
public byte noAction;
public byte error;
public byte sendDataCmd;
public byte dataCmd;
public byte exitCmd;
}//end of class InstallFirmwareSettings
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Capulin1::installNewRabbitFirmware
//
// Transmits the Rabbit firmware image to the UT board to replace the existing
// code.
//
// See corresponding function in the parent class Board.
//
public void installNewRabbitFirmware()
{
//create an object to hold codes specific to the UT board for use by the
//firmware installer method
InstallFirmwareSettings settings = new InstallFirmwareSettings();
settings.loadFirmwareCmd = LOAD_FIRMWARE_CMD;
settings.noAction = NO_ACTION;
settings.error = ERROR;
settings.sendDataCmd = SEND_DATA_CMD;
settings.dataCmd = DATA_CMD;
settings.exitCmd = EXIT_CMD;
installNewRabbitFirmwareHelper(
"UT", "Rabbit\\CAPULIN UT BOARD.bin", settings);
}//end of Capulin1::installNewRabbitFirmware
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Board::installNewRabbitFirmwareHelper
//
// Transmits the Rabbit firmware code to the specified board to replace the
// existing code.
//
// The firmware in the Rabbit is stored in flash memory. There is a slight
// danger in installing new firmware because the Rabbit may become inoperable
// if a power glitch occurs during the process – a reload via serial cable
// would then be required.
//
// Since there is a danger of locking up the Rabbit and the flash memory can be
// written to a finite number of times, the code is not sent each time the
// system starts as is done with the FPGA code. Rather, it is only updated
// by explicit command.
//
// This function uses TCP/IP and transmits to the single board handled by
// this Board object. If multiple boards are being loaded simultaneously,
// the load time increases significantly.
//
// This function uses the “binary file” (*.bin) produced by the Dynamic C
// compiler.
//
// The file is transmitted in 1025 byte blocks: one command byte followed by
// 1024 data bytes. The last block is truncated as necessary.
//
// The remote should send the command SEND_DATA when it is ready for each
// block, including the first one.
//
void installNewRabbitFirmwareHelper(String pBoardType, String pFilename,
InstallFirmwareSettings pS)
{
int CODE_BUFFER_SIZE = 1025; //transfer command word and 1024 data bytes
byte[] codeBuffer;
codeBuffer = new byte[CODE_BUFFER_SIZE];
int remoteStatus;
int timeOutRead;
int bufPtr;
int pktCounter = 0;
boolean fileDone = false;
FileInputStream inFile = null;
try {
sendBytes(pS.loadFirmwareCmd); //send command to initiate loading
logger.logMessage(pBoardType + " " + ipAddrS +
" loading Rabbit firmware..." + "
");
timeOutRead = 0;
inFile = new FileInputStream(pFilename);
int c, inCount;
while(timeOutRead < FIRMWARE_LOAD_TIMEOUT){
inBuffer[0] = pS.noAction; //clear request byte from host
//clear status word (upper byte) from host
inBuffer[1] = pS.noAction;
//clear status word (lower byte) from host
inBuffer[2] = pS.noAction;
remoteStatus = 0;
//check for a request from the remote if connected
if (byteIn != null){
inCount = byteIn.available();
//0 = buffer offset, 2 = number of bytes to read
if (inCount >= 3) {byteIn.read(inBuffer, 0, 3);}
remoteStatus = (int)((inBuffer[0]<<8) & 0xff00)
+ (int)(inBuffer[1] & 0xff);
}
//trap and respond to messages from the remote
if (inBuffer[0] == pS.error){
logger.logMessage(pBoardType + " " + ipAddrS +
" error loading firmware, error code: " + remoteStatus + "
");
return;
}
//send data packet when requested by remote
if (inBuffer[0] == pS.sendDataCmd && !fileDone){
bufPtr = 0; c = 0;
codeBuffer[bufPtr++] = pS.dataCmd; // command byte = data packet
//be sure to check bufPtr on left side or a byte will get read
//and ignored every time bufPtr test fails
while (bufPtr