/******************************************************************************
*
* Filename: loader_prompt.c
*
* Instantiation of the interactive loader functions.
*
* Revision information:
*
* 20AUG2004 kb_admin initial creation
* 12JAN2005 kb_admin massive changes for tftp, strings, and more
* 05JUL2005 kb_admin save tag address, and set registers on boot
*
* BEGIN_KBDD_BLOCK
* No warranty, expressed or implied, is included with this software. It is
* provided "AS IS" and no warranty of any kind including statutory or aspects
* relating to merchantability or fitness for any purpose is provided. All
* intellectual property rights of others is maintained with the respective
* owners. This software is not copyrighted and is intended for reference
* only.
* END_BLOCK
*
* $FreeBSD$
*****************************************************************************/
#include "at91rm9200_lowlevel.h"
#include "at91rm9200.h"
#include "emac.h"
#include "loader_prompt.h"
#include "env_vars.h"
#include "lib.h"
#include "spi_flash.h"
#include "ee.h"
/******************************* GLOBALS *************************************/
/*********************** PRIVATE FUNCTIONS/DATA ******************************/
static char inputBuffer[MAX_INPUT_SIZE];
static int buffCount;
// argv pointer are either NULL or point to locations in inputBuffer
static char *argv[MAX_COMMAND_PARAMS];
#define FLASH_OFFSET (0 * FLASH_PAGE_SIZE)
#define KERNEL_OFFSET (220 * FLASH_PAGE_SIZE)
#define KERNEL_LEN (6 * 1024 * FLASH_PAGE_SIZE)
static const char *backspaceString = "\010 \010";
static const command_entry_t CommandTable[] = {
{COMMAND_DUMP, "d"},
{COMMAND_EXEC, "e"},
{COMMAND_LOCAL_IP, "ip"},
{COMMAND_MAC, "m"},
{COMMAND_SERVER_IP, "server_ip"},
{COMMAND_TFTP, "tftp"},
{COMMAND_XMODEM, "x"},
{COMMAND_RESET, "R"},
{COMMAND_LOAD_SPI_KERNEL, "k"},
{COMMAND_REPLACE_KERNEL_VIA_XMODEM, "K"},
{COMMAND_REPLACE_FLASH_VIA_XMODEM, "I"},
{COMMAND_REPLACE_ID_EEPROM, "E"},
{COMMAND_FINAL_FLAG, 0}
};
/*
* .KB_C_FN_DEFINITION_START
* unsigned BuildIP(void)
* This private function packs the test IP info to an unsigned value.
* .KB_C_FN_DEFINITION_END
*/
static unsigned
BuildIP(void)
{
return ((p_ASCIIToDec(argv[1]) << 24) |
(p_ASCIIToDec(argv[2]) << 16) |
(p_ASCIIToDec(argv[3]) << 8) |
p_ASCIIToDec(argv[4]));
}
/*
* .KB_C_FN_DEFINITION_START
* int StringToCommand(char *cPtr)
* This private function converts a command string to a command code.
* .KB_C_FN_DEFINITION_END
*/
static int
StringToCommand(char *cPtr)
{
int i;
for (i = 0; CommandTable[i].command != COMMAND_FINAL_FLAG; ++i)
if (!strcmp(CommandTable[i].c_string, cPtr))
return (CommandTable[i].command);
return (COMMAND_INVALID);
}
/*
* .KB_C_FN_DEFINITION_START
* int BreakCommand(char *)
* This private function splits the buffer into separate strings as pointed
* by argv and returns the number of parameters (< 0 on failure).
* .KB_C_FN_DEFINITION_END
*/
static int
BreakCommand(char *buffer)
{
int pCount, cCount, state;
state = pCount = 0;
p_memset((char*)argv, 0, sizeof(argv));
for (cCount = 0; cCount < MAX_INPUT_SIZE; ++cCount) {
if (!state) {
/* look for next command */
if (!p_IsWhiteSpace(buffer[cCount])) {
argv[pCount++] = &buffer[cCount];
state = 1;
} else {
buffer[cCount] = 0;
}
} else {
/* in command, find next white space */
if (p_IsWhiteSpace(buffer[cCount])) {
buffer[cCount] = 0;
state = 0;
}
}
if (pCount >= MAX_COMMAND_PARAMS) {
return (-1);
}
}
return (pCount);
}
#if 0
static void
UpdateEEProm(int eeaddr)
{
char *addr = (char *)SDRAM_BASE + (1 << 20); /* Load to base + 1MB */
int len;
while ((len = xmodem_rx(addr)) == -1)
continue;
printf("\nDownloaded %u bytes.\n", len);
WriteEEPROM(eeaddr, 0, addr, len);
}
#endif
static void
UpdateFlash(int offset)
{
char *addr = (char *)SDRAM_BASE + (1 << 20); /* Load to base + 1MB */
int len, i, off;
while ((len = xmodem_rx(addr)) == -1)
continue;
printf("\nDownloaded %u bytes.\n", len);
for (i = 0; i < len; i+= FLASH_PAGE_SIZE) {
off = i + offset;
SPI_WriteFlash(off, addr + i, FLASH_PAGE_SIZE);
}
}
static void
LoadKernelFromSpi(char *addr)
{
int i, off;
for (i = 0; i < KERNEL_LEN; i+= FLASH_PAGE_SIZE) {
off = i + KERNEL_OFFSET;
SPI_ReadFlash(off, addr + i, FLASH_PAGE_SIZE);
}
}
/*
* .KB_C_FN_DEFINITION_START
* void ParseCommand(char *)
* This private function executes matching functions.
* .KB_C_FN_DEFINITION_END
*/
static void
ParseCommand(char *buffer)
{
int argc, i;
if ((argc = BreakCommand(buffer)) < 1)
return;
switch (StringToCommand(argv[0])) {
case COMMAND_DUMP:
// display boot commands
DumpBootCommands();
break;
case COMMAND_EXEC:
{
// "e <address>"
// execute at address
void (*execAddr)(unsigned, unsigned);
if (argc > 1) {
/* in future, include machtypes (MACH_KB9200 = 612) */
execAddr = (void (*)(unsigned, unsigned))
p_ASCIIToHex(argv[1]);
(*execAddr)(0, 612);
}
break;
}
case COMMAND_TFTP:
{
// "tftp <local_dest_addr filename>"
// tftp download
unsigned address = 0;
if (argc > 2)
address = p_ASCIIToHex(argv[1]);
TFTP_Download(address, argv[2]);
break;
}
case COMMAND_SERVER_IP:
// "server_ip <server IP 192 200 1 20>"
// set download server address
if (argc > 4)
SetServerIPAddress(BuildIP());
break;
case COMMAND_LOCAL_IP:
// "local_ip <local IP 192 200 1 21>
// set ip of this module
if (argc > 4)
SetLocalIPAddress(BuildIP());
break;
case COMMAND_MAC:
{
// "m <mac address 12 34 56 78 9a bc>
// set mac address using 6 byte values
unsigned char mac[6];
if (argc > 6) {
for (i = 0; i < 6; i++)
mac[i] = p_ASCIIToHex(argv[i + 1]);
EMAC_SetMACAddress(mac);
}
break;
}
case COMMAND_LOAD_SPI_KERNEL:
// "k <address>"
if (argc > 1)
LoadKernelFromSpi((char *)p_ASCIIToHex(argv[1]));
break;
case COMMAND_XMODEM:
// "x <address>"
// download X-modem record at address
if (argc > 1)
xmodem_rx((char *)p_ASCIIToHex(argv[1]));
break;
case COMMAND_RESET:
printf("Reset\n");
reset();
while (1) continue;
break;
case COMMAND_REPLACE_KERNEL_VIA_XMODEM:
printf("Updating KERNEL image\n");
UpdateFlash(KERNEL_OFFSET);
break;
case COMMAND_REPLACE_FLASH_VIA_XMODEM:
printf("Updating FLASH image\n");
UpdateFlash(FLASH_OFFSET);
break;
case COMMAND_REPLACE_ID_EEPROM:
{
char buf[25];
printf("Testing Config EEPROM\n");
EEWrite(0, "This is a test", 15);
EERead(0, buf, 15);
printf("Found '%s'\n", buf);
break;
}
default:
break;
}
printf("\n");
}
/*
* .KB_C_FN_DEFINITION_START
* void ServicePrompt(char)
* This private function process each character checking for valid commands.
* This function is only executed if the character is considered valid.
* Each command is terminated with NULL (0) or ''.
* .KB_C_FN_DEFINITION_END
*/
static void
ServicePrompt(char p_char)
{
if (p_char == '\r')
p_char = 0;
if (p_char == '\010') {
if (buffCount) {
/* handle backspace BS */
inputBuffer[--buffCount] = 0;
printf(backspaceString);
}
return;
}
if (buffCount < MAX_INPUT_SIZE - 1) {
inputBuffer[buffCount++] = p_char;
putchar(p_char);
}
if (!p_char) {
printf("\n");
ParseCommand(inputBuffer);
p_memset(inputBuffer, 0, MAX_INPUT_SIZE);
buffCount = 0;
printf("\n>");
}
}
/* ************************** GLOBAL FUNCTIONS ********************************/
/*
* .KB_C_FN_DEFINITION_START
* void Bootloader(void *inputFunction)
* This global function is the entry point for the bootloader. If the
* inputFunction pointer is NULL, the loader input will be serviced from
* the uart. Otherwise, inputFunction is called to get characters which
* the loader will parse.
* .KB_C_FN_DEFINITION_END
*/
void
Bootloader(int(*inputFunction)(int))
{
int ch = 0;
p_memset((void*)inputBuffer, 0, sizeof(inputBuffer));
buffCount = 0;
printf("\n>");
while (1)
if ((ch = ((*inputFunction)(0))) > 0)
ServicePrompt(ch);
}