⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tftp.c

📁 umon bootloader source code, support mips cpu.
💻 C
📖 第 1 页 / 共 3 页
字号:
/* tftp.c:
 *	This code supports the monitor's TFTP server.
 *	TFTP is covered under RFC 783.
 *
 *	General notice:
 *	This code is part of a boot-monitor package developed as a generic base
 *	platform for embedded system designs.  As such, it is likely to be
 *	distributed to various projects beyond the control of the original
 *	author.  Please notify the author of any enhancements made or bugs found
 *	so that all may benefit from the changes.  In addition, notification back
 *	to the author will allow the new user to pick up changes that may have
 *	been made by other users after this version of the code was distributed.
 *
 *	Note1: the majority of this code was edited with 4-space tabs.
 *	Note2: as more and more contributions are accepted, the term "author"
 *		   is becoming a mis-representation of credit.
 *
 *	Original author:	Ed Sutter
 *	Email:				esutter@lucent.com
 *	Phone:				908-582-2351
 */
#include "config.h"
#if INCLUDE_TFTP
#include "endian.h"
#include "genlib.h"
#include "cpuio.h"
#include "ether.h"
#include "tfs.h"
#include "tfsprivate.h"
#include "monflags.h"
#include "stddefs.h"
#include "flash.h"
#include "cli.h"
#include "timer.h"

#define MODE_NULL		0
#define MODE_NETASCII	1
#define MODE_OCTET		2

void ShowTftpStats();
int	SendTFTPData(struct ether_header *,ushort,uchar *,int);
int SendTFTPErr(struct ether_header *,short,char *,int);
int SendTFTPAck(struct ether_header *,ushort);
int SendTFTPRRQ(uchar *,uchar *,char *,char *,uchar *);


static	struct elapsed_tmr tftpTmr;

static ushort	tftpPrevBlock;	/* Keeps the value of the block number of
								 * the previous TFTP transaction.
								 */
static int TftpWrqMode;			/* Set to MODE_NETASCII, MODE_OCTET or
								 * MODE_NULL based on the incoming WRQ
								 * request
								 */
static int TftpChopCount;		/* Number of characters chopped from the
								 * incoming file because of NETASCII
								 * conversion (remove 0x0d).
								 */
static int TftpLastPktSize;
static uchar TftpLastPkt[TFTP_PKTOVERHEAD+TFTP_DATAMAX+4];
								/* Storage of last packet sent.  This is
								 * used if it is determined that the packet
								 * most recently sent must be sent again.
								 */

static long	TftpRetryTimeout;	/* Count used during a TFTP transfer to
								 * kick off a retransmission of a packet.
								 */
static ushort TftpRmtPort;		/* Remote port of tftp transfer */
static uchar *TftpAddr;			/* Current destination address used for tftp
								 * file transfer into local memory.
								 */
static char *TftpLTMptr;		/* List-to-mem pointer used when filename
								 * for RRQ is ".".
								 */
static int	TftpCount;			/* Running total number of bytes transferred
								 * for a particular tftp transaction.
								 */
static int	TftpState;			/* Used to keep track of the current state
								 * of a tftp transaction.
								 */
static int	TftpTurnedOff;		/* If non-zero, then tftp is disabled. */
static char TftpErrString[32];	/* Used to post a tftp error message. */
static char TftpTfsFname[TFSNAMESIZE+64];	/* Store name of WRQ destination
											 * file (plus flags & info).
											 */
/* ls_to_mem():
 * Turn the list of files into a string buffer that contains
 * each filename followed by a newline.  Each filename is the "full"
 * name, meaning that it will contain commas to delimit the flags and
 * info fields of the file.
 * Return a pointer to the allocated space.
 */
char *
ls_to_mem(void)
{
#if INCLUDE_TFS
	int		size;
	TFILE	*tfp;
	char 	*base, *bp, *info, *flags, fbuf[16];

	/* First determine how much memory will be needed to store
	 * the list of files...  Each filename in the list will consist 
	 * of the string formatted as "name,flags,info\n"...
	 */
	size = 0;
	tfp = (TFILE *)0;
	while((tfp = tfsnext(tfp))) {
		/* Increment size to include filename, 2 commas and a newline...
		 */
		size += (strlen(TFS_NAME(tfp)) + 3);

		/* Determine if flags and/or info field is to be present in
		 * the name, and add that to the size appropriately...
		 */
		flags = tfsflagsbtoa(TFS_FLAGS(tfp),fbuf);
		if (!fbuf[0])
			flags = 0;
		if (flags)
			size += strlen(flags);
		if (TFS_INFO(tfp))
			size += strlen(TFS_INFO(tfp));
	}

	if (size == 0)
		return(0);

	/* Allocate the space.  Add one additional byte to the size to
	 * account for the NULL termination at the end of the string...
	 */
	base = bp = malloc(size+1);				
	if (bp == (char *)0)
		return(0);

	/* Load the buffer with the list of names with their
	 * TFS extensions...
	 */
	tfp = (TFILE *)0;
	while((tfp = tfsnext(tfp))) {
		flags = tfsflagsbtoa(TFS_FLAGS(tfp),fbuf);
		if (!flags || !fbuf[0])
			flags = "";
		if (TFS_INFO(tfp))
			info = TFS_INFO(tfp);
		else
			info = "";

		bp += sprintf(bp,"%s,%s,%s\n",TFS_NAME(tfp),flags,info);
	}
	
	return(base);
#else
	return(0);
#endif
}

char *
tftpStringState(int state)
{
	switch(state) {
		case TFTPOFF:
			return("OFF");
		case TFTPIDLE:
			return("IDLE");
		case TFTPACTIVE:
			return("ACTIVE");
		case TFTPERROR:
			return("ERROR");
		case TFTPSENTRRQ:
			return("SENTRRQ");
		case TFTPTIMEOUT:
			return("TIMEOUT");
		default:
			return("???");
	}
}

int
tftpGotoState(int state)
{
	int	ostate;

	ostate = TftpState;
	TftpState = state;
	if ((EtherVerbose & SHOW_TFTP_STATE) && (state != ostate))
		printf("  TFTP State change %s -> %s\n",
			tftpStringState(ostate), tftpStringState(state));
	return(ostate);
}

/* tftpGet():
 *	Return size of file if successful; else 0.
 */
int
tftpGet(ulong addr,char *tftpsrvr,char *mode, char *hostfile,char *tfsfile,
	char *tfsflags,char *tfsinfo)
{
	int		done;
	char	buf[32];
	uchar	binip[8], binenet[8], *enetaddr;

	setenv("TFTPGET",0);

	/* Convert IP address to binary: */
	if (IpToBin(tftpsrvr,binip) < 0)
		return(0);


	/* Get the ethernet address for the IP: */
	/* Give ARP the same verbosity (if any) set up for TFTP: */
	if (EtherVerbose & SHOW_TFTP_STATE)
		EtherVerbose |= SHOW_ARP;
	enetaddr = ArpEther(binip,binenet,0);
	EtherVerbose &= ~SHOW_ARP;
	if (!enetaddr) {
		printf("ARP failed for %s\n",tftpsrvr);
		return(0);
	}

	printf("Retrieving %s from %s...\n",hostfile,tftpsrvr);

	/* Send the TFTP RRQ to initiate the transfer. */
	if (SendTFTPRRQ(binip,binenet,hostfile,mode,(uchar *)addr) < 0) {
		printf("RRQ failed\n");
		return(0);
	}

	/* Wait for TftpState to indicate that the transaction has completed... */
	done = 0;
	while(!done) {
		pollethernet();
		switch(TftpState) {
			case TFTPIDLE:
				//stone debug
				//printf("Rcvd %d bytes",TftpCount);
				done = 1;
				break;
			case TFTPERROR:
				printf("Host error: %s\n",TftpErrString);
				done = 2;
				break;
			case TFTPTIMEOUT:
				printf("Timing out (%d bytes rcvd)\n",TftpCount);
				done = 2;
				break;
			default:
				break;
		}
	}
	if (done == 2)
		return(0);
	
/*	stone debug ,dont use tfs .	
	if (tfsfile) {
		int	err, filesize;

		filesize = TftpCount - TftpChopCount;
		printf("\nAdding %s (size=%d) to TFS...",tfsfile,filesize);
		err = tfsadd(tfsfile,tfsinfo,tfsflags,(uchar *)addr,filesize);
		if (err != TFS_OKAY)
			printf("%s: %s\n",tfsfile,(char *)tfsctrl(TFS_ERRMSG,err,0));
	}
*/
	sprintf(buf,"%d",TftpCount);
	setenv("TFTPGET",buf);
	printf("Rcvd %d bytes\n",TftpCount);
	return(TftpCount);
}

/* tftpInit():
 *	Called by the ethenet initialization to initialize state variables.
 */
void
tftpInit()
{
	TftpCount = -1;
	TftpRmtPort = 0;
	TftpTurnedOff = 0;
	tftpGotoState(TFTPIDLE);
	TftpAddr = (uchar *)0;
}

/* storePktAndSend():
 *	The final stage in sending a TFTP packet...
 *	1. Compute IP and UDP checksums;
 *	2. Copy ethernet packet to a buffer so that it can be resent
 *		if necessary (by tftpStateCheck()).
 *	3. Store the size of the packet; 
 *	4. Send the packet out the interface.
 *	5. Reset the timeout count and re-transmission delay variables.
 */
void
storePktAndSend(struct ip *ipp, struct ether_header *epkt,int size)
{
	ipChksum(ipp);							/* Compute csum of ip hdr */
	udpChksum(ipp);							/* Compute UDP checksum */
											/* Copy packet to static buffer */
	memcpy((char *)TftpLastPkt,(char *)epkt,size);
	TftpLastPktSize = size;					/* Copy size to static location */
	sendBuffer(size);						/* Send buffer out ethernet i*/

	/* Re-initialize the re-transmission delay variables.
	 */
	TftpRetryTimeout = RetransmitDelay(DELAY_INIT_TFTP);
	startElapsedTimer(&tftpTmr,TftpRetryTimeout * 1000);
}

/* getTftpSrcPort():
 *	Each time a TFTP RRQ goes out, use a new source port number.
 *	Cycle through a block of 256 port numbers...
 */
ushort
getTftpSrcPort(void)
{
	if (TftpSrcPort < (IPPORT_TFTPSRC+256))
		TftpSrcPort++;
	else
		TftpSrcPort = IPPORT_TFTPSRC;
	return(TftpSrcPort);
}

/* tftpStateCheck():
 *	Called by the pollethernet function to support the ability to retry
 *	on a TFTP transmission that appears to have terminated prematurely
 *	due to a lost packet.  If a packet is sent and the response is not
 *	received within about 1-2 seconds, the packet is re-sent.  The retry
 *	will repeat 8 times; then give up and set the TFTP state to idle.
 *
 *	Taken from RFC 1350 section 2 "Overview of the Protocol"...
 *	... If a packet gets lost in the network, the intended recipient will
 *	timeout and may retransmit his last packet (which may be data or an
 *	acknowledgement), thus causing the sender of the lost packet to retransmit
 *	the lost packet.
 *
 *	Taken from RFC 1123 section 4.2.3.2 "Timeout Algorithms"...
 *	... a TFTP implementation MUST use an adaptive timeout ...
 */

void
tftpStateCheck(void)
{
	uchar	*buf;
	long	delay;
	ushort	tftp_opcode;
	struct	ip *ihdr;
	struct	Udphdr *uhdr;
	struct	ether_header *ehdr;

	switch(TftpState) {
		case TFTPIDLE:
		case TFTPTIMEOUT:
		case TFTPERROR:
			return;
		default:
			break;
	}

	/* If timeout occurs, re-transmit the packet...
	 */
	if (!msecElapsed(&tftpTmr))
		return;

	delay = RetransmitDelay(DELAY_INCREMENT);
	if (delay == RETRANSMISSION_TIMEOUT) {
		if (EtherVerbose & SHOW_TFTP_STATE)
			printf("  TFTP_RETRY giving up\n");
		tftpGotoState(TFTPTIMEOUT);
		enableBroadcastReception();
		return;
	}

	/* Get a transmit buffer and copy the packet that was last sent.
	 * Insert a new IP ID, recalculate the checksums and send it again...
	 * If the opcode of the packet to be re-transmitted is RRQ, then
	 * use a new port number.
	 */
	buf = (uchar *)getXmitBuffer();
	memcpy((char *)buf,(char *)TftpLastPkt,TftpLastPktSize);
	ehdr = (struct ether_header *)buf;
	ihdr = (struct ip *)(ehdr + 1);
	uhdr = (struct Udphdr *)(ihdr + 1);
	tftp_opcode = *(ushort *)(uhdr + 1);
	ihdr->ip_id = ipId();
	if (tftp_opcode == ecs(TFTP_RRQ)) {
		uhdr->uh_sport = getTftpSrcPort();
		self_ecs(uhdr->uh_sport);
	}
	ipChksum(ihdr);
	udpChksum(ihdr);
	sendBuffer(TftpLastPktSize);

	if (EtherVerbose & SHOW_TFTP_STATE)
		printf("  TFTP_RETRY (%ld secs)\n",TftpRetryTimeout);

	TftpRetryTimeout = delay;
	startElapsedTimer(&tftpTmr,TftpRetryTimeout * 1000);
	return;
}

/* tftpStartSrvrFilter():
 *	Called when either a TFTP_RRQ or TFTP_WRQ is received indicating that
 *	a client wants to start up a transfer.
 *	If TftpState is IDLE, ERROR or TIMEOUT, this means it is safe to start

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -