/* * Scan the resource data in the node's data area for compatible device IDs * and descriptions. */ static void biospnp_scanresdata(struct pnpinfo *pi, struct pnp_devNode *dn) { int tag, i, rlen, dlen; u_int8_t *p; char *str; p = dn->dn_data; /* point to resource data */ dlen = dn->dn_size - (p - (u_int8_t *)dn); /* length of resource data */ for (i = 0; i < dlen; i+= rlen) { tag = p[i]; i++; if (PNP_RES_TYPE(tag) == 0) { rlen = PNP_SRES_LEN(tag); /* small resource */ switch (PNP_SRES_NUM(tag)) { case COMP_DEVICE_ID: /* got a compatible device ID */ pnp_addident(pi, pnp_eisaformat(p + i)); break; case END_TAG: return; } } else { /* large resource */ rlen = *(u_int16_t *)(p + i); i += sizeof(u_int16_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; } } } }
u_char *pnp_scan_resources(device_t dev, u_char *resources, int len, struct isa_config *config, int ldn, pnp_scan_cb *cb) { u_char *p; u_char tag; int l; p = resources; while (len > 0) { tag = *p++; len--; if (PNP_RES_TYPE(tag) == 0) { /* small resource */ l = PNP_SRES_LEN(tag); if (len < l) break; if ((*cb)(dev, tag, p, l, config, ldn)) return (p + l); if (PNP_SRES_NUM(tag) == PNP_TAG_END) return (p + l); } else { /* large resource */ if (len < 2) break; l = I16(p); p += 2; len -= 2; if (len < l) break; if ((*cb)(dev, tag, p, l, config, ldn)) return (p + l); } p += l; len -= l; } return NULL; }
void pnp_parse_resources(device_t dev, u_char *resources, int len, int ldn) { struct isa_config *configs; struct isa_config *config; device_t parent; int priorities[1 + MAXDEP]; u_char *start; u_char *p; u_char tag; u_int32_t id; int ncfgs; int l; int i; parent = device_get_parent(dev); id = isa_get_logicalid(dev); configs = (struct isa_config *)malloc(sizeof(*configs)*(1 + MAXDEP), M_DEVBUF, M_NOWAIT | M_ZERO); if (configs == NULL) { device_printf(parent, "No memory to parse PNP data\n"); return; } config = &configs[0]; priorities[0] = 0; ncfgs = 1; p = resources; start = NULL; while (len > 0) { tag = *p++; len--; if (PNP_RES_TYPE(tag) == 0) { /* Small resource */ l = PNP_SRES_LEN(tag); if (len < l) { len = 0; continue; } len -= l; switch (PNP_SRES_NUM(tag)) { case PNP_TAG_START_DEPENDANT: if (start != NULL) { /* * Copy the common resources first, * then parse the "dependent" resources. */ pnp_merge_resources(dev, &configs[0], config); pnp_parse_dependant(dev, start, p - start - 1, config, ldn); } start = p + l; if (ncfgs > MAXDEP) { device_printf(parent, "too many dependant configs (%d)\n", MAXDEP); len = 0; break; } config = &configs[ncfgs]; /* * If the priority is not specified, * then use the default of 'acceptable' */ if (l > 0) priorities[ncfgs] = p[0]; else priorities[ncfgs] = 1; if (bootverbose) pnp_printf(id, "start dependent (%d)\n", priorities[ncfgs]); ncfgs++; break; case PNP_TAG_END_DEPENDANT: if (start == NULL) { device_printf(parent, "malformed resources\n"); len = 0; break; } /* * Copy the common resources first, * then parse the "dependent" resources. */ pnp_merge_resources(dev, &configs[0], config); pnp_parse_dependant(dev, start, p - start - 1, config, ldn); start = NULL; if (bootverbose) pnp_printf(id, "end dependent\n"); /* * Back to the common part; clear it * as its contents has already been copied * to each dependant. */ config = &configs[0]; bzero(config, sizeof(*config)); break; case PNP_TAG_END: if (start != NULL) { device_printf(parent, "malformed resources\n"); } len = 0; break; default: if (start != NULL) /* defer parsing a dependent section */ break; if (pnp_parse_desc(dev, tag, p, l, config, ldn)) len = 0; break; } p += l; } else { /* Large resource */ if (len < 2) { len = 0; break; } l = I16(p); p += 2; len -= 2; if (len < l) { len = 0; break; } len -= l; if (start == NULL && pnp_parse_desc(dev, tag, p, l, config, ldn)) { len = 0; break; } p += l; } } if (ncfgs == 1) { /* Single config without dependants */ ISA_ADD_CONFIG(parent, dev, priorities[0], &configs[0]); free(configs, M_DEVBUF); return; } for (i = 1; i < ncfgs; i++) { /* * Merge the remaining part of the common resources, * if any. Strictly speaking, there shouldn't be common/main * resources after the END_DEPENDENT tag. */ pnp_merge_resources(dev, &configs[0], &configs[i]); ISA_ADD_CONFIG(parent, dev, priorities[i], &configs[i]); } free(configs, M_DEVBUF); }
/* * Quiz the PnP BIOS, build a list of PNP IDs and resource data. */ static int pnpbios_identify(driver_t *driver, device_t parent) { struct PnPBIOS_table *pt = PnPBIOStable; struct bios_args args; struct pnp_sysdev *pd; struct pnp_sysdevargs *pda; uint16_t ndevs, bigdev; int error, currdev; uint8_t *devnodebuf, tag; uint32_t *devid, *compid; int idx, left; device_t dev; /* * Umm, we aren't going to rescan the PnP BIOS to look for new additions. */ if (device_get_state(parent) == DS_ATTACHED) return (0); /* no PnP BIOS information */ if (pt == NULL) return (ENXIO); /* ACPI already active */ if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL) return (ENXIO); bzero(&args, sizeof(args)); args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase); args.seg.code16.limit = 0xffff; /* XXX ? */ args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg); args.seg.data.limit = 0xffff; args.entry = pt->pmentryoffset; if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff)) kprintf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax); ndevs &= 0xff; /* clear high byte garbage */ if (bootverbose) kprintf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev); devnodebuf = kmalloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT); pda = (struct pnp_sysdevargs *)devnodebuf; pd = &pda->node; for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) { bzero(pd, bigdev); pda->next = currdev; /* get current configuration */ if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) { kprintf("pnpbios: error %d making BIOS16 call\n", error); break; } if (bootverbose) kprintf("pnp_get_devnode cd=%d nxt=%d size=%d handle=%d devid=%08x type=%02x%02x%02x, attrib=%04x\n", currdev, pda->next, pd->size, pd->handle, pd->devid, pd->type[0], pd->type[1], pd->type[2], pd->attrib); if ((error = (args.r.eax & 0xff))) { if (bootverbose) kprintf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev); if (error & 0x80) break; } currdev = pda->next; if (pd->size < sizeof(struct pnp_sysdev)) { kprintf("pnpbios: bogus system node data, aborting scan\n"); break; } /* * Ignore PICs so that we don't have to worry about the PICs * claiming IRQs to prevent their use. The PIC drivers * already ensure that invalid IRQs are not used. */ if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000")) /* ISA PIC */ continue; if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003")) /* APIC */ continue; /* Add the device and parse its resources */ dev = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, NULL, -1); isa_set_vendorid(dev, pd->devid); isa_set_logicalid(dev, pd->devid); /* * It appears that some PnP BIOS doesn't allow us to re-enable * the embedded system device once it is disabled. We shall * mark all system device nodes as "cannot be disabled", regardless * of actual settings in the device attribute byte. XXX */ #if 0 isa_set_configattr(dev, ((pd->attrib & PNPATTR_NODISABLE) ? 0 : ISACFGATTR_CANDISABLE) | ((!(pd->attrib & PNPATTR_NOCONFIG) && PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) ? ISACFGATTR_DYNAMIC : 0)); #endif isa_set_configattr(dev, (!(pd->attrib & PNPATTR_NOCONFIG) && PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC) ? ISACFGATTR_DYNAMIC : 0); ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0); pnp_parse_resources(dev, &pd->devdata[0], pd->size - sizeof(struct pnp_sysdev), 0); if (!device_get_desc(dev)) device_set_desc_copy(dev, pnp_eisaformat(pd->devid)); /* Find device IDs */ devid = &pd->devid; compid = NULL; /* look for a compatible device ID too */ left = pd->size - sizeof(struct pnp_sysdev); idx = 0; while (idx < left) { tag = pd->devdata[idx++]; if (PNP_RES_TYPE(tag) == 0) { /* Small resource */ switch (PNP_SRES_NUM(tag)) { case PNP_TAG_COMPAT_DEVICE: compid = (uint32_t *)(pd->devdata + idx); if (bootverbose) kprintf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid); /* FALLTHROUGH */ case PNP_TAG_END: idx = left; break; default: idx += PNP_SRES_LEN(tag); break; } } else /* Large resource, skip it */ idx += *(uint16_t *)(pd->devdata + idx) + 2; } if (bootverbose) { kprintf("pnpbios: handle %d device ID %s (%08x)", pd->handle, pnp_eisaformat(*devid), *devid); if (compid != NULL) kprintf(" compat ID %s (%08x)", pnp_eisaformat(*compid), *compid); kprintf("\n"); } } return (0); }