kernel: Use DEVMETHOD_END in the drivers.
[dragonfly.git] / sys / platform / pc32 / i386 / pnpbios.c
1 /*-
2  * Copyright (c) 2004 Joerg Sonnenberger <joerg@bec.de>
3  *
4  * Copyright (c) 1997 Michael Smith
5  * Copyright (c) 1998 Jonathan Lemon
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 /*
31  * Code for dealing with the PnP-BIOS in x86 PC systems.
32  */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/bus.h>
39
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
42
43 #include <machine/md_var.h>
44 #include <machine/pc/bios.h>
45
46 #include <bus/isa/pnpreg.h>
47 #include <bus/isa/isavar.h>
48
49 /*
50  * PnP BIOS interface; enumerate devices only known to the system
51  * BIOS and save information about them for later use.
52  */
53
54 struct pnp_sysdev 
55 {
56     uint16_t    size;
57     uint8_t     handle;
58     uint32_t    devid;
59     uint8_t     type[3];
60     uint16_t    attrib;
61     /* device-specific data comes here */
62     uint8_t     devdata[0];
63 } __attribute__((__packed__));
64
65 #define PNPATTR_NODISABLE       (1<<0)  /* can't be disabled */
66 #define PNPATTR_NOCONFIG        (1<<1)  /* can't be configured */
67 #define PNPATTR_OUTPUT          (1<<2)  /* can be primary output */
68 #define PNPATTR_INPUT           (1<<3)  /* can be primary input */
69 #define PNPATTR_BOOTABLE        (1<<4)  /* can be booted from */
70 #define PNPATTR_DOCK            (1<<5)  /* is a docking station */
71 #define PNPATTR_REMOVEABLE      (1<<6)  /* device is removeable */
72
73 #define PNPATTR_CONFIG(a)       (((a) >> 7) & 0x03)
74 #define PNPATTR_CONFIG_STATIC   0x00
75 #define PNPATTR_CONFIG_DYNAMIC  0x01
76 #define PNPATTR_CONFIG_DYNONLY  0x03
77
78 /* We have to cluster arguments within a 64k range for the bios16 call */
79 struct pnp_sysdevargs
80 {
81     uint16_t    next;
82     uint16_t    pad;
83     struct pnp_sysdev node;
84 };
85
86 /*
87  * This function is called after the bus has assigned resource
88  * locations for a logical device.
89  */
90 static void
91 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
92 {
93 }
94
95 /*
96  * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
97  */
98 static int
99 pnpbios_identify(driver_t *driver, device_t parent)
100 {
101     struct PnPBIOS_table        *pt = PnPBIOStable;
102     struct bios_args            args;
103     struct pnp_sysdev           *pd;
104     struct pnp_sysdevargs       *pda;
105     uint16_t                    ndevs, bigdev;
106     int                         error, currdev;
107     uint8_t                     *devnodebuf, tag;
108     uint32_t                    *devid, *compid;
109     int                         idx, left;
110     device_t                    dev;
111
112     /*
113      * Umm, we aren't going to rescan the PnP BIOS to look for new additions.
114      */
115     if (device_get_state(parent) == DS_ATTACHED)
116         return (0);
117         
118     /* no PnP BIOS information */
119     if (pt == NULL)
120         return (ENXIO);
121
122     /* ACPI already active */
123     if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
124         return (ENXIO);
125     
126     bzero(&args, sizeof(args));
127     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
128     args.seg.code16.limit = 0xffff;             /* XXX ? */
129     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
130     args.seg.data.limit = 0xffff;
131     args.entry = pt->pmentryoffset;
132
133     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
134         kprintf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
135     ndevs &= 0xff;                              /* clear high byte garbage */
136     if (bootverbose)
137         kprintf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
138
139     devnodebuf = kmalloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_INTWAIT);
140     pda = (struct pnp_sysdevargs *)devnodebuf;
141     pd = &pda->node;
142
143     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
144         bzero(pd, bigdev);
145         pda->next = currdev;
146
147         /* get current configuration */
148         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
149             kprintf("pnpbios: error %d making BIOS16 call\n", error);
150             break;
151         }
152         if (bootverbose)
153             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);
154
155         if ((error = (args.r.eax & 0xff))) {
156             if (bootverbose)
157                 kprintf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
158             if (error & 0x80) 
159                 break;
160         }
161         currdev = pda->next;
162         if (pd->size < sizeof(struct pnp_sysdev)) {
163             kprintf("pnpbios: bogus system node data, aborting scan\n");
164             break;
165         }
166         
167         /*
168          * Ignore PICs so that we don't have to worry about the PICs
169          * claiming IRQs to prevent their use.  The PIC drivers
170          * already ensure that invalid IRQs are not used.
171          */
172         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
173             continue;
174         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
175             continue;
176
177         /* Add the device and parse its resources */
178         dev = BUS_ADD_CHILD(parent, parent, ISA_ORDER_PNP, NULL, -1);
179         isa_set_vendorid(dev, pd->devid);
180         isa_set_logicalid(dev, pd->devid);
181
182         /*
183          * It appears that some PnP BIOS doesn't allow us to re-enable
184          * the embedded system device once it is disabled.  We shall
185          * mark all system device nodes as "cannot be disabled", regardless
186          * of actual settings in the device attribute byte.  XXX
187          */
188 #if 0
189         isa_set_configattr(dev, 
190             ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
191             ((!(pd->attrib & PNPATTR_NOCONFIG) && 
192                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
193                 ? ISACFGATTR_DYNAMIC : 0));
194 #endif
195         isa_set_configattr(dev, 
196             (!(pd->attrib & PNPATTR_NOCONFIG) && 
197                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
198                 ? ISACFGATTR_DYNAMIC : 0);
199
200         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
201         pnp_parse_resources(dev, &pd->devdata[0],
202                             pd->size - sizeof(struct pnp_sysdev), 0);
203         if (!device_get_desc(dev))
204             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
205
206         /* Find device IDs */
207         devid = &pd->devid;
208         compid = NULL;
209
210         /* look for a compatible device ID too */
211         left = pd->size - sizeof(struct pnp_sysdev);
212         idx = 0;
213         while (idx < left) {
214             tag = pd->devdata[idx++];
215             if (PNP_RES_TYPE(tag) == 0) {
216                 /* Small resource */
217                 switch (PNP_SRES_NUM(tag)) {
218                 case PNP_TAG_COMPAT_DEVICE:
219                     compid = (uint32_t *)(pd->devdata + idx);
220                     if (bootverbose)
221                         kprintf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
222                     /* FALLTHROUGH */
223                 case PNP_TAG_END:
224                     idx = left;
225                     break;
226                 default:
227                     idx += PNP_SRES_LEN(tag);
228                     break;
229                 }
230             } else
231                 /* Large resource, skip it */
232                 idx += *(uint16_t *)(pd->devdata + idx) + 2;
233         }
234         if (bootverbose) {
235             kprintf("pnpbios: handle %d device ID %s (%08x)", 
236                    pd->handle, pnp_eisaformat(*devid), *devid);
237             if (compid != NULL)
238                 kprintf(" compat ID %s (%08x)",
239                        pnp_eisaformat(*compid), *compid);
240             kprintf("\n");
241         }
242     }
243     return (0);
244 }
245
246 static device_method_t pnpbios_methods[] = {
247         /* Device interface */
248         DEVMETHOD(device_identify,      pnpbios_identify),
249
250         DEVMETHOD_END
251 };
252
253 static driver_t pnpbios_driver = {
254         "pnpbios",
255         pnpbios_methods,
256         1,                      /* no softc */
257 };
258
259 static devclass_t pnpbios_devclass;
260
261 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, NULL, NULL);