Files
zOS/common/umlibc/misc/xmodem.c

423 lines
16 KiB
C
Executable File

/*! \file xmodem.c \brief XModem Transmit/Receive Implementation with CRC and 1K support. */
//*****************************************************************************
//
// File Name : 'xmodem.c'
// Title : XModem Transmit/Receive Implementation with CRC and 1K support
// Author : Pascal Stang - Copyright (C) 2006
// Created : 4/22/2006
// Revised : 7/22/2006
// Version : 0.1
// Target MCU : AVR processors
// Editor Tabs : 4
//
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************
// Modified by BogdanM for the eLua project
// Modified by rhempel for the pbLua project
//*****************************************************************************
#include <string.h>
#include <stdlib.h>
#include "stdconst.h"
#include "xmodem.h"
#include "checksums.h"
#include "console.h"
#include "SysISRHandler.h"
static int xmodem_out( unsigned char c )
{
return( nxt_putc( c ) );
}
static int xmodem_in( void )
{
return( nxt_getc( 0 ) );
}
static int xmodem_crc_check( const unsigned char *buffer, int size )
{
// check checksum against packet
if(crc_xmodem( buffer, size) == ((buffer[size]<<8)+buffer[size+1]))
return 1;
else
return 0;
}
static xmodem_state *pXMstate;
static int xmodem_init( UBYTE state )
{
pXMstate = malloc( sizeof( xmodem_state ) );
// If we still don't have a state structure allocated, there's an
// error...
if( NULL == pXMstate ) {
return( XMODEM_ERROR_NOMEMORY );
}
// Now we can set up the receive state for the first time..
pXMstate->ready = XMODEM_READY_IDLE;
pXMstate->flush = 0;
pXMstate->seqNum = 1; // Sequence number starts at 1
pXMstate->response = 'C'; // Solicit a connection with CRC
pXMstate->retries = XMODEM_RETRIES;
pXMstate->expiry = ISRTicks + XMODEM_TIMEOUT;
pXMstate->pktBytes = 0;
pXMstate->state = state;
return( XMODEM_ERROR_NONE );
}
static int xmodem_done( void )
{
free( pXMstate );
pXMstate = NULL;
}
static UBYTE xmodem_updateretry( UBYTE newState )
{
pXMstate->state = newState;
if( pXMstate->retries ) {
pXMstate->retries--;
if( (0 == pXMstate->retries) && (pXMstate->state != XMODEM_SEND_ACK) ) {
pXMstate->state = XMODEM_ABORT;
pXMstate->retries = 4;
}
}
}
static int xmodem_processinput( void )
{
if( NULL == pXMstate ) {
return( 0 );
}
pXMstate->process = 0;
if( EOF != (pXMstate->input = xmodem_in()) ) {
// We have a character, process it if we're not flushing the input stream
if( !pXMstate->flush ) {
pXMstate->expiry = ISRTicks + XMODEM_TIMEINC;
pXMstate->process = 1;
}
} else {
// We have no character, check to see if we've timed out
if( pXMstate->expiry <= ISRTicks ) {
// We have timed out, so update the next timeout, unconditionally clear
// the flush state, set the timeout state, and update the retry counter
pXMstate->expiry += XMODEM_TIMEINC;
pXMstate->flush = 0;
pXMstate->process = 1;
xmodem_updateretry( pXMstate->state );
}
}
return( pXMstate->process || (pXMstate->state & XMODEM_FORCE) );
}
int xmodem_recv( const char **string )
{
if( NULL == pXMstate ) {
xmodem_init( XMODEM_RECV_QUERY );
}
if( NULL == string ) {
xmodem_out( CAN );
xmodem_out( CAN );
xmodem_done();
}
while( xmodem_processinput() ) {
// Check for input and process only if we have a character or a timeout...
switch( pXMstate->state ) {
case XMODEM_RECV_QUERY: xmodem_out( pXMstate->response );
pXMstate->state = XMODEM_RECV_REPLY;
break;
case XMODEM_RECV_REPLY: switch( pXMstate->input ) {
case EOF: pXMstate->state = XMODEM_RECV_QUERY;
break;
case SOH: pXMstate->buffer[0] = SOH;
pXMstate->pktBytes = 1;
pXMstate->state = XMODEM_RECV_PART;
break;
case EOT: pXMstate->flush = 1;
pXMstate->response = ACK;
pXMstate->state = XMODEM_RECV_DONE;
break;
case CAN: pXMstate->state = XMODEM_RECV_CANCEL;
break;
default: break;
}
break;
case XMODEM_RECV_PART: switch( pXMstate->input ) {
case EOF: pXMstate->response = NAK;
pXMstate->state = XMODEM_RECV_QUERY;
break;
default: // We've got a character - add it to the packet received
// so far...
do {
pXMstate->buffer[pXMstate->pktBytes++] = (unsigned char)(pXMstate->input);
if( (XMODEM_PKTSIZE+XMODEM_CRCFLAG+4) == pXMstate->pktBytes ) {
pXMstate->state = XMODEM_RECV_PACKET;
} else {
pXMstate->input = xmodem_in();
}
} while( (EOF != pXMstate->input) && (XMODEM_RECV_PACKET != pXMstate->state ) );
break;
}
break;
case XMODEM_RECV_PACKET: // We've got a full packet, so do a consistency check on the sequence number
// and the CRC
if( (pXMstate->buffer[1] == (unsigned char)(~pXMstate->buffer[2]) ) &&
xmodem_crc_check( &pXMstate->buffer[3], XMODEM_PKTSIZE ) ) {
if( pXMstate->buffer[1] == pXMstate->seqNum ) {
// Is this the packet we are waiting for?
pXMstate->seqNum++;
pXMstate->ready = XMODEM_READY_NEWRX;
pXMstate->retries = XMODEM_RETRIES;
pXMstate->response = ACK;
pXMstate->state = XMODEM_RECV_QUERY;
*string = &pXMstate->buffer[3];
} else if( pXMstate->buffer[1] == (unsigned char)(pXMstate->seqNum - 1) ) {
// Retry of previous packet, acknowledge and move on...
pXMstate->response = ACK;
pXMstate->state = XMODEM_RECV_QUERY;
} else {
// We're totally out of sync - abort!
pXMstate->state = XMODEM_ABORT;
}
} else {
// Packet was corrupt - NAK it, flush the input stream for one timeout
// period and try again
pXMstate->response = NAK;
pXMstate->flush = 1;
xmodem_updateretry( XMODEM_RECV_QUERY );
}
break;
case XMODEM_RECV_CANCEL: switch( pXMstate->input ) {
case EOF: // No character recieved, ignore for now
break;
case CAN: // This is two CAN bytes in a row - ACK it, flush the input
// stream
pXMstate->flush = 1;
pXMstate->response = ACK;
pXMstate->state = XMODEM_RECV_DONE;
break;
default: // Some other character was received, reset the machine
pXMstate->flush = 1;
pXMstate->response = NAK;
pXMstate->state = XMODEM_RECV_QUERY;
break;
}
break;
case XMODEM_ABORT: pXMstate->flush = 1;
xmodem_out( CAN );
xmodem_out( CAN );
pXMstate->response = CAN;
pXMstate->state = XMODEM_RECV_DONE;
break;
case XMODEM_RECV_DONE: xmodem_out( pXMstate->response );
xmodem_done();
break;
default: break;
}
}
return( (NULL == pXMstate) ? XMODEM_READY_DONE : pXMstate->ready );
}
int xmodem_send( const char *string, size_t len )
{
// First, check if we're sending for the first time, and set up
// accordingly...
if( NULL == pXMstate ) {
xmodem_init( XMODEM_SEND_QUERY );
}
if( xmodem_processinput() ) {
switch( pXMstate->state ) {
case XMODEM_SEND_QUERY: switch( pXMstate->input ) {
case 'C': pXMstate->state = XMODEM_SEND_PACKET;
pXMstate->ready = XMODEM_READY_NEWTX;
break;
case CAN: pXMstate->state = XMODEM_SEND_CANCEL;
break;
default: // Should we flush if we get a garbage character here?
break;
}
break;
case XMODEM_SEND_PACKET: if( pXMstate->ready == XMODEM_READY_TX) {
// Ready to send or retry a packet
UWORD crc = 0;
xmodem_out( SOH );
xmodem_out( pXMstate->seqNum );
xmodem_out( ~pXMstate->seqNum );
for( pXMstate->pktBytes = 3; pXMstate->pktBytes < XMODEM_PKTSIZE+3; pXMstate->pktBytes++ ) {
xmodem_out( pXMstate->buffer[pXMstate->pktBytes] );
}
crc = crc_xmodem(&(pXMstate->buffer[3]),XMODEM_PKTSIZE);
xmodem_out( (crc>>8) & 0xFF );
xmodem_out( (crc ) & 0xFF );
pXMstate->state = XMODEM_SEND_WAIT;
} else if( (XMODEM_READY_NEWTX == pXMstate->ready) && string ) {
// New packet is ready to be copied into the local buffer
memset( pXMstate->buffer, 0, sizeof( pXMstate->buffer ) );
len = (len > XMODEM_PKTSIZE) ? XMODEM_PKTSIZE : len;
for( pXMstate->pktBytes = 3; pXMstate->pktBytes < len+3; pXMstate->pktBytes++ ) {
pXMstate->buffer[pXMstate->pktBytes] = *string++;
}
pXMstate->ready = XMODEM_READY_TX;
} else {
// No more data from application
pXMstate->state = XMODEM_SEND_CLOSE;
}
break;
case XMODEM_SEND_WAIT: switch( pXMstate->input ) {
case ACK: pXMstate->seqNum++;
pXMstate->retries = XMODEM_RETRIES;
pXMstate->state = XMODEM_SEND_PACKET;
pXMstate->ready = XMODEM_READY_NEWTX;
break;
case NAK: xmodem_updateretry( XMODEM_SEND_PACKET );
break;
case CAN: pXMstate->state = XMODEM_SEND_CANCEL;
break;
default: // Should we flush if we get a garbage character here?
xmodem_updateretry( XMODEM_SEND_PACKET );
break;
}
break;
case XMODEM_SEND_CANCEL: switch( pXMstate->input ) {
case EOF: // No character recieved, ignore for now
break;
case CAN: // This is two CAN bytes in a row - cancel by receiver
xmodem_out( ACK );
xmodem_done ();
break;
default: // Should we flush if we get a garbage character here?
// Some other character was received, look for correct response
pXMstate->state = XMODEM_SEND_WAIT;
break;
}
break;
case XMODEM_ABORT: pXMstate->response = CAN;
xmodem_out( pXMstate->response );
xmodem_out( pXMstate->response );
pXMstate->state = XMODEM_SEND_ACK;
break;
case XMODEM_SEND_CLOSE: pXMstate->response = EOT;
xmodem_out( pXMstate->response );
pXMstate->state = XMODEM_SEND_ACK;
break;
case XMODEM_SEND_ACK: switch( pXMstate->input ) {
case ACK: xmodem_done();
break;
default: xmodem_out( pXMstate->response );
xmodem_out( pXMstate->response );
if( 0 == pXMstate->retries ) {
xmodem_done();
}
}
break;
}
}
return( (NULL == pXMstate) ? XMODEM_READY_DONE : pXMstate->ready );
}