/************************************************************************* ** ** Broadcom Corp. Confidential ** Copyright 1999, 2000 Broadcom Corp. All Rights Reserved. ** ** THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE ** AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR ** EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. ** ** File: xmodem.c ** Description: XModem download code ** REVISION: ** ****************************************************************************/ #include "xmodem.h" #include "bcmuart.h" struct bserial { volatile UartChannel *uart; }bserial; #if 0 extern void print_string(char *str); #define XDEBUG(x) print_string x #else #define XDEBUG(x) #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* Common ASCII code */ #define ASC_CR 0x0d /* Carriage return */ #define ASC_LF 0x0a /* Line feed */ #define ASC_ESC 0x1b /* Escape */ #define ASC_BS 0x08 /* Back space */ #define ASC_SP 0x20 /* Space */ //////////////////////////////////////////////////////// #define MODEM_SOH 0x01 #define MODEM_STX 0x02 #define MODEM_EOT 0x04 #define MODEM_ACK 0x06 #define MODEM_NCK 0x15 #define MODEM_CAN 0x18 #define MODEM_C 0x43 #define XMODEM_MAX_ERROR 100 /* Time constants (unit is 54.918 ms) */ #define C_TIMEOUT 75*CLOCK_FACTOR // Time period of sending C out at startup #define DATA_TIMEOUT 10*CLOCK_FACTOR // Time out of data/control char receiving #define SOH_TIMEOUT 30*CLOCK_FACTOR // Time out of waiting for SOH // X Modem error condition enum XM_ERROR { No_Error, User_Abort, Block_Number_Timeout, Non_Sequential_Block_Number, Complement_Block_Number_Timeout, Block_Number_Unmatch, Data_Timeout, Checksum_High_Timeout, Checksum_Low_Timeout, Bad_Checksum, SOH_Timeout, EOT_received }XM_ERROR; /* Static vars */ static int crc_computed; static unsigned char blockCount; static unsigned int blockSize; static unsigned int lastBlockSize; static unsigned char * blockPtr; static unsigned char * target_buffer; static int modemError; static unsigned int errCount; /* Forward Dec */ int RxData(volatile UartChannel *uart, unsigned char *inData ); void AddCRC( unsigned char data ); int WaitStart(volatile UartChannel *uart); int WaitBlockNumber(volatile UartChannel *uart); int WaitData(volatile UartChannel *uart); int WaitCRC(volatile UartChannel *uart); int WaitSOH(volatile UartChannel *uart); #if (BCHP_CHIP==7550) || (BCHP_CHIP==7552) #define TxData(uart,x) \ while(!(uart->sdw_lsr & THRE)); \ uart->sdw_rbr_thr_dll = (unsigned char)x; #else #define TxData(uart,x) \ while(!(uart->txstat & TXDREGEMT)); \ uart->txdata = x; #endif void ClrCRC(void){crc_computed=0;} int GetCRC(void){return crc_computed & 0xffff;} void AssertXMError(int er) { XDEBUG(("AssertXMError\n")); modemError = er; errCount++; }; // Reset XModem void ResetXModem() { XDEBUG(("ResetXModem\n")); blockPtr = target_buffer; blockCount = 1; modemError = No_Error; }; /***************************************************************** int RxData( unsigned char *inData ) Checks for incoming data Return: TRUE - Incoming data received FALSE - No incoming data *****************************************************************/ #define RXRDA 0x01 #define RXOVFERR 0x02 #define RXPARERR 0x04 #define RXFRAMERR 0x08 int RxData(volatile UartChannel *uart, unsigned char *inData ) { volatile unsigned char Status; volatile unsigned char junk; //XDEBUG(("RxData\n")); #if (BCHP_CHIP==7550) || (BCHP_CHIP==7552) Status = uart->sdw_lsr; if (Status & RXRDA) { if (Status & RXOVFERR) { XDEBUG(("OVERRUNERR\n")); junk = uart->sdw_rbr_thr_dll & 0xFF; TxData(uart,MODEM_C); } else if (Status & RXPARERR) { XDEBUG(("PARERR\n")); junk = uart->sdw_rbr_thr_dll & 0xFF; TxData(uart,MODEM_C); } else if (Status & RXFRAMERR) { XDEBUG(("FRAMEERR\n")); junk = uart->sdw_rbr_thr_dll & 0xFF; TxData(uart,MODEM_C); } *inData = uart->sdw_rbr_thr_dll & 0xFF; return TRUE; } #else Status = uart->rxstat; if (Status & RXDATARDY) { if (Status & OVERRUNERR) { XDEBUG(("OVERRUNERR\n")); junk = uart->rxdata; TxData(uart,MODEM_C); } else if (Status & PARERR) { XDEBUG(("PARERR\n")); junk = uart->rxdata; TxData(uart,MODEM_C); } else if (Status & FRAMEERR) { XDEBUG(("FRAMEERR\n")); junk = uart->rxdata; TxData(uart,MODEM_C); } *inData = uart->rxdata; return TRUE; } #endif return FALSE; } /***************************************************************** void AddCRC( unsigned char data ) Adds data into CRC *****************************************************************/ void AddCRC( unsigned char data ) { int i; // XDEBUG(("AddCRC\n")); crc_computed = crc_computed ^ (int)data << 8; for (i=0; i<8; ++i) { if ( crc_computed & 0x8000 ) { crc_computed = crc_computed << 1 ^ 0x1021; } else { crc_computed = crc_computed << 1; } } } #define TEN_SECOND 10000000 //#define TEN_SECOND 1000000 /***************************************************************** WaitStart() Waits for XMODEM receiving to start Return: TRUE - XMODEM downloading started FALSE - XMODEM downloading aborted Note: User can press ESC to abort the downloading. *****************************************************************/ int WaitStart(volatile UartChannel *uart) { unsigned char data; unsigned int delay; XDEBUG(("WaitStart\n")); ResetXModem(); delay = 0; while (1) { /* Generate C every 10 seconds if SOH has not received. */ if (delay == TEN_SECOND) { delay = 0; TxData(uart,MODEM_C); } delay++; /* Wait for hand shake ack */ if ( RxData(uart, &data) ) { if ( data == MODEM_SOH ) { XDEBUG(("MODEM_SOH\n")); blockSize = 128; lastBlockSize = blockSize; return TRUE; } else if ( data == MODEM_STX ) { XDEBUG(("MODEM_STX\n")); blockSize = 1024; lastBlockSize = blockSize; return TRUE; } else if ( data == ASC_ESC ) { XDEBUG(("ASC_ESC\n")); AssertXMError( User_Abort ); return FALSE; } } } } /***************************************************************** WaitBlockNumber() Wait for XMODEM receiving the block number complement Return: TRUE - XMODEM received block number complement (May not be correct) FALSE - Timeout, should restart the XModem *****************************************************************/ int WaitBlockNumber(volatile UartChannel *uart) { unsigned char data; unsigned char receivedblockCount; XDEBUG(("WaitBlockNumber\n")); /* Wait for block number */ while (1) { /* Check if block number is received */ if ( RxData(uart,&receivedblockCount) ) { break; } } /* Wait for block number complement */ while (1) { /* Check block number */ if ( RxData(uart,&data) ) { /* Check if complement matches */ if ( (data + receivedblockCount) != 0xff ) { XDEBUG(("Block_Number_Unmatch\n")); AssertXMError( Block_Number_Unmatch ); } /* Check if it is re-doing the last block */ /* or reciving the very first block */ if ( receivedblockCount == blockCount) { XDEBUG(("re-doing the last block or first")); ClrCRC(); return TRUE; } /* Check if it is a new block */ if ( (receivedblockCount == blockCount+1) || ((receivedblockCount==0) && (blockCount==0xff)) ) { blockCount = receivedblockCount; blockPtr += lastBlockSize; lastBlockSize = blockSize; ClrCRC(); return TRUE; } /* Error: the block number doesn't make sense */ AssertXMError( Non_Sequential_Block_Number ); return TRUE; } } } /***************************************************************** WaitData() Wait for data Return: TRUE - XMODEM received data block FALSE - Timeout, should restart the XModem *****************************************************************/ int WaitData(volatile UartChannel *uart) { unsigned char data; unsigned int i,cnt; unsigned long timeout = 0; cnt = 0; XDEBUG(("WaitData ")); for (i = 0; i < blockSize; i++) { while (1) { if ( RxData(uart,&data) ) { *(unsigned char*)(blockPtr+i) = data; AddCRC( data ); cnt++; if (cnt == blockSize) { return TRUE; } break; } timeout++; if (timeout > TEN_SECOND) { XDEBUG(("WaitData - TIMEOUT\n")); return FALSE; } } } return TRUE; } /***************************************************************** WaitCRC() Wait for CRC check sum Return: TRUE - XMODEM received check sum FALSE - Timeout, should restart the XModem *****************************************************************/ int WaitCRC(volatile UartChannel *uart) { unsigned char data; int crc_received; XDEBUG(("WaitCRC\n")); /* Wait CRC check sum high byte */ while (1) { /* Wait for CRC check sum high byte */ if ( RxData(uart,&data) ) { crc_received = data << 8; break; } } /* Wait CRC check sum low byte */ while (1) { /* Wait for CRC check sum low byte */ if ( RxData(uart,&data) ) { crc_received |= data ; break; } } if ( crc_received != GetCRC() ) AssertXMError( Bad_Checksum ); return TRUE; } /***************************************************************** WaitSOH() Waits for SOH control character Return: TRUE - SOH or STX or EOT recived FALSE - Timeout *****************************************************************/ int WaitSOH(volatile UartChannel *uart) { unsigned char data; XDEBUG(("WaitSOH\n")); while (1) { /* Wait for hand shake (SOH or EOT) */ if ( RxData(uart,&data) ) { if ( data == MODEM_SOH ) { blockSize = 128; return TRUE; } if ( data == MODEM_STX ) { blockSize = 1024; return TRUE; } if ( data == MODEM_EOT ) { TxData(uart,MODEM_ACK); AssertXMError( EOT_received ); return TRUE; } } } } /***************************************************************** XModemReceive( volatile UartChannel *uart, char* buf ) Starts XModem receive Return: The number of bytes received If it is zero, an error condition has occurred. *****************************************************************/ unsigned int XModemReceive(volatile UartChannel *uart, unsigned char* buf ) { /* Initialize the buffer pointer */ target_buffer = buf; /* Clear error count */ errCount = 0; /* Wait X Modem start */ if ( !WaitStart(uart) ) { return 0; } /* X Modem main loop */ do { if ( !WaitBlockNumber(uart) ) { if ( !WaitStart(uart) ) return 0; } else if ( !WaitData(uart) ) { if ( !WaitStart(uart) ) return 0; } else if ( !WaitCRC(uart) ) { if ( !WaitStart(uart) ) return 0; } else { if ( modemError == No_Error ) { TxData(uart,MODEM_ACK); if ( !WaitSOH(uart) ) if ( !WaitStart(uart) ) return 0; } else { modemError = No_Error; if ( errCount > XMODEM_MAX_ERROR ) { TxData(uart,MODEM_CAN); errCount = 0; if ( !WaitStart(uart) ) return 0; } else { TxData(uart,MODEM_NCK); if ( !WaitSOH(uart) ) if ( !WaitStart(uart) ) return 0; } } } } while ( modemError != EOT_received ); return(unsigned int)(blockPtr - target_buffer + blockSize); } unsigned int xmodem_receive(volatile UartChannel *uart, unsigned char* buf ) { return XModemReceive(uart,buf); }