#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>

#include "/usr/src/linux/drivers/atm/suni.h"
#include "/usr/src/linux/drivers/atm/midway.h"
#include "/usr/src/linux/drivers/atm/tonga.h"


#define MEM_DEV		"/dev/mem"
#define BASE_ADDR	0xff400000


struct descr {
    const char *name;
    char shift;	/* -1 = flag */
    unsigned long mask; /* 0 = label */
    const char *table;
};

struct descr midway_regs[] = {
    { "Midway Reset/ID",	0, 			0,			NULL },
    { "Daughter board",		0,			DAUGTHER_ID,		NULL },
    { "UTOPIA type", 		-1,			MID_CON_V6,		"non-pipelined UTOPIA\0UTOPIA" },
    { "PHY type",		-1,			MID_CON_SUNI,		"UTOPIA\0SUNI" },
    { "PHY interface",		-1,			MID_CON_TI,		"Normal\0SABRE" },
    { "Mother board",		MID_MOTHER_SHIFT,	MID_MOTHER_ID,		NULL },
    { "Midway version",		MID_SHIFT,		MID_ID,			NULL },
    { "",			0,			0,			NULL },
    { "Interrupt Status",	0,			0,			NULL },
    { "Statistics overflow",	-1,			MID_STAT_OVFL,		NULL },
    { "SUNI",			-1,			MID_SUNI_INT,		NULL },
    { "Service list",		-1,			MID_SERVICE,		NULL },
    { "TX DMA complete",	-1,			MID_TX_DMA_COMPLETE,	NULL },
    { "RX DMA complete",	-1,			MID_RX_DMA_COMPLETE,	NULL },
    { "ERR ACK",		-1,			MID_DMA_ERR_ACK,	NULL },
    { "LERR ACK",		-1,			MID_DMA_LERR_ACK,	NULL },
    { "TX DMA ident mismatch",	-1,			MID_TX_IDENT_MISM,	NULL },
    { "TX DMA overflow",	-1,			MID_TX_DMA_OVFL,	NULL },
    { "TX 0 complete",		-1,			MID_TX_COMPLETE_0,	NULL },
    { "TX 1 complete",		-1,			MID_TX_COMPLETE_1,	NULL },
    { "TX 2 complete",		-1,			MID_TX_COMPLETE_2,	NULL },
    { "TX 3 complete",		-1,			MID_TX_COMPLETE_3,	NULL },
    { "TX 4 complete",		-1,			MID_TX_COMPLETE_4,	NULL },
    { "TX 5 complete",		-1,			MID_TX_COMPLETE_5,	NULL },
    { "TX 6 complete",		-1,			MID_TX_COMPLETE_6,	NULL },
    { "TX 7 complete",		-1,			MID_TX_COMPLETE_7,	NULL },
    { "Interrupt Enable",	0,			0,			NULL },
    { "Statistics overflow",	-1,			MID_STAT_OVFL,		NULL },
    { "SUNI",			-1,			MID_SUNI_INT,		NULL },
    { "Service list",		-1,			MID_SERVICE,		NULL },
    { "TX DMA complete",	-1,			MID_TX_DMA_COMPLETE,	NULL },
    { "RX DMA complete",	-1,			MID_RX_DMA_COMPLETE,	NULL },
    { "ERR ACK",		-1,			MID_DMA_ERR_ACK,	NULL },
    { "LERR ACK",		-1,			MID_DMA_LERR_ACK,	NULL },
    { "TX DMA ident mismatch",	-1,			MID_TX_IDENT_MISM,	NULL },
    { "TX DMA overflow",	-1,			MID_TX_DMA_OVFL,	NULL },
    { "TX 0 complete",		-1,			MID_TX_COMPLETE_0,	NULL },
    { "TX 1 complete",		-1,			MID_TX_COMPLETE_1,	NULL },
    { "TX 2 complete",		-1,			MID_TX_COMPLETE_2,	NULL },
    { "TX 3 complete",		-1,			MID_TX_COMPLETE_3,	NULL },
    { "TX 4 complete",		-1,			MID_TX_COMPLETE_4,	NULL },
    { "TX 5 complete",		-1,			MID_TX_COMPLETE_5,	NULL },
    { "TX 6 complete",		-1,			MID_TX_COMPLETE_6,	NULL },
    { "TX 7 complete",		-1,			MID_TX_COMPLETE_7,	NULL },
    { "Master Control/Status",	0,			0,			NULL },
    { "Wait 500us",		-1,			MID_WAIT_500US,		NULL },
    { "Wait 1ms",		-1,			MID_WAIT_1MS,		NULL },
    { "RX enabled",		-1,			MID_RX_ENABLE,		NULL },
    { "TX enabled",		-1,			MID_TX_ENABLE,		NULL },
    { "DMA enabled",		-1,			MID_DMA_ENABLE,		NULL },
    { "TX DMA overflow handling",-1,			MID_TX_LOCK_MODE,	"Streaming\0Lock" },
    { "Interrupt level",	MID_INT_SEL_SHIFT,	MID_INT_SELECT,		NULL },
    { "Statistics",		0,			0,			NULL },
    { "Overflow",		0,			MID_OVFL_TRASH,		NULL },
    { "Trashed",		MID_VCI_TRASH_SHIFT,	MID_VCI_TRASH,		NULL },
    { "Pointers",		0,			0,			NULL },
    { "Service write pointer",	0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "DMA address",		0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "DMA RX write",		0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "DMA RX read",		0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "DMA TX write",		0,			0xffffffff,		NULL },
    { "",			0,			0,			NULL },
    { "DMA TX read",		0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr tx_place[] = {
    { "Location",		0,			MID_LOCATION,		NULL },
    { "Size",			MID_SIZE_SHIFT,		MID_SIZE,		 "1kB\0002kB\0004kB\0008kB\00016kB\00032kB\00064kB\000128kB" },
    { NULL,			0,			0,			NULL }
};

struct descr tx_rdptr[] = {
    { "Read pointer",		0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr tx_descrstart[] = {
    { "Descriptor start",	0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr vci[] = {
    { "In service",		-1,			MID_VCI_IN_SERVICE,	NULL },
    { "Size",			MID_VCI_SIZE_SHIFT,	MID_VCI_SIZE,		"1kB\0002kB\0004kB\0008kB\00016kB\00032kB\00064kB\000128kB" },
    { "Location",		MID_VCI_LOCATION_SHIFT,	MID_VCI_LOCATION,	NULL },
    { "PTI mode",		-1,			MID_VCI_PTI_MODE,	"Trash\0Preserve" },
    { "Mode",			MID_VCI_MODE_SHIFT,	MID_VCI_MODE,		"Trash\0non AAL5\0AAL5\0???" },
    { "",			0,			0,			NULL },
    { "Read pointer",		MID_VCI_READ_SHIFT,	MID_VCI_READ,		NULL },
    { "Descriptor start",	MID_VCI_DESCR_SHIFT,	MID_VCI_DESCR,		NULL },
    { "",			0,			0,			NULL },
    { "Cell count",		MID_VCI_COUNT_SHIFT,	MID_VCI_COUNT,		NULL },
    { "State",			MID_VCI_STATE_SHIFT,	MID_VCI_STATE,		"Idle\0Reassembling\0???\0Trashing" },
    { "Write pointer",		MID_VCI_WRITE_SHIFT,	MID_VCI_WRITE,		NULL },
    { "",			0,			0,			NULL },
    { "CRC",			0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr rx_dma[] = {
    { "Type",			0,			MID_DMA_TYPE,
	"Word\0(Byte)\0(HWord)\0JK\0004W\0008W\00016W\0002W\000???\000???\000???\000???\0004WM\0008WM\00016WM\0002WM" },
    { "End",			-1,			MID_DMA_END,		NULL },
    { "VCI",			MID_DMA_VCI_SHIFT,	MID_DMA_VCI,		NULL },
    { "Count",			MID_DMA_COUNT_SHIFT,	MID_DMA_COUNT,		NULL },
    { "",			0,			0,			NULL },
    { "Host address",		0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};

struct descr tx_dma[] = {
    { "Type",			0,			MID_DMA_TYPE,
	"Word\0Byte\0HWord\0JK\0004W\0008W\00016W\0002W\000???\000???\000???\000???\0004WM\0008WM\00016WM\0002WM" },
    { "End",			-1,			MID_DMA_END,		NULL },
    { "Chan",			MID_DMA_CHAN_SHIFT,	MID_DMA_CHAN,		NULL },
    { "Count",			MID_DMA_COUNT_SHIFT,	MID_DMA_COUNT,		NULL },
    { "",			0,			0,			NULL },
    { "Host address",		0,			0xffffffff,		NULL },
    { NULL,			0,			0,			NULL }
};


static void process(unsigned long *base,struct descr *list)
{
    int ind,fetch;
    unsigned long value,eff;
    const char *here;

    if (!list->mask) {
	ind = -1;
	fetch = 0;
    }
    else {
	ind = 0;
	fetch = 1;
    }
    value = 0; /* for GCC ... */
    while (list->name) {
	if (!list->mask) {
	    ind++;
	    fetch = 1;
	    if (*list->name) printf("%s[%d]\n",list->name,ind);
	}
	else {
	    printf("  %-30s",list->name);
	    if (fetch) {value = base[ind];
/*printf("  [%d] = 0x%lx\n",ind,value);*/
}
	    fetch = 0;
	    if (list->shift == -1) eff = !!(value & list->mask);
	    else eff = (value & list->mask) >> list->shift;
	    if (!list->table) printf("0x%08lx %10ld\n",eff,eff);
	    else {
		for (here = list->table; eff; eff--)
		    here = strchr(here,0)+1;
		printf("%s\n",here);
	    }

	}
	list++;
    }
}


static void dump_registers(unsigned long *base)
{
    int i;

    process(base,midway_regs);
    for (i = 0; i < NR_CHAN; i++) {
	printf("TX %d\n",i);
	process(base+0x10+4*i,tx_place);
	process(base+0x11+4*i,tx_rdptr);
	process(base+0x12+4*i,tx_descrstart);
	
    }
}


static void dump_vcis(unsigned long *base)
{
    int i;

    printf("VCI %p\n",base);
    for (i = 0; i < NR_VCI; i++)
	if ((base[i*4] >> MID_VCI_MODE_SHIFT) != MID_MODE_TRASH) {
	    printf("VCI %d\n",i);
	    process(base+i*4,vci);
	}
}


static void dump_rx_dma(unsigned long *base)
{
    int i;

    printf("RX DMA %p\n",base);
    for (i = 0; i < NR_DMA_RX; i++) {
	printf("RX DMA %d\n",i);
	process(base+i*2,rx_dma);
    }
}


static void dump_tx_dma(unsigned long *base)
{
    int i;

    printf("TX DMA %p\n",base);
    for (i = 0; i < NR_DMA_TX; i++) {
	printf("TX DMA %d\n",i);
	process(base+i*2,tx_dma);
    }
}


static void dump_service(unsigned long *base)
{
    printf("SERV %p\n",base);
}


void main(int argc,char **argv)
{
    unsigned char *base;
    int fd;
int i;
    if ((fd = open(MEM_DEV,O_RDWR,0)) < 0) {
	perror("open");
	return;
    }
    base = mmap(0L,MAP_MAX_SIZE,PROT_READ | PROT_WRITE,MAP_FILE | MAP_SHARED,
      fd,(off_t) BASE_ADDR);
    if (base == (unsigned char *) -1) {
	perror("mmap " MEM_DEV);
	return;
    }
    printf("%x\n",MID_FREE_BASE);
    printf("0x%x\n",(int) ((unsigned long *) (base+MID_FREE_BASE+(100/*+128*/)*
      1024))-(int) base);
    for (i = 0; i < 20; i++)
	printf("%2d 0x%08lX\n",i,((unsigned long *) (base+MID_FREE_BASE+
	    (100/*+128*/)*1024))[i+atoi(argv[1])]);
    printf("0x%x\n",(int) ((unsigned long *) (base+MID_FREE_BASE+(100+128)*
      1024))-(int) base);
    for (i = 0; i < 20; i++)
	printf("%2d 0x%08lX\n",i,((unsigned long *) (base+MID_FREE_BASE+
	  (100+128)*1024))[i]);
    dump_registers((unsigned long *) (base+REG_BASE));
    dump_vcis((unsigned long *) (base+RAM_BASE));
    dump_rx_dma((unsigned long *) (base+MID_DMA_RX_BASE));
    dump_tx_dma((unsigned long *) (base+MID_DMA_TX_BASE));
    dump_service((unsigned long *) (base+MID_SERVICE_BASE));
}
