/* $NetBSD: pnpbios.c,v 1.12 2000/03/01 20:23:55 groo Exp $ */ /* * Copyright (c) 2000 Christian E. Hopps. All rights reserved. * Copyright (c) 1999 * Matthias Drochner. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * PnP BIOS documentation is available at the following locations. * * http://www.microsoft.com/hwdev/download/respec/pnpbios.zip * http://www.microsoft.com/hwdev/download/respec/biosclar.zip * http://www.microsoft.com/hwdev/download/respec/devids.txt * * PNPBIOSEVENTS is unfinished. After coding what I did I discovered * I had no platforms to test on so someone else will need to finish * it. I didn't want to toss the code though */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opt_pnpbiosverbose.h" #include "isadma.h" #include "locators.h" #ifdef PNPBIOSDEBUG #define DPRINTF(x) printf x #else #define DPRINTF(x) #endif struct pnpbios_softc { struct device sc_dev; isa_chipset_tag_t sc_ic; struct proc *sc_evthread; u_int8_t *sc_evaddr; int sc_version; int sc_control; int sc_threadrun; }; #define PNPGET4(p) ((p)[0] + ((p)[1] << 8) + \ ((p)[2] << 16) + ((p)[3] << 24)) /* bios calls */ int pnpbios_getapmtable __P((u_char *, size_t *)); int pnpbios_getdockinfo __P((struct pnpdockinfo *)); int pnpbios_getnode __P((int, int *, u_int8_t *, size_t)); int pnpbios_getnumnodes __P((int *, size_t *)); int pnpbios_getevent __P((u_int16_t *event)); int pnpbios_sendmessage __P((int)); int pnpbios_setnode __P((int, int, const u_int8_t *, size_t)); void pnpbios_create_event_thread __P((void *)); void pnpbios_event_thread __P((void *)); /* configuration stuff */ caddr_t pnpbios_mapit __P((u_long, u_long, int)); caddr_t pnpbios_find __P((void)); int pnpbios_match __P((struct device *, struct cfdata *, void *)); void pnpbios_attach __P((struct device *, struct device *, void *)); void pnpbios_printres __P((struct pnpresources *)); int pnpbios_print __P((void *, const char *)); void pnpbios_id_to_string __P((u_int32_t, char *)); void pnpbios_attachnode __P((struct pnpbios_softc *, int, const u_int8_t *, size_t)); int pnp_scan __P((const u_int8_t **, size_t,struct pnpresources *, int)); int pnpbios_submatch __P((struct device *, struct cfdata *, void *)); extern int pnpbioscall __P((int)); /* scanning functions */ int pnp_compatid __P((struct pnpresources *, const void *, size_t)); int pnp_newirq __P((struct pnpresources *, const void *, size_t)); int pnp_newdma __P((struct pnpresources *, const void *, size_t)); int pnp_newioport __P((struct pnpresources *, const void *, size_t)); int pnp_newfixedioport __P((struct pnpresources *, const void *, size_t)); #ifdef PNPBIOSDEBUG int pnp_debugdump __P((struct pnpresources *, const void *, size_t)); #endif /* * small ressource types (beginning with 1) */ static struct{ int (*handler) __P((struct pnpresources *, const void *, size_t)); int minlen, maxlen; } smallrescs[] = { {0, 2, 2}, /* PnP version number */ {0, 5, 6}, /* logical device id */ {pnp_compatid, 4, 4}, /* compatible device id */ {pnp_newirq, 2, 3}, /* irq descriptor */ {pnp_newdma, 2, 2}, /* dma descriptor */ {0, 0, 1}, /* start dep */ {0, 0, 0}, /* end dep */ {pnp_newioport, 7, 7}, /* io descriptor */ {pnp_newfixedioport, 3, 3}, /* fixed io descriptor */ {0, -1, -1}, /* reserved */ {0, -1, -1}, {0, -1, -1}, {0, -1, -1}, {0, 1, 7}, /* vendor defined */ {0, 1, 1} /* end */ }; struct cfattach pnpbios_ca = { sizeof(struct pnpbios_softc), pnpbios_match, pnpbios_attach }; /* * Private stack and return value buffer. Spec (1.0a, ch. 4.3) says that * 1024 bytes must be available to the BIOS function. */ #define PNPBIOS_BUFSIZE 4096 int pnpbios_enabled = 1; size_t pnpbios_entry; caddr_t pnpbios_scratchbuf; /* * There can be only one of these, and the i386 ISA code needs to * reference this. */ struct pnpbios_softc *pnpbios_softc; #define PNPBIOS_SIGNATURE ('$' | ('P' << 8) | ('n' << 16) | ('P' << 24)) caddr_t pnpbios_find() { caddr_t p, c; u_int8_t cksum; size_t structlen; for (p = (caddr_t)ISA_HOLE_VADDR(0xf0000); p <= (caddr_t)ISA_HOLE_VADDR(0xffff0); p += 16) { if (*(int *)p != PNPBIOS_SIGNATURE) continue; structlen = *(u_int8_t *)(p + 5); if ((structlen < 0x21) || ((p + structlen - 1) > (caddr_t)ISA_HOLE_VADDR(0xfffff))) continue; cksum = 0; for (c = p; c < p + structlen; c++) cksum += *(u_int8_t *)c; if (cksum != 0) continue; if (*(char *)(p + 4) != 0x10) { printf("unknown version %x\n", *(char *)(p + 4)); continue; } return (p); } return (0); } int pnpbios_probe() { return (pnpbios_find() != 0); } int pnpbios_match(parent, match, aux) struct device *parent; struct cfdata *match; void *aux; { struct pnpbios_attach_args *paa = aux; /* These are not the droids you're looking for. */ if (strcmp(paa->paa_busname, "pnpbios") != 0) return (0); /* There can be only one! */ if (pnpbios_softc != NULL) return (0); return (pnpbios_enabled); } caddr_t pnpbios_mapit(addr, len, prot) u_long addr, len; int prot; { u_long startpa, pa, endpa; vaddr_t startva, va; pa = startpa = i386_trunc_page(addr); endpa = i386_round_page(addr + len); va = startva = uvm_km_valloc(kernel_map, endpa - startpa); if (!startva) return (0); for (; pa < endpa; pa += NBPG, va += NBPG) pmap_kenter_pa(va, pa, prot); return ((caddr_t)(startva + (addr - startpa))); } void pnpbios_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct pnpbios_softc *sc = (struct pnpbios_softc *)self; struct pnpbios_attach_args *paa = aux; struct pnpdevnode *dn; caddr_t p; unsigned int codepbase, datapbase, evaddrp; caddr_t codeva, datava; extern char pnpbiostramp[], epnpbiostramp[]; int res, num, i, size, idx; #ifdef PNPBIOSVERBOSE struct pnpdockinfo di; #endif #ifdef PNPBIOSEVENTS int evtype; #endif u_int8_t *buf; pnpbios_softc = sc; sc->sc_ic = paa->paa_ic; #if NISADMA > 0 isa_dmainit(sc->sc_ic, I386_BUS_SPACE_IO, &isa_bus_dma_tag, self); #endif p = pnpbios_find(); if (!p) panic("pnpbios_attach: disappeared"); sc->sc_version = *(u_int8_t *)(p + 0x04); sc->sc_control = *(u_int8_t *)(p + 0x06); evaddrp = *(u_int32_t *)(p + 0x09); codepbase = *(u_int32_t *)(p + 0x13); datapbase = *(u_int32_t *)(p + 0x1d); pnpbios_entry = *(u_int16_t *)(p + 0x11); #ifdef PNPBIOSEVENTS /* if we have an event mechnism queue a thread to deal with them */ evtype = (sc->sc_control & PNP_IC_CONTORL_EVENT_MASK); if (evtype == PNP_IC_CONTROL_EVENT_POLL) { sc->sc_evaddr = pnpbios_mapit(evaddrp, NBPG, VM_PROT_READ | VM_PROT_WRITE); if (!sc->sc_evaddr) printf("pnpbios: couldn't map event flag 0x%08x\n", evaddrp); DPRINTF(("pnpbios: event flag vaddr 0x%08x\n", (int)sc->sc_evaddr)); } #endif #ifdef PNPBIOSVERBOSE printf(": code %x, data %x, entry %x, control %x eventp %x\n%s", codepbase, datapbase, pnpbios_entry, sc->sc_control, (int)evaddrp, self->dv_xname); #endif codeva = pnpbios_mapit(codepbase, 0x10000, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); datava = pnpbios_mapit(datapbase, 0x10000, VM_PROT_READ | VM_PROT_WRITE); if (codeva == 0 || datava == 0) { printf("no vm for mapping\n"); return; } pnpbios_scratchbuf = malloc(PNPBIOS_BUFSIZE, M_DEVBUF, M_NOWAIT); setsegment(&gdt[GPNPBIOSCODE_SEL].sd, codeva, 0xffff, SDT_MEMERA, SEL_KPL, 0, 0); setsegment(&gdt[GPNPBIOSDATA_SEL].sd, datava, 0xffff, SDT_MEMRWA, SEL_KPL, 0, 0); setsegment(&gdt[GPNPBIOSSCRATCH_SEL].sd, pnpbios_scratchbuf, PNPBIOS_BUFSIZE - 1, SDT_MEMRWA, SEL_KPL, 0, 0); setsegment(&gdt[GPNPBIOSTRAMP_SEL].sd, pnpbiostramp, epnpbiostramp - pnpbiostramp - 1, SDT_MEMERA, SEL_KPL, 1, 0); res = pnpbios_getnumnodes(&num, &size); if (res) { printf("pnpbios_getnumnodes: error %d\n", res); return; } printf(": nodes %d, max len %d\n", num, size); buf = malloc(size, M_DEVBUF, M_NOWAIT); idx = 0; for (i = 0; i < num && idx != 0xff; i++) { int node = idx; res = pnpbios_getnode(1, &idx, buf, size); if (res) { printf("pnpbios_getnode: error %d\n", res); continue; } dn = (struct pnpdevnode *)buf; if (dn->dn_handle != node) printf("node idx: called %d, got %d\n", node, dn->dn_handle); pnpbios_attachnode(sc, node, buf, dn->dn_size); } if (i != num) printf("got only %d nodes\n", i); if (idx != 0xff) printf("last idx=%x\n", idx); free(buf, M_DEVBUF); #ifdef PNPBIOSVERBOSE res = pnpbios_getdockinfo(&di); if (res == PNP_RC_SYSTEM_NOT_DOCKED) printf("%s: not docked\n", sc->sc_dev.dv_xname); else if (res) DPRINTF(("pnpbios: dockinfo fails 0x%02x\n", res)); else { char idstr[8]; pnpbios_id_to_string(di.di_id, idstr); printf("%s: dock id %s serial number %d capabilities 0x%04x\n", sc->sc_dev.dv_xname, idstr, di.di_serial, di.di_cap); } #endif #ifdef PNPBIOSEVENTS /* if we have an event mechnism queue a thread to deal with them */ /* XXX need to update with irq if we do that */ if (evtype != PNP_IC_CONTROL_EVENT_NONE) { if (evtype != PNP_IC_CONTROL_EVENT_POLL || sc->sc_evaddr) { sc->sc_threadrun = 1; config_pending_incr(); kthread_create(pnpbios_create_event_thread, sc); } } #endif } int pnpbios_getnumnodes(nump, sizep) int *nump; size_t *sizep; { int res; short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 2; /* buffer offset for node size */ *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for numnodes */ *--help = PNP_FC_GET_NUM_NODES; res = pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf); if (res) return (res); *nump = *(short *)(pnpbios_scratchbuf + 0); *sizep = *(short *)(pnpbios_scratchbuf + 2); return (0); } int pnpbios_getnode(flags, idxp, buf, len) int flags; int *idxp; u_int8_t *buf; size_t len; { int res; short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = flags; *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 2; /* buffer offset for node data */ *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for index in/out */ *--help = PNP_FC_GET_DEVICE_NODE; *(short *)(pnpbios_scratchbuf + 0) = *idxp; res = pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf); if (res) return (res); *idxp = *(short *)(pnpbios_scratchbuf + 0); bcopy(pnpbios_scratchbuf + 2, buf, len); return (0); } int pnpbios_setnode(flags, idx, buf, len) int flags, idx; const u_int8_t *buf; size_t len; { short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = flags; *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for node data */ *--help = idx; *--help = PNP_FC_SET_DEVICE_NODE; memcpy(pnpbios_scratchbuf, buf, len); return (pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf)); } int pnpbios_getevent(event) u_int16_t *event; { int res; short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for message data */ *--help = PNP_FC_GET_EVENT; res = pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf); *event = pnpbios_scratchbuf[0] + (pnpbios_scratchbuf[1] << 8); return (res); } int pnpbios_sendmessage(msg) int msg; { short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = msg; *--help = PNP_FC_SEND_MESSAGE; return (pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf)); } int pnpbios_getdockinfo(di) struct pnpdockinfo *di; { int res; short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for dock info */ *--help = PNP_FC_GET_DOCK_INFO; res = pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf); memcpy(di, pnpbios_scratchbuf, sizeof(*di)); return (res); } /* XXX we don't support more than PNPBIOS_BUFSIZE - (stacklen + 2) */ int pnpbios_getapmtable(tab, len) u_int8_t *tab; size_t *len; { short *help = (short *)(pnpbios_scratchbuf + PNPBIOS_BUFSIZE); size_t origlen, stacklen; int res; *--help = GSEL(GPNPBIOSDATA_SEL, SEL_KPL); *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 2; /* buffer offset for table */ *--help = GSEL(GPNPBIOSSCRATCH_SEL, SEL_KPL); *--help = 0; /* buffer offset for length */ *--help = PNP_FC_GET_APM_TABLE; origlen = *len; stacklen = (caddr_t)help - pnpbios_scratchbuf; if (origlen > PNPBIOS_BUFSIZE - stacklen - 2) origlen = PNPBIOS_BUFSIZE - stacklen - 2; *(u_int16_t *)(pnpbios_scratchbuf) = origlen; res = pnpbioscall(((caddr_t)help) - pnpbios_scratchbuf); *len = *(u_int16_t *)pnpbios_scratchbuf; if (res) return (res); if (origlen && *len > origlen) { printf("pnpbios: returned apm table exceed requested size\n"); return (PNP_RC_BUFFER_TOO_SMALL); } memcpy(tab, pnpbios_scratchbuf + 2, *len); return (0); } void pnpbios_id_to_string(pnpid, s) u_int32_t pnpid; char *s; { static char hex[] = "0123456789ABCDEF"; u_int8_t *id; id = (u_int8_t *)&pnpid; *s++ = 'A' + (id[0] >> 2) - 1; *s++ = 'A' + ((id[0] & 3) << 3) + (id[1] >> 5) - 1; *s++ = 'A' + (id[1] & 0x1f) - 1; *s++ = hex[id[2] >> 4]; *s++ = hex[id[2] & 0x0f]; *s++ = hex[id[3] >> 4]; *s++ = hex[id[3] & 0x0f]; *s = '\0'; } void pnpbios_printres(r) struct pnpresources *r; { struct pnp_mem *mem; struct pnp_io *io; struct pnp_irq *irq; struct pnp_dma *dma; int p = 0; mem = SIMPLEQ_FIRST(&r->mem); if (mem) { printf("mem"); do { printf(" %x", mem->minbase); if (mem->len > 1) printf("-%x", mem->minbase + mem->len - 1); } while ((mem = SIMPLEQ_NEXT(mem, next))); p++; } io = SIMPLEQ_FIRST(&r->io); if (io) { if (p++) printf(", "); printf("io"); do { printf(" %x", io->minbase); if (io->len > 1) printf("-%x", io->minbase + io->len - 1); } while ((io = SIMPLEQ_NEXT(io, next))); } irq = SIMPLEQ_FIRST(&r->irq); if (irq) { if (p++) printf(", "); printf("irq"); do { printf(" %d", ffs(irq->mask) - 1); } while ((irq = SIMPLEQ_NEXT(irq, next))); } dma = SIMPLEQ_FIRST(&r->dma); if (dma) { if (p) printf(", "); printf("dma"); do { printf(" %d", ffs(dma->mask) - 1); } while ((dma = SIMPLEQ_NEXT(dma, next))); } } int pnpbios_print(aux, pnp) void *aux; const char *pnp; { struct pnpbiosdev_attach_args *aa = aux; if (pnp) return (QUIET); printf(" index %d (%s", aa->idx, aa->primid); if (aa->resc->longname) printf(", %s", aa->resc->longname); if (aa->idstr != aa->primid) printf(", attached as %s", aa->idstr); printf(")"); return (0); } void pnpbios_print_devres(dev, aa) struct device *dev; struct pnpbiosdev_attach_args *aa; { printf("%s: ", dev->dv_xname); pnpbios_printres(aa->resc); printf("\n"); } int pnpbios_submatch(parent, match, aux) struct device *parent; struct cfdata *match; void *aux; { struct pnpbiosdev_attach_args *aa = aux; if (match->cf_loc[PNPBIOSCF_INDEX] != PNPBIOSCF_INDEX_DEFAULT && match->cf_loc[PNPBIOSCF_INDEX] != aa->idx) return (0); return ((*match->cf_attach->ca_match)(parent, match, aux)); } void pnpbios_attachnode(sc, idx, buf, len) struct pnpbios_softc *sc; int idx; const u_int8_t *buf; size_t len; { struct pnpdevnode *dn; const u_int8_t *p; char idstr[8]; struct pnpresources r, s; struct pnpbiosdev_attach_args aa; struct pnp_compatid *compatid; int res, i; dn = (struct pnpdevnode *)buf; pnpbios_id_to_string(dn->dn_product, idstr); p = (u_char *)(dn + 1); DPRINTF(("%s: type 0x%02x subtype 0x%02x dpi 0x%02x attr 0x%04x:\n", idstr, dn->dn_type, dn->dn_subtype, dn->dn_dpi, dn->dn_attr)); DPRINTF(("%s: allocated config scan:\n", idstr)); res = pnp_scan(&p, len - 12, &r, 0); if (res < 0) { printf("error in config data\n"); goto dump; } /* * the following is consistency check only for now */ DPRINTF(("\tpossible config scan:\n")); res = pnp_scan(&p, len - (p - buf), &s, 0); if (res < 0) { printf("error in possible configuration\n"); goto dump; } DPRINTF(("\tcompat id scan:\n")); res = pnp_scan(&p, len - (p - buf), &s, 0); if (res < 0) { printf("error in compatible ID\n"); goto dump; } if (p != buf + len) { printf("%s: length mismatch in node %d: used %d of %d Bytes\n", sc->sc_dev.dv_xname, idx, p - buf, len); if (p > buf + len) { /* XXX shouldn't happen - pnp_scan should catch it */ goto dump; } /* Crappy BIOS: Buffer is not fully used. Be generous. */ } if (r.nummem + r.numio + r.numirq + r.numdma == 0) { #ifdef PNPBIOSVERBOSE printf("%s", idstr); if (r.longname) printf(", %s", r.longname); compatid = s.compatids; while (compatid) { printf(", %s", compatid->idstr); compatid = compatid->next; } printf(" at %s index %d disabled\n", sc->sc_dev.dv_xname, idx); #endif return; } aa.pbt = 0; /* XXX placeholder */ aa.idx = idx; aa.resc = &r; aa.ic = sc->sc_ic; aa.primid = idstr; /* first try the specific device ID */ aa.idstr = idstr; if (config_found_sm((struct device *)sc, &aa, pnpbios_print, pnpbios_submatch)) return; /* if no driver was found, try compatible IDs */ compatid = s.compatids; while (compatid) { aa.idstr = compatid->idstr; if (config_found_sm((struct device *)sc, &aa, pnpbios_print, pnpbios_submatch)) return; compatid = compatid->next; } #ifdef PNPBIOSVERBOSE printf("%s", idstr); if (r.longname) printf(", %s", r.longname); compatid = s.compatids; while (compatid) { printf(", %s", compatid->idstr); compatid = compatid->next; } printf(" ("); pnpbios_printres(&r); printf(") at %s index %d ignored\n", sc->sc_dev.dv_xname, idx); #endif return; /* XXX should free ressource lists */ dump: i = 0; #ifdef PNPBIOSDEBUG /* print some useful info */ if (len >= sizeof(*dn)) { printf("%s idx %d size %d type 0x%x:0x%x:0x%x attr 0x%x\n", idstr, dn->dn_handle, dn->dn_size, dn->dn_type, dn->dn_subtype, dn->dn_dpi, dn->dn_attr); i += sizeof(*dn); } #endif for (; i < len; i++) printf(" %02x", buf[i]); printf("\n"); } int pnp_scan(bufp, maxlen, r, in_depends) const u_int8_t **bufp; size_t maxlen; struct pnpresources *r; int in_depends; { const void *start; const u_int8_t *p; struct pnp_mem *mem; int tag, type, len; char *idstr; int i; p = *bufp; bzero(r, sizeof(*r)); SIMPLEQ_INIT(&r->mem); SIMPLEQ_INIT(&r->io); SIMPLEQ_INIT(&r->irq); SIMPLEQ_INIT(&r->dma); for (;;) { if (p >= *bufp + maxlen) { printf("pnp_scanresources: end of buffer\n"); return (-1); } start = p; tag = *p; if (tag & ISAPNP_LARGE_TAG) { len = *(u_int16_t *)(p + 1); p += sizeof(struct pnplargeres) + len; switch (tag) { case ISAPNP_TAG_MEM_RANGE_DESC: { const struct pnpmem16rangeres *res = start; if (len != sizeof(*res) - 3) { printf("pnp_scan: bad mem desc\n"); return (-1); } mem = malloc(sizeof(struct pnp_mem), M_DEVBUF, M_WAITOK); mem->flags = res->r_flags; mem->minbase = res->r_minbase << 8; mem->maxbase = res->r_maxbase << 8; mem->align = res->r_align; if (mem->align == 0) mem->align = 0x10000; mem->len = res->r_len << 8; DPRINTF(("\ttag memrange ")); goto gotmem; } case ISAPNP_TAG_ANSI_IDENT_STRING: { const struct pnpansiidentres *res = start; if (in_depends) printf("ID in dep?\n"); idstr = malloc(len + 1, M_DEVBUF, M_NOWAIT); for (i = 0; i < len; i++) idstr[i] = res->r_id[i]; idstr[len] = '\0'; DPRINTF(("\ttag ansiident %s\n", idstr)); if (idstr[0] == '\0') { /* disabled device */ free(idstr, M_DEVBUF); break; } r->longname = idstr; break; } case ISAPNP_TAG_MEM32_RANGE_DESC: { const struct pnpmem32rangeres *res = start; if (len != sizeof(*res) - 3) { printf("pnp_scan: bad mem32 desc\n"); return (-1); } mem = malloc(sizeof(struct pnp_mem), M_DEVBUF, M_WAITOK); mem->flags = res->r_flags; mem->minbase = res->r_minbase; mem->maxbase = res->r_maxbase; mem->align = res->r_align; mem->len = res->r_len; DPRINTF(("\ttag mem32range ")); goto gotmem; } case ISAPNP_TAG_FIXED_MEM32_RANGE_DESC: { const struct pnpfixedmem32rangeres *res = start; if (len != sizeof(*res) - 3) { printf("pnp_scan: bad mem32 desc\n"); return (-1); } mem = malloc(sizeof(struct pnp_mem), M_DEVBUF, M_WAITOK); mem->flags = res->r_flags; mem->minbase = res->r_base; mem->maxbase = mem->minbase; mem->align = 0; mem->len = res->r_len; DPRINTF(("\ttag fixedmem32range ")); gotmem: if (mem->len == 0) { /* disabled */ DPRINTF(("zeroed\n")); free(mem, M_DEVBUF); break; } SIMPLEQ_INSERT_TAIL(&r->mem, mem, next); r->nummem++; DPRINTF(("flags %02x min %08x max %08x " "align %08x len %08x\n", mem->flags, mem->minbase, mem->maxbase, mem->align, mem->len)); break; } case ISAPNP_TAG_UNICODE_IDENT_STRING: case ISAPNP_TAG_VENDOR_DEFINED: default: #ifdef PNPBIOSDEBUG pnp_debugdump(r, start, len); #endif break; } } else { type = (tag >> 3) & 0x0f; len = tag & 0x07; p += 1 + len; if (type == 0 || len < smallrescs[type - 1].minlen || len > smallrescs[type - 1].maxlen) { printf("pnp_scan: bad small resource\n"); return (-1); } if (type == ISAPNP_TAG_END) { #ifdef PNPBIOSDEBUG const struct pnpendres *res = start; #endif if (in_depends) { /* * this seems to occur and is * an optimization to not require * the end dep in a depend * that ends the section */ p -= 1 + len; } DPRINTF(("\ttag end cksum %02x\n", res->r_cksum)); break; } if (type == ISAPNP_TAG_DEP_START) { #ifdef PNPBIOSDEBUG const struct pnpdepstartres *res = start; #endif struct pnpresources *new, *last; int rv; DPRINTF(("\ttag startdep flags %02x\n", len ? res->r_pri : ISAPNP_DEP_ACCEPTABLE)); if (r->dependant_link) { printf("second dep?\n"); return (-1); } /* XXX not sure about this */ if (in_depends) { *bufp = p; return (1); } last = r; do { new = malloc(sizeof(*new), M_DEVBUF, M_NOWAIT); rv = pnp_scan(&p, maxlen - (p - *bufp), new, 1); if (rv < 0) { printf("error in dependant " "function\n"); free(new, M_DEVBUF); return (-1); } last->dependant_link = new; last = new; } while (rv > 0); continue; } if (type == ISAPNP_TAG_DEP_END) { DPRINTF(("\ttag enddep\n")); if (!in_depends) { printf("tag %d end dep?\n", tag); return (-1); } break; } if (!smallrescs[type - 1].handler) { #ifdef PNPBIOSDEBUG pnp_debugdump(r, start, len); #endif } else if ( (*smallrescs[type - 1].handler)(r, start, len)) return (-1); } } *bufp = p; return (0); } int pnp_newirq(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const struct pnpirqres *res; struct pnp_irq *irq; res = vres; if (res->r_mask == 0) { /* disabled */ DPRINTF(("\ttag irq zeroed\n")); return (0); } irq = malloc(sizeof(struct pnp_irq), M_DEVBUF, M_NOWAIT); irq->mask = res->r_mask; if (len > 2) irq->flags = res->r_info; else irq->flags = 0x01; SIMPLEQ_INSERT_TAIL(&r->irq, irq, next); r->numirq++; DPRINTF(("\ttag irq flags %02x mask %04x\n", irq->flags,irq->mask)); return (0); } int pnp_newdma(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const struct pnpdmares *res; struct pnp_dma *dma; res = vres; if (res->r_mask == 0) { /* disabled */ DPRINTF(("\ttag dma zeroed\n")); return (0); } dma = malloc(sizeof(struct pnp_dma), M_DEVBUF, M_NOWAIT); dma->mask = res->r_mask; dma->flags = res->r_flags; SIMPLEQ_INSERT_TAIL(&r->dma, dma, next); r->numdma++; DPRINTF(("\ttag dma flags %02x mask %02x\n", dma->flags,dma->mask)); return (0); } int pnp_newioport(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const struct pnpportres *res; struct pnp_io *io; res = vres; if (res->r_len == 0) { /* disabled */ DPRINTF(("\ttag io zeroed\n")); return (0); } io = malloc(sizeof(struct pnp_io), M_DEVBUF, M_NOWAIT); io->flags = res->r_flags; io->minbase = res->r_minbase; io->maxbase = res->r_maxbase; io->align = res->r_align; io->len = res->r_len; SIMPLEQ_INSERT_TAIL(&r->io, io, next); r->numio++; DPRINTF(("\ttag io flags %02x min %04x max %04x align " "0x%02x len 0x%02x\n", io->flags, io->minbase, io->maxbase, io->align, io->len)); return (0); } int pnp_newfixedioport(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const struct pnpfixedportres *res; struct pnp_io *io; res = vres; if (res->r_len == 0) { /* disabled */ DPRINTF(("\ttag fixedio zeroed\n")); return (0); } io = malloc(sizeof(struct pnp_io), M_DEVBUF, M_NOWAIT); io->flags = 1; /* 10 bit decoding */ io->minbase = io->maxbase = res->r_base; io->align = 1; io->len = res->r_len; SIMPLEQ_INSERT_TAIL(&r->io, io, next); r->numio++; DPRINTF(("\ttag fixedio flags %02x base %04x align %02x len %02x\n", io->flags, io->minbase, io->align, io->len)); return (0); } int pnp_compatid(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const struct pnpcompatres *res; struct pnp_compatid *id; res = vres; id = malloc(sizeof(*id), M_DEVBUF, M_NOWAIT); pnpbios_id_to_string(res->r_id, id->idstr); id->next = r->compatids; r->compatids = id; DPRINTF(("\ttag compatid %s\n", id->idstr)); return (0); } #ifdef PNPBIOSDEBUG int pnp_debugdump(r, vres, len) struct pnpresources *r; const void *vres; size_t len; { const u_int8_t *res; int type, i; if (res[0] & ISAPNP_LARGE_TAG) { type = res[0] & 0x7f; printf("\tTAG %02x len %04x %s", type, len, len ? "data" : ""); i = 3; } else { type = (res[0] >> 3) & 0x0f; printf("\tTAG %02x len %02x %s", type, len, len ? "data" : ""); i = 1; } for (; i < len; i++) printf(" %02x", res[i]); printf("\n"); return (0); } #endif int pnpbios_io_map(pbt, resc, idx, tagp, hdlp) pnpbios_tag_t pbt; struct pnpresources *resc; int idx; bus_space_tag_t *tagp; bus_space_handle_t *hdlp; { struct pnp_io *io; if (idx >= resc->numio) return (EINVAL); io = SIMPLEQ_FIRST(&resc->io); while (idx--) io = SIMPLEQ_NEXT(io, next); *tagp = I386_BUS_SPACE_IO; return (i386_memio_map(I386_BUS_SPACE_IO, io->minbase, io->len, 0, hdlp)); } void pnpbios_io_unmap(pbt, resc, idx, tag, hdl) pnpbios_tag_t pbt; struct pnpresources *resc; int idx; bus_space_tag_t tag; bus_space_handle_t hdl; { struct pnp_io *io; if (idx >= resc->numio) return; io = SIMPLEQ_FIRST(&resc->io); while (idx--) io = SIMPLEQ_NEXT(io, next); i386_memio_unmap(tag, hdl, io->len); } int pnpbios_getiobase(pbt, resc, idx, tagp, basep) pnpbios_tag_t pbt; struct pnpresources *resc; int idx; bus_space_tag_t *tagp; int *basep; { struct pnp_io *io; if (idx >= resc->numio) return (EINVAL); io = SIMPLEQ_FIRST(&resc->io); while (idx--) io = SIMPLEQ_NEXT(io, next); if (tagp) *tagp = I386_BUS_SPACE_IO; if (basep) *basep = io->minbase; return (0); } void * pnpbios_intr_establish(pbt, resc, idx, level, fcn, arg) pnpbios_tag_t pbt; struct pnpresources *resc; int idx, level; int (*fcn) __P((void *)); void *arg; { struct pnp_irq *irq; int irqnum, type; if (idx >= resc->numirq) return (0); irq = SIMPLEQ_FIRST(&resc->irq); while (idx--) irq = SIMPLEQ_NEXT(irq, next); irqnum = ffs(irq->mask) - 1; type = (irq->flags & 0x0c) ? IST_LEVEL : IST_EDGE; return (isa_intr_establish(0, irqnum, type, level, fcn, arg)); } int pnpbios_getirqnum(pbt, resc, idx, irqp) pnpbios_tag_t pbt; struct pnpresources *resc; int idx; int *irqp; { struct pnp_irq *irq; if (idx >= resc->numirq) return (EINVAL); irq = SIMPLEQ_FIRST(&resc->irq); while (idx--) irq = SIMPLEQ_NEXT(irq, next); *irqp = ffs(irq->mask) - 1; return (0); } int pnpbios_getdmachan(pbt, resc, idx, chanp) pnpbios_tag_t pbt; struct pnpresources *resc; int idx; int *chanp; { struct pnp_dma *dma; if (idx >= resc->numdma) return (EINVAL); dma = SIMPLEQ_FIRST(&resc->dma); while (idx--) dma = SIMPLEQ_NEXT(dma, next); *chanp = ffs(dma->mask) - 1; return (0); } #ifdef PNPBIOSEVENTS void pnpbios_create_event_thread(arg) void *arg; { struct pnpbios_softc *sc; sc = arg; if (kthread_create1(pnpbios_event_thread, sc, &sc->sc_evthread, "%s", sc->sc_dev.dv_xname)) panic("pnpbios_create_event_thread"); } void pnpbios_event_thread(arg) void *arg; { struct pnpbios_softc *sc; u_int16_t event; u_int evflag; int rv, poll; sc = arg; if ((sc->sc_control & PNP_IC_CONTORL_EVENT_MASK) != PNP_IC_CONTROL_EVENT_POLL) poll = 0; else { poll = hz; rv = pnpbios_sendmessage(PNP_CM_PNP_OS_ACTIVE); DPRINTF(("pnpbios: os active returns 0x%02x\n", rv)); } config_pending_decr(); goto start; while (sc->sc_threadrun) { /* maybe we have an event */ if (!poll) (void)tsleep(pnpbios_event_thread, PWAIT, "pnpbiosevent", 0); else if (((evflag = *sc->sc_evaddr) & 0x01) == 0) { if (evflag) DPRINTF(("pnpbios: evflags 0x%02x\n", evflag)); (void)tsleep(pnpbios_event_thread, PWAIT, "pnpbiosevent", poll); continue; } else { DPRINTF(("pnpbios: evflags 0x%02x\n", evflag)); } start: if ((rv = pnpbios_getevent(&event))) { DPRINTF(("pnpbios: getevent rc: 0x%02x\n", rv)); #ifdef DIAGNOSTIC if (rv != PNP_RC_EVENTS_NOT_PENDING) printf("pnpbios: getevent failed: %d\n", rv); #endif continue; } switch (event) { case PNP_EID_ABOUT_TO_CHANGE_CONFIG: DPRINTF(("pnpbios: about to change event\n")); break; case PNP_EID_DOCK_CHANGED: DPRINTF(("pnpbios: dock changed event\n")); break; case PNP_EID_SYSTEM_DEVICE_CHANGED: DPRINTF(("pnpbios: system device changed event\n")); break; case PNP_EID_CONFIG_CHANGE_FAILED: DPRINTF(("pnpbios: config changed event\n")); break; case PNP_EID_UNKNOWN_SYSTEM_EVENT: #ifdef DIAGNOSTIC printf("pnpbios: \"unknown system event\"\n"); #endif break; default: #ifdef DIAGNOSTIC #ifdef PNPBIOSVERBOSE if (event & PNP_EID_OEM_DEFINED_BIT) printf("pnpbios: vendor defined event 0x%04x\n", event); else #endif printf("pnpbios: unkown event 0x%04x\n", event); #endif break; } } pnpbios_sendmessage(PNP_CM_PNP_OS_INACTIVE); kthread_exit(0); } #endif /* PNPBIOSEVENTS */