/*#############################################################################
#
# ipburn.c - Interphase Corp, CBII FLASH Burn Utility
#
# Target port specifics:
#
#   1. provide functions "phy2vm" & "phyfree"
#   2. provide appropiate OS #include files
#   3. provide default Short IO #define
#
#############################################################################*/

#ifdef LynxOS
/******************************************************************************
 * LYNXOS -Target Specifics
 *****************************************************************************/

#include <kernel.h>
#include <stdio.h>
#include <file.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sysdev.h>
#include <mem.h>
#include <smem.h>

typedef unsigned short	U16;		/* D16 Short IO accesses	*/
typedef unsigned int	HW32;

#define	DEFAULT_SHIO	0xffff6000	/* default Short IO address	*/
#define SHIO_SIZE	512		/* V/ATM Short IO size	*/
#endif /* LYNXOS */

#ifdef HPRT
/******************************************************************************
 * HPRT -Target Specifics
 *****************************************************************************/

#include <stdio.h>
#include <file.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/shmmap.h>

typedef unsigned short	U16;		/* D16 Short IO accesses	*/
typedef volatile int HW32;

#define	DEFAULT_SHIO	0xffff6000	/* default Short IO address	*/
#define SHIO_SIZE	512		/* V/ATM Short IO size	*/
#endif /* HPRT */

/******************************************************************************
 * should be Target independent
 *****************************************************************************/

extern	char	*optarg;		/* getopt() ascii string ptr	*/
extern	int	 optind;		/* getopt() argv[index]		*/
extern	int	 opterr;		/* getopt() error number	*/
extern	int	 errno;

/******************************************************************************
 * Locals
 *****************************************************************************/

static	int	 reset();
static	int	 phy2vm();
static	int	 phyfree();

#define CB_OK		0x43424f4b	/* CB OK indication		*/
#define CB_FAIL		0x4641494c	/* CB FAIL indication		*/
#define CB_DNLD		0x444e4c44	/* CB DNLD command		*/
#define CB_BURN		0x4255524e	/* CB BURN command		*/
#define	SBUF_LEN	256		/* Srecord buffer length	*/
#define SPINMASK	0x1f		/* spinner every N'th srecord	*/
#define SPINCNT		(32 * 1024)	/* burn spin count		*/

static	FILE	*fd;			/* file descriptor		*/
static	HW32	*cb;			/* CB_START pointer		*/
static	HW32	*vm;			/* Short IO virtual address	*/
static	char	*spin;			/* spinner pointer		*/
static	int	 shio;			/* Short IO physical address	*/
static	int	 srec;			/* Short IO SRecord load ptr	*/
static	int	 scnt;			/* spinner counter		*/
static	int	 bufa;			/* aligned buffer pointer	*/
static	char	 sbuf[SBUF_LEN+2];	/* Srecord buffer (+alignment)	*/
static	char	 spinner[] = {"|/-|"};	/* console spinner characters	*/

/******************************************************************************
 * main
 *****************************************************************************/

int
main(argc, argv)

	int   argc;
	char **argv;
{
	U16  *s, *d, p;
	char *c,  b;
	int   x,  cnt;

	shio  = DEFAULT_SHIO;

	/*=====================================================================
	 * command line options
	 *===================================================================*/

	while ((x = getopt(argc, argv, "a:")) != -1) {

		switch (x) {

			case 'a':
				shio = strtoul(optarg, (char **)0, 16);
				break;

			case '?':
				fprintf(stderr, "\nusage: %s [-a address] filename\n\n",
						argv[0]);
				return 0;
		}
	}

	/*=====================================================================
	 * map Short IO
	 *===================================================================*/

	if (-1 == (int)(vm = (HW32 *)phy2vm(shio))) {
		fprintf(stderr, "%s: unable to open Short IO address 0x%x\n",
			argv[0], shio);
		return 1;
	}

	/*=====================================================================
	 * open file
	 *===================================================================*/

	if (NULL == (fd = fopen(argv[optind], "r"))) {
		fprintf(stderr, "%s: unable to open file %s\n",
			argv[0], argv[optind]);
		phyfree();
		return 1;
	}

	/*=====================================================================
	 * reset controller, activating CBII
	 *===================================================================*/

	fprintf(stderr, "CB addr: phys=0x%x vm=0x%x\n", shio, vm);
	reset();

	/*=====================================================================
	 * download Secords - 1 record at a time
	 *
	 *  -Short IO accesses should not be D8 type, so count the number of
	 *   bytes per record, and put into Short IO via D16 xfers
	 *===================================================================*/

	fprintf(stderr, "CB dnld: ");
	spin = &spinner[0];
	scnt = 1;
	bufa = ((int)&sbuf[0] + 1) & ~1;

	while (NULL != fgets((char *)bufa, SBUF_LEN, fd)) {

		c   = (char *)bufa;
		x   = 0;
		cnt = SBUF_LEN;

		while (1) {

			++x;

			b = *c++;

			if ((b == '\n') || (b == '\r'))
				break;

			if (--cnt == 0)
				break;
		}

		if (!cnt) {
			fprintf(stderr, "ERROR! -record %d too long\n", scnt);
			phyfree();
			return 1;
		}

		x = ((x + 1) & ~1) / 2;
		for (s = (U16 *)bufa, d = (U16 *)srec; x; *d++ = *s++, --x);

		*cb = CB_DNLD;

		while (CB_OK != (x = *cb)) {

			if (CB_FAIL == x) {
				fprintf(stderr, "FAILED! record %d\n", scnt);
				phyfree();
				return 1;
			}
		}

		if ( !(++scnt & SPINMASK) ) {
		    fprintf(stderr, "%c\b", *spin++);
		    if (*spin == 0)
			spin = &spinner[0];
		}
	    }
	fprintf(stderr, "%d records\n", scnt-1);
	
	/*=====================================================================
	 * BURN flash
	 *===================================================================*/
	
	*cb  = CB_BURN;
	scnt = SPINCNT;
	
	fprintf(stderr, "CB burn: ");
	
	while (CB_OK != (x = *cb)) {

	    if (x == CB_FAIL) {
		fprintf(stderr, "FAILED!\n");
		phyfree();
		return 1;
	    }
	    if (++scnt > SPINCNT) {
		fprintf(stderr, "%c\b", *spin++);
		if (*spin == 0)
		    spin = &spinner[0];
		scnt = 0;
	    }
	}
	fprintf(stderr, "complete\n");
	
	/*=====================================================================
	 * done
	 *===================================================================*/
	
	reset();
	phyfree();
	return 0;
    }

/******************************************************************************
 * Reset controller & set CB pointers
 *
 *   -returns 0 if successful
 *****************************************************************************/


static int
reset()
{
    char *s;
    HW32 c, x;
    HW32 *lvm = vm;
    /*=====================================================================
     * reset controller
     *===================================================================*/

    *vm = 0xffffffff;
    for (c = 0x1000; c; --c);	/* hold active */
    *vm = 0;

    /*=====================================================================
     * wait for CB herald (compare 2 heralds)
     *===================================================================*/

    while (1) {

	c = *vm;
	x = *(vm + 16);
	if ((c == x) && (0xcb000000 == (c & 0xff000000)))
	    break;
    }

    /*=====================================================================
     * set CB pointers & wait CBOK
     *===================================================================*/

    cb = (HW32 *)(((c & 0x00ff0000) >> 14) + (int)vm);
    srec = (int)cb + 8;

    while (CB_OK != *cb);	/* wait for CBOK */

    /*=====================================================================
     * dump CB info
     *===================================================================*/

    fprintf(stderr, "CB info: ");
    for (s = (char *)((int)cb + 4), c = 32; c; fprintf(stderr, "%c", *s++), --c);
    fprintf(stderr, "\n");

    return 0;
}

#ifdef LYNXOS
/******************************************************************************
 * Short IO physical to virtual
 *
 *   -returns virtual address or -1 if unable to map
 *****************************************************************************/

#define ALLOC_SIZE	((SHIO_SIZE + (PAGESIZE-1)) & ~(PAGESIZE-1))
#define PERM_FLAGS	(SM_READ | SM_WRITE | SM_SHORTIO)

static int  shiovm;
static char ipid[16];

static int
phy2vm(address)
    int address;
{
    int vadr = address - VMESBASE;

    sprintf(ipid, "%x", getpid());

    if (NULL == (vadr = (int)smem_create(ipid, vadr, ALLOC_SIZE, PERM_FLAGS)))
	vadr = -1;

    shiovm = vadr;
    return (vadr);
}

static int
phyfree()
{
    smem_create(0, shiovm, 0, SM_DETACH);
    smem_remove(ipid);
}
#endif /* LYNXOS */


#ifdef HPRT
/******************************************************************************
 * Short IO physical to virtual
 *
 *   -returns virtual address or -1 if unable to map
 *****************************************************************************/

#define ALLOC_SIZE	((SHIO_SIZE + (PAGESIZE-1)) & ~(PAGESIZE-1))
#define PERM_FLAGS	(SHM_READ | SHM_WRITE | SHM_SHORTIO)

static int  shiovm;
static char ipid[16];

static int
phy2vm(address)
    int address;
{
    int fd = open("/dev/shio",O_RDWR);
    void *va;
    extern int errno;

    if (fd < 0) {
	perror("/dev/shio");
	exit(1);
    }

    va = shmmap(fd, 0x0, 0x1000, 0x6000, SHM_READ|SHM_WRITE);
    printf("shmmap returned va=%x, errno=%d\n",va, errno);

    shiovm = va;
    return (va);
}

static int
phyfree()
{
#ifdef notdef
    smem_create(0, shiovm, 0, SHM_DETACH);
    smem_remove(ipid);
#endif
}
#endif /* HPRT */

