Skip to content
Snippets Groups Projects
load.c 24.6 KiB
Newer Older
Wolfgang Denk's avatar
Wolfgang Denk committed
/*
 * (C) Copyright 2000-2004
Wolfgang Denk's avatar
Wolfgang Denk committed
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * SPDX-License-Identifier:	GPL-2.0+
Wolfgang Denk's avatar
Wolfgang Denk committed
 */

/*
 * Serial up- and download support
 */
#include <common.h>
#include <command.h>
#include <console.h>
Wolfgang Denk's avatar
Wolfgang Denk committed
#include <s_record.h>
#include <net.h>
Wolfgang Denk's avatar
Wolfgang Denk committed

Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_CMD_LOADB)
Angus Ainslie's avatar
Angus Ainslie committed
static ulong load_serial_ymodem(ulong offset, int mode);
#if defined(CONFIG_CMD_LOADS)
static ulong load_serial(long offset);
static int read_record(char *buf, ulong len);
# if defined(CONFIG_CMD_SAVES)
static int save_serial(ulong offset, ulong size);
static int write_record(char *buf);
Wolfgang Denk's avatar
Wolfgang Denk committed

static int do_echo = 1;
Wolfgang Denk's avatar
Wolfgang Denk committed

/* -------------------------------------------------------------------- */

#if defined(CONFIG_CMD_LOADS)
static int do_load_serial(cmd_tbl_t *cmdtp, int flag, int argc,
			  char * const argv[])
Wolfgang Denk's avatar
Wolfgang Denk committed
{
	long offset = 0;
Wolfgang Denk's avatar
Wolfgang Denk committed
	ulong addr;
	int i;
	char *env_echo;
	int rcode = 0;
#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
Wolfgang Denk's avatar
Wolfgang Denk committed
	int load_baudrate, current_baudrate;

	load_baudrate = current_baudrate = gd->baudrate;
#endif

	if (((env_echo = getenv("loads_echo")) != NULL) && (*env_echo == '1')) {
		do_echo = 1;
	} else {
		do_echo = 0;
	}

#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
Wolfgang Denk's avatar
Wolfgang Denk committed
	if (argc >= 2) {
		offset = simple_strtol(argv[1], NULL, 16);
Wolfgang Denk's avatar
Wolfgang Denk committed
	}
	if (argc == 3) {
		load_baudrate = (int)simple_strtoul(argv[2], NULL, 10);

		/* default to current baudrate */
		if (load_baudrate == 0)
			load_baudrate = current_baudrate;
	}
	if (load_baudrate != current_baudrate) {
		printf("## Switch baudrate to %d bps and press ENTER ...\n",
Wolfgang Denk's avatar
Wolfgang Denk committed
			load_baudrate);
		udelay(50000);
		gd->baudrate = load_baudrate;
		serial_setbrg();
Wolfgang Denk's avatar
Wolfgang Denk committed
		udelay(50000);
		for (;;) {
			if (getc() == '\r')
				break;
		}
	}
#else	/* ! CONFIG_SYS_LOADS_BAUD_CHANGE */
Wolfgang Denk's avatar
Wolfgang Denk committed
	if (argc == 2) {
		offset = simple_strtol(argv[1], NULL, 16);
Wolfgang Denk's avatar
Wolfgang Denk committed
	}
#endif	/* CONFIG_SYS_LOADS_BAUD_CHANGE */
Wolfgang Denk's avatar
Wolfgang Denk committed

	printf("## Ready for S-Record download ...\n");
Wolfgang Denk's avatar
Wolfgang Denk committed

	addr = load_serial(offset);
Wolfgang Denk's avatar
Wolfgang Denk committed

	/*
	 * Gather any trailing characters (for instance, the ^D which
	 * is sent by 'cu' after sending a file), and give the
	 * box some time (100 * 1 ms)
	 */
	for (i=0; i<100; ++i) {
		if (tstc()) {
			(void) getc();
Wolfgang Denk's avatar
Wolfgang Denk committed
		}
		udelay(1000);
	}

	if (addr == ~0) {
		printf("## S-Record download aborted\n");
Wolfgang Denk's avatar
Wolfgang Denk committed
		rcode = 1;
	} else {
		printf("## Start Addr      = 0x%08lX\n", addr);
Wolfgang Denk's avatar
Wolfgang Denk committed
		load_addr = addr;
	}

#ifdef	CONFIG_SYS_LOADS_BAUD_CHANGE
Wolfgang Denk's avatar
Wolfgang Denk committed
	if (load_baudrate != current_baudrate) {
		printf("## Switch baudrate to %d bps and press ESC ...\n",
Wolfgang Denk's avatar
Wolfgang Denk committed
			current_baudrate);
		udelay(50000);
Wolfgang Denk's avatar
Wolfgang Denk committed
		gd->baudrate = current_baudrate;
		serial_setbrg();
		udelay(50000);
Wolfgang Denk's avatar
Wolfgang Denk committed
		for (;;) {
			if (getc() == 0x1B) /* ESC */
				break;
		}
	}
#endif
	return rcode;
}

static ulong load_serial(long offset)
Wolfgang Denk's avatar
Wolfgang Denk committed
{
	char	record[SREC_MAXRECLEN + 1];	/* buffer for one S-Record	*/
	char	binbuf[SREC_MAXBINLEN];		/* buffer for binary data	*/
	int	binlen;				/* no. of data bytes in S-Rec.	*/
	int	type;				/* return code for record type	*/
	ulong	addr;				/* load address from S-Record	*/
	ulong	size;				/* number of bytes transferred	*/
	ulong	store_addr;
	ulong	start_addr = ~0;
	ulong	end_addr   =  0;
	int	line_count =  0;

	while (read_record(record, SREC_MAXRECLEN + 1) >= 0) {
		type = srec_decode(record, &binlen, &addr, binbuf);
Wolfgang Denk's avatar
Wolfgang Denk committed

		if (type < 0) {
			return (~0);		/* Invalid S-Record		*/
		}

		switch (type) {
		case SREC_DATA2:
		case SREC_DATA3:
		case SREC_DATA4:
		    store_addr = addr + offset;
#ifndef CONFIG_SYS_NO_FLASH
Wolfgang Denk's avatar
Wolfgang Denk committed
		    if (addr2info(store_addr)) {
			int rc;

Wolfgang Denk's avatar
Wolfgang Denk committed
			rc = flash_write((char *)binbuf,store_addr,binlen);
Wolfgang Denk's avatar
Wolfgang Denk committed
			if (rc != 0) {
				flash_perror(rc);
Wolfgang Denk's avatar
Wolfgang Denk committed
				return (~0);
			}
		    } else
#endif
		    {
			memcpy((char *)(store_addr), binbuf, binlen);
Wolfgang Denk's avatar
Wolfgang Denk committed
		    }
		    if ((store_addr) < start_addr)
			start_addr = store_addr;
		    if ((store_addr + binlen - 1) > end_addr)
			end_addr = store_addr + binlen - 1;
		    break;
		case SREC_END2:
		case SREC_END3:
		case SREC_END4:
		    udelay(10000);
Wolfgang Denk's avatar
Wolfgang Denk committed
		    size = end_addr - start_addr + 1;
		    printf("\n"
Wolfgang Denk's avatar
Wolfgang Denk committed
			    "## First Load Addr = 0x%08lX\n"
			    "## Last  Load Addr = 0x%08lX\n"
			    "## Total Size      = 0x%08lX = %ld Bytes\n",
			    start_addr, end_addr, size, size
		    );
		    flush_cache(start_addr, size);
		    setenv_hex("filesize", size);
Wolfgang Denk's avatar
Wolfgang Denk committed
		    return (addr);
		case SREC_START:
		    break;
		default:
		    break;
		}
		if (!do_echo) {	/* print a '.' every 100 lines */
			if ((++line_count % 100) == 0)
				putc('.');
Wolfgang Denk's avatar
Wolfgang Denk committed
		}
	}

	return (~0);			/* Download aborted		*/
}

Loading
Loading full blame...