/* dump.c: PnP BIOS system dev node printer */
/*
 * $Header: /root/pnp/module/RCS/dump.c,v 1.1 1996/05/28 20:37:55 root Exp $
 *
 * $Log: dump.c,v $
 * Revision 1.1  1996/05/28  20:37:55  root
 * Read-only index version works
 *
 *
 */

/*
 * (c) Copyright 1996  D.W.Howells <dwh@nexor.co.uk>,
 */

#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <asm/bitops.h>
#include "pnp_if.h"

/* PnP device config details */
extern u_int pnp_sysdev_num;	/* number of system devices */
extern u_char *pnp_sysdev_list;	/* list of system device nodes */

/*****************************************************************************/
static const char *mem32rom[] = { "", "ROM " };
static const char *mem32shad[] = { "", "shadow " };
static const char *mem32stat[] = { "8b ", "16b ", "8/16/32 ", "32b " };
static const char *mem32supp[] = { "hi-adr ", "range-len " };
static const char *mem32cach[] = { "", "cache " };
static const char *mem32rw[] = { "ro ", "rw " };

/* dump out a resource list */
static int pnp_dump_res_list(const u_char **pnode, u_short *psize)
{
    u_char res, cont;
    u_short len, tmp, size = *psize;
    const u_char *node = *pnode;

    /* read the resource list */
    cont = 1;
    while (cont) {
	if (size<=0)
	    return -EIO;

	res = *node;
	node++;
	size--;

	/* is it large or small format? */
	if (res&0x80) {
	    /*---------------------------------------------------------------*/
	    /* large - get size */
	    if (size<2)
		return -EIO;
	    len = *(u_short*)node;
	    node += 2;
	    size -= 2;

	    if (size<len)
		return -EIO;

	    /* deal with each command */
	    switch ((enum PNP_LARGE_RES_TAG)(res&0x7F)) {
	     case PNPLTAG_ANSI_ID:
		printk("    id: '%.*s'\n",len,node);
		break;

	     case PNPLTAG_MEM32:
		printk("    mem32: %s%s%s%s%s%s base:%08x-%08x al:%08x"
		       " len:%08x\n",
		       mem32rom[(*node>>6)&0x01],
		       mem32shad[(*node>>5)&0x01],
		       mem32stat[(*node>>3)&0x03],
		       mem32supp[(*node>>2)&0x01],
		       mem32cach[(*node>>1)&0x01],
		       mem32rw[*node&0x01],
		       *(u_int*)(node+1),
		       *(u_int*)(node+5),
		       *(u_int*)(node+9),
		       *(u_int*)(node+13)
		       );
		break;

	     case PNPLTAG_MEM32_FIXED:
		printk("    mem32fix: %s%s%s%s%s%s base:%08x len:%08x\n",
		       mem32rom[(*node>>6)&0x01],
		       mem32shad[(*node>>5)&0x01],
		       mem32stat[(*node>>3)&0x03],
		       mem32supp[(*node>>2)&0x01],
		       mem32cach[(*node>>1)&0x01],
		       mem32rw[*node&0x01],
		       *(u_int*)(node+1),
		       *(u_int*)(node+5)
		       );
		break;

	     case PNPLTAG_MEM:
	     case PNPLTAG_UNICODE_ID:
	     case PNPLTAG_VENDOR_DEF:
	     default:
		printk("    unknown large: %02x L:%d\n",res&0x7F,len);
		break;
	    }
	    node += len;
	    size -= len;
	}
	else {
	    /*---------------------------------------------------------------*/
	    /* small - read res data */
	    len = res & 0x7;

	    if (size<len)
		return -EIO;

	    /* switch on command */
	    switch ((enum PNP_SMALL_RES_TAG)((res&0x78)>>3)) {
	     case PNPSTAG_PNPVERSION:
		printk("    version: %02x  vendor:%02x\n",node[0],node[1]);
		break;
		
	     case PNPSTAG_LOGICAL_DEV_ID:
		tmp = node[0]<<8 | node[1];
		printk("    LOG DEV ID: %c%c%c%02x%02x boot:%c"
		       " cmds:%02x%02x\n",
		       ((tmp>>10)&0x1F)+'A'-1,
		       ((tmp>>5)&0x1F)+'A'-1,
		       ((tmp>>0)&0x1F)+'A'-1,
		       node[2],
		       node[3],
		       node[4]&0x01 ? 'y' : 'n',
		       node[5],
		       node[4]&0xFE
		       );
		break;

	     case PNPSTAG_COMPATIBLE_DEV_ID:
		tmp = node[0]<<8 | node[1];
		printk("    com dev ID: %c%c%c%02x%02x\n",
		       ((tmp>>10)&0x1F)+'A'-1,
		       ((tmp>>5)&0x1F)+'A'-1,
		       ((tmp>>0)&0x1F)+'A'-1,
		       node[2],
		       node[3]
		       );
		break;

	     case PNPSTAG_IRQ:
		tmp = (len==3) ? node[2] : 0x01;
		printk("    irq: "
		       "i:%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c E:%c%c%c%c\n",
		       test_bit(0x0,node) ? '0' : '-',
		       test_bit(0x1,node) ? '1' : '-',
		       test_bit(0x2,node) ? '2' : '-',
		       test_bit(0x3,node) ? '3' : '-',
		       test_bit(0x4,node) ? '4' : '-',
		       test_bit(0x5,node) ? '5' : '-',
		       test_bit(0x6,node) ? '6' : '-',
		       test_bit(0x7,node) ? '7' : '-',
		       test_bit(0x8,node) ? '8' : '-',
		       test_bit(0x9,node) ? '9' : '-',
		       test_bit(0xA,node) ? 'A' : '-',
		       test_bit(0xB,node) ? 'B' : '-',
		       test_bit(0xC,node) ? 'C' : '-',
		       test_bit(0xD,node) ? 'D' : '-',
		       test_bit(0xE,node) ? 'E' : '-',
		       test_bit(0xF,node) ? 'F' : '-',
		       test_bit(0x0,&tmp) ? 'l' : '-',
		       test_bit(0x1,&tmp) ? 'L' : '-',
		       test_bit(0x2,&tmp) ? 'e' : '-',
		       test_bit(0x3,&tmp) ? 'E' : '-'
		       );
		break;

	     case PNPSTAG_DMA:
		printk("    dma: d:%c%c%c%c%c%c%c%c sp:%d mo:%c%c bus:%c "
		       "tran:%d\n",
		       test_bit(0x0,node) ? '0' : '-',
		       test_bit(0x1,node) ? '1' : '-',
		       test_bit(0x2,node) ? '2' : '-',
		       test_bit(0x3,node) ? '3' : '-',
		       test_bit(0x4,node) ? '4' : '-',
		       test_bit(0x5,node) ? '5' : '-',
		       test_bit(0x6,node) ? '6' : '-',
		       test_bit(0x7,node) ? '7' : '-',
		       (node[1]&0x60)>>5,
		       test_bit(0x4,node+1) ? 'w' : '-',
		       test_bit(0x3,node+1) ? 'b' : '-',
		       test_bit(0x2,node+1) ? 'y' : 'n',
		       node[1]&0x03
		       );
		break;

	     case PNPSTAG_START_DEP:
		if (len==1)
		    printk("    START DEP: p:%d\n",node[0]);
		else
		    printk("    START DEP\n");
		break;

	     case PNPSTAG_END_DEP:
		printk("    END DEP\n");
		break;

	     case PNPSTAG_IO:
		printk("    io: 16:%c %04x-%04x a:%02x N:%02x\n",
		       test_bit(0,node) ? 'y' : 'n',
		       *(u_short*)(node+1),
		       *(u_short*)(node+3),
		       node[5],
		       node[6]
		       );
		break;

	     case PNPSTAG_IO_FIXED:
		printk("    iofix: %04x N:%02x\n",
		       *(u_short*)(node),
		       node[2]
		       );
		break;

	     case PNPSTAG_VENDOR_DEF:
		printk("    vendor def: N:%d\n",len);
		break;

	     case PNPSTAG_END:
		printk("    END\n");
		cont = 0;
		break;

	     default:
		printk("    unknown small: %02x N:%d\n",(res&0x78)>>3,len);
		break;
	    }
	    node += len;
	    size -= len;
	}
    } /* end while cont */

    *psize = size;
    *pnode = node;

    return 0;

} /* end pnp_dump_res_list */

/*****************************************************************************/
/* dump out the sys dev node at the pointer */
int pnp_dump_sysdev(const u_char *node)
{
    u_short id, size;

    id = *(u_short*)(node+3);
    size = *(u_short*)(node+0);
    printk("BIOS: dev[%d] sz=%04x id=%c%c%c%02x%02x type=%02x,%02x,%02x"
	   " attr=%04x\n",
	   node[2],
	   size,
	   ((id>>10)&0x1F)+'A'-1, ((id>>5)&0x1F)+'A'-1, ((id>>0)&0x1F)+'A'-1,
	   node[5], node[6],
	   node[7], node[8], node[9],
	   *(u_short*)(node+10)
	   );

    /* move onto the resource description sections */
    node += 12;
    size -= 12;
    printk("  Current config:\n");
    pnp_dump_res_list(&node,&size);
    printk("  Possible config:\n");
    pnp_dump_res_list(&node,&size);
    printk("  Compatible devices:\n");
    pnp_dump_res_list(&node,&size);

    return 0;

} /* end pnp_dump_sysdev() */

/*****************************************************************************/
/* dump all the system device nodes */
int pnp_dump_all_sysdev(void)
{
    int rt, loop;
    u_char *ptr;

    /* dump all devices BIOS knows about */
    ptr = pnp_sysdev_list;
    for (loop=0; loop<pnp_sysdev_num; loop++) {
	rt = pnp_dump_sysdev(ptr);
	if (rt<0)
	    return rt;
	ptr += *(u_short*)ptr;
    }

    return 0;
} /* end pnp_all_dump_sysdev() */
