📄 tftpc.c
字号:
* server to send next data block.
* If all attempts are exhausted, it returns with
* TFTP_TIMEOUT.
*
* Note: By default, this funciton uses "octet" or binary
* mode of file transfer.
********************************************************************/
TFTP_RESULT TFTPIsGetReady(void)
{
WORD_VAL opCode;
WORD_VAL blockNumber;
BOOL bTimeOut;
// Check to see if timeout has occurred.
bTimeOut = FALSE;
if ( TickGetDiff(TickGet(), _tftpStartTick) >= TFTP_GET_TIMEOUT_VAL )
{
bTimeOut = TRUE;
_tftpStartTick = TickGet();
}
switch(_tftpState)
{
case SM_TFTP_WAIT_FOR_DATA:
// If timeout occurs in this state, it may be because, we have not
// even received very first data block or some in between block.
if ( bTimeOut == TRUE )
{
bTimeOut = FALSE;
if ( _tftpRetries++ > (TFTP_MAX_RETRIES-1) )
{
DEBUG(printf("TFTPIsGetReady(): Timeout.\n"));
// Forget about all previous attempts.
_tftpRetries = 1;
return TFTP_TIMEOUT;
}
// If we have not even received first block, ask application
// retry.
if ( MutExVar.group2._tftpBlockNumber.Val == 1 )
{
// DEBUG(printf("TFTPIsGetReady(): TFTPOpen Retry.\n"));
return TFTP_RETRY;
}
else
{
DEBUG(printf("TFTPIsGetReady(): ACK Retry #%d...,\n", _tftpRetries));
// Block number was already incremented in last ACK attempt,
// so decrement it.
MutExVar.group2._tftpBlockNumber.Val--;
// Do it.
_tftpState = SM_TFTP_SEND_ACK;
break;
}
}
// For Read operation, server will respond with data block.
if ( !UDPIsGetReady(_tftpSocket) )
break;
// Get opCode
UDPGet(&opCode.byte.MSB);
UDPGet(&opCode.byte.LSB);
// Get block number.
UDPGet(&blockNumber.byte.MSB);
UDPGet(&blockNumber.byte.LSB);
// In order to read file, this must be data with block number of 0.
if ( opCode.Val == TFTP_OPCODE_DATA )
{
// Make sure that this is not a duplicate block.
if ( MutExVar.group2._tftpBlockNumber.Val == blockNumber.Val )
{
// Mark that we have not acked this block.
_tftpFlags.bits.bIsAcked = FALSE;
// Since we have a packet, forget about previous retry count.
_tftpRetries = 1;
_tftpState = SM_TFTP_READY;
return TFTP_OK;
}
// If received block has already been received, simply ack it
// so that Server can "get over" it and send next block.
else if ( MutExVar.group2._tftpBlockNumber.Val > blockNumber.Val )
{
DEBUG(printf("TFTPIsGetReady(): "\
"Duplicate block %d received - droping it...\n", \
blockNumber.Val));
MutExVar.group2._tftpDuplicateBlock.Val = blockNumber.Val;
_tftpState = SM_TFTP_DUPLICATE_ACK;
}
#if defined(TFTP_DEBUG)
else
{
DEBUG(printf("TFTPIsGetReady(): "\
"Unexpected block %d received - droping it...\n", \
blockNumber.Val));
}
#endif
}
// Discard all unexpected and error blocks.
UDPDiscard();
// If this was an error, remember error code for later delivery.
if ( opCode.Val == TFTP_OPCODE_ERROR )
{
_tftpError = blockNumber.Val;
return TFTP_ERROR;
}
break;
case SM_TFTP_DUPLICATE_ACK:
if ( UDPIsPutReady(_tftpSocket) )
{
_TFTPSendAck(MutExVar.group2._tftpDuplicateBlock);
_tftpState = SM_TFTP_WAIT_FOR_DATA;
}
break;
case SM_TFTP_READY:
if ( UDPIsGetReady(_tftpSocket) )
{
_tftpStartTick = TickGet();
return TFTP_OK;
}
// End of file is reached when data block is less than 512 bytes long.
// To reduce code, only MSB compared against 0x02 (of 0x200 = 512) to
// determine if block is less than 512 bytes long or not.
else if ( MutExVar.group2._tftpBlockLength.Val == 0 ||
MutExVar.group2._tftpBlockLength.byte.MSB < TFTP_BLOCK_SIZE_MSB )
_tftpState = SM_TFTP_SEND_LAST_ACK;
else
break;
case SM_TFTP_SEND_LAST_ACK:
case SM_TFTP_SEND_ACK:
if ( UDPIsPutReady(_tftpSocket) )
{
_TFTPSendAck(MutExVar.group2._tftpBlockNumber);
// This is the next block we are expecting.
MutExVar.group2._tftpBlockNumber.Val++;
// Remember that we have already acked current block.
_tftpFlags.bits.bIsAcked = TRUE;
if ( _tftpState == SM_TFTP_SEND_LAST_ACK )
return TFTP_END_OF_FILE;
_tftpState = SM_TFTP_WAIT_FOR_DATA;
}
break;
}
return TFTP_NOT_READY;
}
/*********************************************************************
* Function: BYTE TFTPGet(void)
*
* PreCondition: TFTPOpenFile() is called with TFTP_FILE_MODE_READ
* and TFTPIsGetReady() = TRUE
*
* Input: None
*
* Output: data byte as received from remote server.
*
* Side Effects: None
*
* Overview: Fetches next data byte from TFTP socket.
* If end of data block is reached, it issues
* ack to server so that next data block can be
* received.
*
* Note: Use this function to read file from server.
********************************************************************/
BYTE TFTPGet(void)
{
BYTE v = 0;
// Read byte from UDP
UDPGet(&v);
// Update block length
MutExVar.group2._tftpBlockLength.Val++;
// Check to see if entire data block is fetched.
// To reduce code, MSB is compared for 0x02 (of 0x200 = 512).
if ( MutExVar.group2._tftpBlockLength.byte.MSB == TFTP_BLOCK_SIZE_MSB )
{
// Entire block was fetched. Discard everything else.
UDPDiscard();
// Remember that we have flushed this block.
_tftpFlags.bits.bIsFlushed = TRUE;
// Reset block length.
MutExVar.group2._tftpBlockLength.Val = 0;
// Must send ACK to receive next block.
_tftpState = SM_TFTP_SEND_ACK;
}
return v;
}
/*********************************************************************
* Function: void TFTPCloseFile(void)
*
* PreCondition: TFTPOpenFile() was called and TFTPIsFileOpened()
* had returned with TFTP_OK.
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: If file is opened in read mode, it makes sure
* that last ACK is sent to server
* If file is opened in write mode, it makes sure
* that last block is sent out to server and
* waits for server to respond with ACK.
*
* Note: TFTPIsFileClosed() must be called to confirm
* if file was really closed.
********************************************************************/
void TFTPCloseFile(void)
{
// If a file was opened for read, we can close it immediately.
if ( _tftpFlags.bits.bIsReading )
{
// If this was premature close, make sure that we discard
// current block.
if ( !_tftpFlags.bits.bIsFlushed )
{
_tftpFlags.bits.bIsFlushed = TRUE;
UDPDiscard();
}
if ( _tftpFlags.bits.bIsAcked )
{
_tftpFlags.bits.bIsClosed = TRUE;
_tftpFlags.bits.bIsClosing = FALSE;
return;
}
else
{
_tftpState = SM_TFTP_SEND_LAST_ACK;
_tftpFlags.bits.bIsClosing = TRUE;
}
return;
}
// For write mode, if we have not flushed last block, do it now.
if ( !_tftpFlags.bits.bIsFlushed )
{
_tftpFlags.bits.bIsFlushed = TRUE;
UDPFlush();
}
// For write mode, if our last block was ack'ed by remote server,
// file is said to be closed.
if ( _tftpFlags.bits.bIsAcked )
{
_tftpFlags.bits.bIsClosed = TRUE;
_tftpFlags.bits.bIsClosing = FALSE;
return;
}
_tftpState = SM_TFTP_WAIT_FOR_ACK;
_tftpFlags.bits.bIsClosing = TRUE;
}
/*********************************************************************
* Function: TFTP_RESULT TFPTIsFileClosed(void)
*
* PreCondition: TFTPCloseFile() is already called.
*
* Input: None
*
* Output: TFTP_OK if file was successfully closdd
*
* TFTP_RETRY if file mode was Write and remote
* server did not receive last packet.
* Application must retry with last block.
*
* TFTP_TIMEOUT if all attempts were exhausted
* in closing file.
*
* TFTP_ERROR if remote server sent an error
* in response to last block.
* Actual error code may be read by calling
* TFTPGetError()
*
* TFTP_NOT_READY if file is not closed yet.
*
* Side Effects: None
*
* Overview: If file mode is Read, it simply makes that
* last block is acknowledged.
* If file mode is Write, it waits for server ack.
* If no ack was received within specified timeout
* instructs appliaction to resend last block.
* It keeps track of retries and declares timeout
* all attempts were exhausted.
*
* Note: None
********************************************************************/
TFTP_RESULT TFTPIsFileClosed(void)
{
if ( _tftpFlags.bits.bIsReading )
return TFTPIsGetReady();
else
return TFTPIsPutReady();
}
/*********************************************************************
* Function: TFTP_RESULT TFTPIsPutReady(void)
*
* PreCondition: TFTPOpenFile() is called with TFTP_FILE_MODE_WRITE
* and TFTPIsFileOpened() returned with TRUE.
*
* Input: None
*
* Output: TFTP_OK if it is okay to write more data byte.
*
* TFTP_TIMEOUT if timeout occurred waiting for
* ack from server
*
* TFTP_RETRY if all server did not send ack
* on time and application needs to resend
* last block.
*
* TFTP_ERROR if remote server returned ERROR.
* Actual error code may be read by calling
* TFTPGetError()
*
* TFTP_NOT_READY if still waiting...
*
* Side Effects: None
*
* Overview: Waits for ack from server. If ack does not
* arrive within specified timeout, it it instructs
* application to retry last block by returning
* TFTP_RETRY.
*
* If all attempts are exhausted, it returns with
* TFTP_TIMEOUT.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -