/* Plug'n'Play Support */
#include <stdio.h>
#include <io.h>
#include <conio.h>
#include <string.h>

struct PNP_Expansion_Header
{
    unsigned char sign[4];
    unsigned char revision;
    unsigned char length;
    unsigned short next_header;
    unsigned char reserved;
    unsigned char checksum;
    unsigned int  device_ident;
    unsigned short manufacturer;
    unsigned short product;
    unsigned char  device_type[3];
    unsigned char device_indicator;
    unsigned short vector_boot_connection;
    unsigned short vector_disconnect;
    unsigned short bootstrap_entry;
    unsigned short reserved2;
    unsigned short vector_static_resource;
};

struct PNP_Option_ROM
{
    unsigned short  magic;               /* 0xAA55 */
    unsigned char   len;                 /* length 512byte blocks */
    unsigned long   entry;               /* entry point */
    unsigned char   reserved[19];
    unsigned short  exp_offs;            /* expansion header offs */
};

struct PNP_BIOS
{
    unsigned char	magic[4];
    unsigned char	version;		/* BCD number */
    unsigned char	length;		/* length in BYTES */
    unsigned short	control_field;
    unsigned char	crc;
    unsigned long	event_notification_flag_addr;
    unsigned short	rmode_entry_ip;
    unsigned short	rmode_entry_cs;
    unsigned short	pmode_entry_ip;
    unsigned long	pmode_entry_cs;
    unsigned long	OEM_device_id;
    unsigned short	rmode_data_seg;
    unsigned long	pmode_data_seg;
};

unsigned long pmseg, pmoffset;
unsigned int getnumdev(unsigned short bios_entry_point);

/* Returns the address of the PnP BIOS if there is one. */
unsigned long search_pnp_bios(void)
{
    struct PNP_BIOS *p_bios;
    unsigned long addr;
    printf("\npnp.c: Searching for Plug and Play BIOS...");

    for (addr = 0xf0000000; addr < 0xf000ffff; addr += 16)
    {
	p_bios = (struct PNP_BIOS*) addr;

	if (!strncmp(p_bios->magic , "$PnP", 4))
	{
	    printf("\npnp.c: PNP Bios found at 0x%000000008X",addr);
	    printf("\npnp.c: PNP Version %d.%d",(p_bios->version >> 4) & 0xF, p_bios->version & 0xF);
	    printf("\npnp.c: PNP BIOS PM-Segment: 0x%08X",p_bios->pmode_entry_cs);
	    printf("\npnp.c: PNP BIOS PM-Offset : 0x%08X",p_bios->pmode_entry_ip);
	    printf("\npnp.c: PNP BIOS PM-Dataseg: 0x%08X",p_bios->pmode_data_seg);
	    printf("\npnp.c: PNP BIOS length    : 0x%08X",p_bios->length);
	    pmseg = p_bios->pmode_entry_cs;
	    pmoffset = p_bios->pmode_entry_ip;
	    return addr;
	}
    }
    printf("\nDONE - FAIL(Couldn't find an ISA PnP BIOS)\n\n");
    return addr;
}

void pnp_init(void)
{
    unsigned long addr;
    struct PNP_Option_ROM *p_opt;
    struct PNP_Expansion_Header *p_exp;
    struct PNP_BIOS *p_bios;
    unsigned long a,b;

    /* Displays where your expansion ROM's are */
    for (addr = 0xC0000000 ; addr < 0xF0000000 ; addr += 2048 )
    {
	p_opt = (struct PNP_Option_ROM*) addr;

	if (p_opt->magic == 0xAA55)
	{
	    printf("\npnp.c: PNP Option ROM Header at 0x%08x",addr);
	    printf("\npnp.c: Offset to exp. header is 0x%08x",p_opt->exp_offs);
	    printf("\npnp.c: Address of exp. header is 0x%08x",addr + p_opt->exp_offs);
	}
    }

    printf ("\n");

    /* Search for the ISAPnP BIOS then pause... */
    addr = search_pnp_bios();
    getch();

    /* No bios, else, get the number of ISAPnP devices on the system */
    if (addr == 0xF000FFFF)
    {
	printf("pnp.c: No PnP BIOS found.");
	return;
    }
    else
    {
	getnumdev(pmseg);
    }

    printf("\n\nDone");
}

/* This is the source of all evils... */
unsigned int getnumdev(unsigned short bios_entry_point)
{
    /* Declares OK */
    typedef unsigned long (*entrypoint)(int Function, unsigned char *NumNodes, unsigned int *NodeSize, unsigned int BiosSelector);
    entrypoint epf = (entrypoint)bios_entry_point;

    /* Temporary variables... */
    unsigned char numnodes;
    unsigned int nodesize;
    unsigned long temp;

    /* This was just to make sure that the above worked and wasn't freezing
    *  BorlandC. It works */
    printf("\n\nBIOS EP = 0x%00008X", bios_entry_point);
    getch();

    /* This is the problem area. It has to be right here because at this
    *  exact point, it dies after spewing out character after character in memory */
    printf("\n\npnp.c: Getting the number of ISAPnP Devices...");
    temp = epf(0x0000, &numnodes, &nodesize, 0);
    getch();

    /* Display the number of nodes that we found, then return... */
    printf("\nNumnodes = %d, Size of largest node = %d", numnodes, nodesize);
    return temp;
}

/* Nothing fancy */
void main()
{
    clrscr();
    pnp_init();
    getch();
}