2 * Copyright (c) 2004 Joerg Sonnenberger <joerg@bec.de>
4 * Copyright (c) 1997 Michael Smith
5 * Copyright (c) 1998 Jonathan Lemon
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $DragonFly: src/sys/platform/pc32/i386/pnpbios.c,v 1.6 2006/12/23 00:27:03 swildner Exp $
33 * Code for dealing with the PnP-BIOS in x86 PC systems.
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
45 #include <machine/md_var.h>
46 #include <machine/pc/bios.h>
48 #include <bus/isa/pnpreg.h>
49 #include <bus/isa/isavar.h>
52 * PnP BIOS interface; enumerate devices only known to the system
53 * BIOS and save information about them for later use.
63 /* device-specific data comes here */
65 } __attribute__((__packed__));
67 #define PNPATTR_NODISABLE (1<<0) /* can't be disabled */
68 #define PNPATTR_NOCONFIG (1<<1) /* can't be configured */
69 #define PNPATTR_OUTPUT (1<<2) /* can be primary output */
70 #define PNPATTR_INPUT (1<<3) /* can be primary input */
71 #define PNPATTR_BOOTABLE (1<<4) /* can be booted from */
72 #define PNPATTR_DOCK (1<<5) /* is a docking station */
73 #define PNPATTR_REMOVEABLE (1<<6) /* device is removeable */
75 #define PNPATTR_CONFIG(a) (((a) >> 7) & 0x03)
76 #define PNPATTR_CONFIG_STATIC 0x00
77 #define PNPATTR_CONFIG_DYNAMIC 0x01
78 #define PNPATTR_CONFIG_DYNONLY 0x03
80 /* We have to cluster arguments within a 64k range for the bios16 call */
85 struct pnp_sysdev node;
89 * This function is called after the bus has assigned resource
90 * locations for a logical device.
93 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
98 * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
101 pnpbios_identify(driver_t *driver, device_t parent)
103 struct PnPBIOS_table *pt = PnPBIOStable;
104 struct bios_args args;
105 struct pnp_sysdev *pd;
106 struct pnp_sysdevargs *pda;
107 uint16_t ndevs, bigdev;
109 uint8_t *devnodebuf, tag;
110 uint32_t *devid, *compid;
115 * Umm, we aren't going to rescan the PnP BIOS to look for new additions.
117 if (device_get_state(parent) == DS_ATTACHED)
120 /* no PnP BIOS information */
124 /* ACPI already active */
125 if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
128 bzero(&args, sizeof(args));
129 args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
130 args.seg.code16.limit = 0xffff; /* XXX ? */
131 args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
132 args.seg.data.limit = 0xffff;
133 args.entry = pt->pmentryoffset;
135 if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
136 kprintf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
137 ndevs &= 0xff; /* clear high byte garbage */
139 kprintf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
141 devnodebuf = kmalloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT);
142 pda = (struct pnp_sysdevargs *)devnodebuf;
145 for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
149 /* get current configuration */
150 if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
151 kprintf("pnpbios: error %d making BIOS16 call\n", error);
155 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);
157 if ((error = (args.r.eax & 0xff))) {
159 kprintf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
164 if (pd->size < sizeof(struct pnp_sysdev)) {
165 kprintf("pnpbios: bogus system node data, aborting scan\n");
170 * Ignore PICs so that we don't have to worry about the PICs
171 * claiming IRQs to prevent their use. The PIC drivers
172 * already ensure that invalid IRQs are not used.
174 if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000")) /* ISA PIC */
176 if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003")) /* APIC */
179 /* Add the device and parse its resources */
180 dev = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, NULL, -1);
181 isa_set_vendorid(dev, pd->devid);
182 isa_set_logicalid(dev, pd->devid);
185 * It appears that some PnP BIOS doesn't allow us to re-enable
186 * the embedded system device once it is disabled. We shall
187 * mark all system device nodes as "cannot be disabled", regardless
188 * of actual settings in the device attribute byte. XXX
191 isa_set_configattr(dev,
192 ((pd->attrib & PNPATTR_NODISABLE) ? 0 : ISACFGATTR_CANDISABLE) |
193 ((!(pd->attrib & PNPATTR_NOCONFIG) &&
194 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
195 ? ISACFGATTR_DYNAMIC : 0));
197 isa_set_configattr(dev,
198 (!(pd->attrib & PNPATTR_NOCONFIG) &&
199 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
200 ? ISACFGATTR_DYNAMIC : 0);
202 ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
203 pnp_parse_resources(dev, &pd->devdata[0],
204 pd->size - sizeof(struct pnp_sysdev), 0);
205 if (!device_get_desc(dev))
206 device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
208 /* Find device IDs */
212 /* look for a compatible device ID too */
213 left = pd->size - sizeof(struct pnp_sysdev);
216 tag = pd->devdata[idx++];
217 if (PNP_RES_TYPE(tag) == 0) {
219 switch (PNP_SRES_NUM(tag)) {
220 case PNP_TAG_COMPAT_DEVICE:
221 compid = (uint32_t *)(pd->devdata + idx);
223 kprintf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
229 idx += PNP_SRES_LEN(tag);
233 /* Large resource, skip it */
234 idx += *(uint16_t *)(pd->devdata + idx) + 2;
237 kprintf("pnpbios: handle %d device ID %s (%08x)",
238 pd->handle, pnp_eisaformat(*devid), *devid);
240 kprintf(" compat ID %s (%08x)",
241 pnp_eisaformat(*compid), *compid);
248 static device_method_t pnpbios_methods[] = {
249 /* Device interface */
250 DEVMETHOD(device_identify, pnpbios_identify),
255 static driver_t pnpbios_driver = {
261 static devclass_t pnpbios_devclass;
263 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);