#include <stand.h>
#include <sys/stdarg.h>
#include <bootstrap.h>
#include <isapnp.h>
#include <btxv86.h>
static int biospnp_init(void);
static void biospnp_enumerate(void);
struct pnphandler biospnphandler =
{
"PnP BIOS",
biospnp_enumerate
};
struct pnp_ICstructure
{
uint8_t pnp_signature[4];
uint8_t pnp_version;
uint8_t pnp_length;
uint16_t pnp_BIOScontrol;
uint8_t pnp_checksum;
uint32_t pnp_eventflag;
uint16_t pnp_rmip;
uint16_t pnp_rmcs;
uint16_t pnp_pmip;
uint32_t pnp_pmcs;
uint8_t pnp_OEMdev[4];
uint16_t pnp_rmds;
uint32_t pnp_pmds;
} __packed;
struct pnp_devNode
{
uint16_t dn_size;
uint8_t dn_handle;
uint8_t dn_id[4];
uint8_t dn_type[3];
uint16_t dn_attrib;
uint8_t dn_data[1];
} __packed;
struct pnp_isaConfiguration
{
uint8_t ic_revision;
uint8_t ic_nCSN;
uint16_t ic_rdport;
uint16_t ic_reserved;
} __packed;
static struct pnp_ICstructure *pnp_Icheck = NULL;
static uint16_t pnp_NumNodes;
static uint16_t pnp_NodeSize;
static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn);
static int biospnp_call(int func, const char *fmt, ...);
#define vsegofs(vptr) (((uint32_t)VTOPSEG(vptr) << 16) + VTOPOFF(vptr))
typedef void v86bios_t(uint32_t, uint32_t, uint32_t, uint32_t);
v86bios_t *v86bios = (v86bios_t *)v86int;
#define biospnp_f00(NumNodes, NodeSize) biospnp_call(0x00, "ll", NumNodes, NodeSize)
#define biospnp_f01(Node, devNodeBuffer, Control) biospnp_call(0x01, "llw", Node, devNodeBuffer, Control)
#define biospnp_f40(Configuration) biospnp_call(0x40, "l", Configuration)
#define PNP_SUCCESS 0x00
#define PNP_FUNCTION_NOT_SUPPORTED 0x80
static int
biospnp_init(void)
{
struct pnp_isaConfiguration icfg;
char *sigptr;
int result;
pnp_Icheck = NULL;
for (sigptr = PTOV(0xf0000); sigptr < PTOV(0xfffff); sigptr += 16)
if (!bcmp(sigptr, "$PnP", 4)) {
pnp_Icheck = (struct pnp_ICstructure *)sigptr;
break;
}
if (pnp_Icheck == NULL)
return(1);
result = biospnp_f00(vsegofs(&pnp_NumNodes), vsegofs(&pnp_NodeSize));
if (result != PNP_SUCCESS) {
return(1);
}
result = biospnp_f40(vsegofs(&icfg));
switch (result) {
case PNP_SUCCESS:
if ((icfg.ic_revision == 1) && (icfg.ic_nCSN > 0))
isapnp_readport = icfg.ic_rdport;
break;
case PNP_FUNCTION_NOT_SUPPORTED:
printf("PnP BIOS claims no ISA bus\n");
isapnp_readport = -1;
break;
}
return(0);
}
static void
biospnp_enumerate(void)
{
uint8_t Node;
struct pnp_devNode *devNodeBuffer;
uint8_t buffer[max(pnp_NodeSize, sizeof(*devNodeBuffer))];
int result;
struct pnpinfo *pi;
int count;
if (biospnp_init())
return;
devNodeBuffer = (struct pnp_devNode *)buffer;
Node = 0;
count = 1000;
while((Node != 0xff) && (count-- > 0)) {
result = biospnp_f01(vsegofs(&Node), vsegofs(devNodeBuffer), 0x1);
if (result != PNP_SUCCESS) {
printf("PnP BIOS node %d: error 0x%x\n", Node, result);
} else {
pi = pnp_allocinfo();
pnp_addident(pi, pnp_eisaformat(devNodeBuffer->dn_id));
biospnp_scanresdata(pi, devNodeBuffer);
pnp_addinfo(pi);
}
}
}
static void
biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn)
{
u_int tag, i, rlen, dlen;
uint8_t *p;
char *str;
p = dn->dn_data;
dlen = dn->dn_size - (p - (uint8_t *)dn);
for (i = 0; i < dlen; i+= rlen) {
tag = p[i];
i++;
if (PNP_RES_TYPE(tag) == 0) {
rlen = PNP_SRES_LEN(tag);
switch (PNP_SRES_NUM(tag)) {
case COMP_DEVICE_ID:
pnp_addident(pi, pnp_eisaformat(p + i));
break;
case END_TAG:
return;
}
} else {
rlen = *(uint16_t *)(p + i);
i += sizeof(uint16_t);
switch(PNP_LRES_NUM(tag)) {
case ID_STRING_ANSI:
str = malloc(rlen + 1);
bcopy(p + i, str, rlen);
str[rlen] = 0;
if (pi->pi_desc == NULL) {
pi->pi_desc = str;
} else {
free(str);
}
break;
}
}
}
}
static int
biospnp_call(int func, const char *fmt, ...)
{
va_list ap;
const char *p;
uint8_t *argp;
uint32_t args[4];
uint32_t i;
argp = (uint8_t *)args;
*(uint16_t *)argp = func;
argp += sizeof(uint16_t);
va_start(ap, fmt);
for (p = fmt; *p != 0; p++) {
switch(*p) {
case 'w':
i = va_arg(ap, u_int);
*(uint16_t *)argp = i;
argp += sizeof(uint16_t);
break;
case 'l':
i = va_arg(ap, uint32_t);
*(uint32_t *)argp = i;
argp += sizeof(uint32_t);
break;
}
}
va_end(ap);
*(uint16_t *)argp = pnp_Icheck->pnp_rmds;
argp += sizeof(uint16_t);
v86.ctl = V86_ADDR | V86_CALLF;
v86.addr = ((uint32_t)pnp_Icheck->pnp_rmcs << 16) + pnp_Icheck->pnp_rmip;
v86bios(args[0], args[1], args[2], args[3]);
return(v86.eax & 0xffff);
}