Index: head/sys/isa/isa_common.c =================================================================== --- head/sys/isa/isa_common.c (revision 327119) +++ head/sys/isa/isa_common.c (revision 327120) @@ -1,1127 +1,1149 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND MIT * * Copyright (c) 1999 Doug Rabson * 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. */ /* * Modifications for Intel architecture by Garrett A. Wollman. * Copyright 1998 Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that both the above copyright notice and this * permission notice appear in all copies, that both the above * copyright notice and this permission notice appear in all * supporting documentation, and that the name of M.I.T. not be used * in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. M.I.T. makes * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied * warranty. * * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT * SHALL M.I.T. 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. */ /* * Parts of the ISA bus implementation common to all architectures. */ #include __FBSDID("$FreeBSD$"); #include "opt_isa.h" #include #include #include #include +#include #include #include #include #include +#include #include #include #include static int isa_print_child(device_t bus, device_t dev); static MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); static int isa_running; /* * At 'probe' time, we add all the devices which we know about to the * bus. The generic attach routine will probe and attach them if they * are alive. */ static int isa_probe(device_t dev) { device_set_desc(dev, "ISA bus"); isa_init(dev); /* Allow machdep code to initialise */ return (0); } extern device_t isa_bus_device; static int isa_attach(device_t dev) { /* * Arrange for isa_probe_children(dev) to be called later. XXX */ isa_bus_device = dev; return (0); } /* * Find a working set of memory regions for a child using the ranges * in *config and return the regions in *result. Returns non-zero if * a set of ranges was found. */ static int isa_find_memory(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NMEM]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NMEM; i++) { bus_delete_resource(child, SYS_RES_MEMORY, i); res[i] = NULL; } success = 1; result->ic_nmem = config->ic_nmem; for (i = 0; i < config->ic_nmem; i++) { uint32_t start, end, size, align; size = config->ic_mem[i].ir_size; /* the PnP device may have a null resource as filler */ if (size == 0) { result->ic_mem[i].ir_start = 0; result->ic_mem[i].ir_end = 0; result->ic_mem[i].ir_size = 0; result->ic_mem[i].ir_align = 0; continue; } for (start = config->ic_mem[i].ir_start, end = config->ic_mem[i].ir_end, align = config->ic_mem[i].ir_align; start + size - 1 <= end && start + size > start; start += MAX(align, 1)) { bus_set_resource(child, SYS_RES_MEMORY, i, start, size); res[i] = bus_alloc_resource_any(child, SYS_RES_MEMORY, &i, rman_make_alignment_flags(align) /* !RF_ACTIVE */); if (res[i]) { result->ic_mem[i].ir_start = start; result->ic_mem[i].ir_end = start + size - 1; result->ic_mem[i].ir_size = size; result->ic_mem[i].ir_align = align; break; } } /* * If we didn't find a place for memory range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NMEM; i++) { if (res[i]) bus_release_resource(child, SYS_RES_MEMORY, i, res[i]); } return (success); } /* * Find a working set of port regions for a child using the ranges * in *config and return the regions in *result. Returns non-zero if * a set of ranges was found. */ static int isa_find_port(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NPORT]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NPORT; i++) { bus_delete_resource(child, SYS_RES_IOPORT, i); res[i] = NULL; } success = 1; result->ic_nport = config->ic_nport; for (i = 0; i < config->ic_nport; i++) { uint32_t start, end, size, align; size = config->ic_port[i].ir_size; /* the PnP device may have a null resource as filler */ if (size == 0) { result->ic_port[i].ir_start = 0; result->ic_port[i].ir_end = 0; result->ic_port[i].ir_size = 0; result->ic_port[i].ir_align = 0; continue; } for (start = config->ic_port[i].ir_start, end = config->ic_port[i].ir_end, align = config->ic_port[i].ir_align; start + size - 1 <= end; start += align) { bus_set_resource(child, SYS_RES_IOPORT, i, start, size); res[i] = bus_alloc_resource_any(child, SYS_RES_IOPORT, &i, rman_make_alignment_flags(align) /* !RF_ACTIVE */); if (res[i]) { result->ic_port[i].ir_start = start; result->ic_port[i].ir_end = start + size - 1; result->ic_port[i].ir_size = size; result->ic_port[i].ir_align = align; break; } } /* * If we didn't find a place for port range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NPORT; i++) { if (res[i]) bus_release_resource(child, SYS_RES_IOPORT, i, res[i]); } return success; } /* * Return the index of the first bit in the mask (or -1 if mask is empty. */ static int find_first_bit(uint32_t mask) { return (ffs(mask) - 1); } /* * Return the index of the next bit in the mask, or -1 if there are no more. */ static int find_next_bit(uint32_t mask, int bit) { bit++; while (bit < 32 && !(mask & (1 << bit))) bit++; if (bit != 32) return (bit); return (-1); } /* * Find a working set of irqs for a child using the masks in *config * and return the regions in *result. Returns non-zero if a set of * irqs was found. */ static int isa_find_irq(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NIRQ]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NIRQ; i++) { bus_delete_resource(child, SYS_RES_IRQ, i); res[i] = NULL; } success = 1; result->ic_nirq = config->ic_nirq; for (i = 0; i < config->ic_nirq; i++) { uint32_t mask = config->ic_irqmask[i]; int irq; /* the PnP device may have a null resource as filler */ if (mask == 0) { result->ic_irqmask[i] = 0; continue; } for (irq = find_first_bit(mask); irq != -1; irq = find_next_bit(mask, irq)) { bus_set_resource(child, SYS_RES_IRQ, i, irq, 1); res[i] = bus_alloc_resource_any(child, SYS_RES_IRQ, &i, 0 /* !RF_ACTIVE */ ); if (res[i]) { result->ic_irqmask[i] = (1 << irq); break; } } /* * If we didn't find a place for irq range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NIRQ; i++) { if (res[i]) bus_release_resource(child, SYS_RES_IRQ, i, res[i]); } return (success); } /* * Find a working set of drqs for a child using the masks in *config * and return the regions in *result. Returns non-zero if a set of * drqs was found. */ static int isa_find_drq(device_t child, struct isa_config *config, struct isa_config *result) { int success, i; struct resource *res[ISA_NDRQ]; /* * First clear out any existing resource definitions. */ for (i = 0; i < ISA_NDRQ; i++) { bus_delete_resource(child, SYS_RES_DRQ, i); res[i] = NULL; } success = 1; result->ic_ndrq = config->ic_ndrq; for (i = 0; i < config->ic_ndrq; i++) { uint32_t mask = config->ic_drqmask[i]; int drq; /* the PnP device may have a null resource as filler */ if (mask == 0) { result->ic_drqmask[i] = 0; continue; } for (drq = find_first_bit(mask); drq != -1; drq = find_next_bit(mask, drq)) { bus_set_resource(child, SYS_RES_DRQ, i, drq, 1); res[i] = bus_alloc_resource_any(child, SYS_RES_DRQ, &i, 0 /* !RF_ACTIVE */); if (res[i]) { result->ic_drqmask[i] = (1 << drq); break; } } /* * If we didn't find a place for drq range i, then * give up now. */ if (!res[i]) { success = 0; break; } } for (i = 0; i < ISA_NDRQ; i++) { if (res[i]) bus_release_resource(child, SYS_RES_DRQ, i, res[i]); } return (success); } /* * Attempt to find a working set of resources for a device. Return * non-zero if a working configuration is found. */ static int isa_assign_resources(device_t child) { struct isa_device *idev = DEVTOISA(child); struct isa_config_entry *ice; struct isa_config *cfg; const char *reason; reason = "Empty ISA id_configs"; cfg = malloc(sizeof(struct isa_config), M_TEMP, M_NOWAIT|M_ZERO); if (cfg == NULL) return(0); TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { reason = "memory"; if (!isa_find_memory(child, &ice->ice_config, cfg)) continue; reason = "port"; if (!isa_find_port(child, &ice->ice_config, cfg)) continue; reason = "irq"; if (!isa_find_irq(child, &ice->ice_config, cfg)) continue; reason = "drq"; if (!isa_find_drq(child, &ice->ice_config, cfg)) continue; /* * A working configuration was found enable the device * with this configuration. */ reason = "no callback"; if (idev->id_config_cb) { idev->id_config_cb(idev->id_config_arg, cfg, 1); free(cfg, M_TEMP); return (1); } } /* * Disable the device. */ bus_print_child_header(device_get_parent(child), child); printf(" can't assign resources (%s)\n", reason); if (bootverbose) isa_print_child(device_get_parent(child), child); bzero(cfg, sizeof (*cfg)); if (idev->id_config_cb) idev->id_config_cb(idev->id_config_arg, cfg, 0); device_disable(child); free(cfg, M_TEMP); return (0); } /* * Claim any unallocated resources to keep other devices from using * them. */ static void isa_claim_resources(device_t dev, device_t child) { struct isa_device *idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; int rid; STAILQ_FOREACH(rle, rl, link) { if (!rle->res) { rid = rle->rid; resource_list_alloc(rl, dev, child, rle->type, &rid, 0, ~0, 1, 0); } } } /* * Called after other devices have initialised to probe for isa devices. */ void isa_probe_children(device_t dev) { struct isa_device *idev; device_t *children, child; struct isa_config *cfg; - int nchildren, i; + int nchildren, i, err; /* * Create all the non-hinted children by calling drivers' * identify methods. */ bus_generic_probe(dev); if (device_get_children(dev, &children, &nchildren)) return; /* * First disable all pnp devices so that they don't get * matched by legacy probes. */ if (bootverbose) printf("isa_probe_children: disabling PnP devices\n"); cfg = malloc(sizeof(*cfg), M_TEMP, M_NOWAIT|M_ZERO); if (cfg == NULL) { free(children, M_TEMP); return; } for (i = 0; i < nchildren; i++) { idev = DEVTOISA(children[i]); bzero(cfg, sizeof(*cfg)); if (idev->id_config_cb) idev->id_config_cb(idev->id_config_arg, cfg, 0); } free(cfg, M_TEMP); /* * Next, probe all the PnP BIOS devices so they can subsume any * hints. */ for (i = 0; i < nchildren; i++) { child = children[i]; idev = DEVTOISA(child); if (idev->id_order > ISA_ORDER_PNPBIOS) continue; if (!TAILQ_EMPTY(&idev->id_configs) && !isa_assign_resources(child)) continue; if (device_probe_and_attach(child) == 0) isa_claim_resources(dev, child); } free(children, M_TEMP); /* * Next, enumerate hinted devices and probe all non-pnp devices so * that they claim their resources first. */ bus_enumerate_hinted_children(dev); if (device_get_children(dev, &children, &nchildren)) return; if (bootverbose) printf("isa_probe_children: probing non-PnP devices\n"); for (i = 0; i < nchildren; i++) { child = children[i]; idev = DEVTOISA(child); if (device_is_attached(child) || !TAILQ_EMPTY(&idev->id_configs)) continue; - device_probe_and_attach(child); + err = device_probe_and_attach(child); + if (err == 0 && idev->id_vendorid == 0 && + strcmp(kern_ident, "GENERIC") == 0) + device_printf(child, + "non-PNP ISA device will be removed from GENERIC in FreeBSD 12."); } /* * Finally assign resource to pnp devices and probe them. */ if (bootverbose) printf("isa_probe_children: probing PnP devices\n"); for (i = 0; i < nchildren; i++) { child = children[i]; idev = DEVTOISA(child); if (device_is_attached(child) || TAILQ_EMPTY(&idev->id_configs)) continue; if (isa_assign_resources(child)) { device_probe_and_attach(child); isa_claim_resources(dev, child); } } free(children, M_TEMP); isa_running = 1; } /* * Add a new child with default ivars. */ static device_t isa_add_child(device_t dev, u_int order, const char *name, int unit) { device_t child; struct isa_device *idev; child = device_add_child_ordered(dev, order, name, unit); if (child == NULL) return (child); idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT | M_ZERO); if (!idev) return (0); resource_list_init(&idev->id_resources); TAILQ_INIT(&idev->id_configs); idev->id_order = order; device_set_ivars(child, idev); return (child); } static int isa_print_all_resources(device_t dev) { struct isa_device *idev = DEVTOISA(dev); struct resource_list *rl = &idev->id_resources; int retval = 0; if (STAILQ_FIRST(rl) || device_get_flags(dev)) retval += printf(" at"); retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#jx"); retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#jx"); retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd"); retval += resource_list_print_type(rl, "drq", SYS_RES_DRQ, "%jd"); if (device_get_flags(dev)) retval += printf(" flags %#x", device_get_flags(dev)); -#ifdef ISAPNP if (idev->id_vendorid) retval += printf(" pnpid %s", pnp_eisaformat(idev->id_vendorid)); -#endif return (retval); } static int isa_print_child(device_t bus, device_t dev) { int retval = 0; retval += bus_print_child_header(bus, dev); retval += isa_print_all_resources(dev); retval += bus_print_child_footer(bus, dev); return (retval); } static void isa_probe_nomatch(device_t dev, device_t child) { if (bootverbose) { bus_print_child_header(dev, child); printf(" failed to probe"); isa_print_all_resources(child); bus_print_child_footer(dev, child); } return; } static int isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) { struct isa_device* idev = DEVTOISA(dev); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; switch (index) { case ISA_IVAR_PORT_0: rle = resource_list_find(rl, SYS_RES_IOPORT, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_PORT_1: rle = resource_list_find(rl, SYS_RES_IOPORT, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_PORTSIZE_0: rle = resource_list_find(rl, SYS_RES_IOPORT, 0); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_PORTSIZE_1: rle = resource_list_find(rl, SYS_RES_IOPORT, 1); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_MADDR_0: rle = resource_list_find(rl, SYS_RES_MEMORY, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_MADDR_1: rle = resource_list_find(rl, SYS_RES_MEMORY, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_MEMSIZE_0: rle = resource_list_find(rl, SYS_RES_MEMORY, 0); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_MEMSIZE_1: rle = resource_list_find(rl, SYS_RES_MEMORY, 1); if (rle) *result = rle->count; else *result = 0; break; case ISA_IVAR_IRQ_0: rle = resource_list_find(rl, SYS_RES_IRQ, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_IRQ_1: rle = resource_list_find(rl, SYS_RES_IRQ, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_DRQ_0: rle = resource_list_find(rl, SYS_RES_DRQ, 0); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_DRQ_1: rle = resource_list_find(rl, SYS_RES_DRQ, 1); if (rle) *result = rle->start; else *result = -1; break; case ISA_IVAR_VENDORID: *result = idev->id_vendorid; break; case ISA_IVAR_SERIAL: *result = idev->id_serial; break; case ISA_IVAR_LOGICALID: *result = idev->id_logicalid; break; case ISA_IVAR_COMPATID: *result = idev->id_compatid; break; case ISA_IVAR_CONFIGATTR: *result = idev->id_config_attr; break; case ISA_IVAR_PNP_CSN: *result = idev->id_pnp_csn; break; case ISA_IVAR_PNP_LDN: *result = idev->id_pnp_ldn; break; case ISA_IVAR_PNPBIOS_HANDLE: *result = idev->id_pnpbios_handle; break; default: return (ENOENT); } return (0); } static int isa_write_ivar(device_t bus, device_t dev, int index, uintptr_t value) { struct isa_device* idev = DEVTOISA(dev); switch (index) { case ISA_IVAR_PORT_0: case ISA_IVAR_PORT_1: case ISA_IVAR_PORTSIZE_0: case ISA_IVAR_PORTSIZE_1: case ISA_IVAR_MADDR_0: case ISA_IVAR_MADDR_1: case ISA_IVAR_MEMSIZE_0: case ISA_IVAR_MEMSIZE_1: case ISA_IVAR_IRQ_0: case ISA_IVAR_IRQ_1: case ISA_IVAR_DRQ_0: case ISA_IVAR_DRQ_1: return (EINVAL); case ISA_IVAR_VENDORID: idev->id_vendorid = value; break; case ISA_IVAR_SERIAL: idev->id_serial = value; break; case ISA_IVAR_LOGICALID: idev->id_logicalid = value; break; case ISA_IVAR_COMPATID: idev->id_compatid = value; break; case ISA_IVAR_CONFIGATTR: idev->id_config_attr = value; break; default: return (ENOENT); } return (0); } /* * Free any resources which the driver missed or which we were holding for * it (see isa_probe_children). */ static void isa_child_detached(device_t dev, device_t child) { struct isa_device* idev = DEVTOISA(child); if (TAILQ_FIRST(&idev->id_configs)) isa_claim_resources(dev, child); } static void isa_driver_added(device_t dev, driver_t *driver) { device_t *children; int nchildren, i; /* * Don't do anything if drivers are dynamically * added during autoconfiguration (cf. ymf724). * since that would end up calling identify * twice. */ if (!isa_running) return; DEVICE_IDENTIFY(driver, dev); if (device_get_children(dev, &children, &nchildren)) return; for (i = 0; i < nchildren; i++) { device_t child = children[i]; struct isa_device *idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; struct resource_list_entry *rle; if (device_get_state(child) != DS_NOTPRESENT) continue; if (!device_is_enabled(child)) continue; /* * Free resources which we were holding on behalf of * the device. */ STAILQ_FOREACH(rle, &idev->id_resources, link) { if (rle->res) resource_list_release(rl, dev, child, rle->type, rle->rid, rle->res); } if (TAILQ_FIRST(&idev->id_configs)) if (!isa_assign_resources(child)) continue; device_probe_and_attach(child); if (TAILQ_FIRST(&idev->id_configs)) isa_claim_resources(dev, child); } free(children, M_TEMP); } static int isa_set_resource(device_t dev, device_t child, int type, int rid, rman_res_t start, rman_res_t count) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY && type != SYS_RES_IRQ && type != SYS_RES_DRQ) return (EINVAL); if (rid < 0) return (EINVAL); if (type == SYS_RES_IOPORT && rid >= ISA_NPORT) return (EINVAL); if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) return (EINVAL); if (type == SYS_RES_IRQ && rid >= ISA_NIRQ) return (EINVAL); if (type == SYS_RES_DRQ && rid >= ISA_NDRQ) return (EINVAL); resource_list_add(rl, type, rid, start, start + count - 1, count); return (0); } static struct resource_list * isa_get_resource_list (device_t dev, device_t child) { struct isa_device* idev = DEVTOISA(child); struct resource_list *rl = &idev->id_resources; if (!rl) return (NULL); return (rl); } static int isa_add_config(device_t dev, device_t child, int priority, struct isa_config *config) { struct isa_device* idev = DEVTOISA(child); struct isa_config_entry *newice, *ice; newice = malloc(sizeof *ice, M_DEVBUF, M_NOWAIT); if (!newice) return (ENOMEM); newice->ice_priority = priority; newice->ice_config = *config; TAILQ_FOREACH(ice, &idev->id_configs, ice_link) { if (ice->ice_priority > priority) break; } if (ice) TAILQ_INSERT_BEFORE(ice, newice, ice_link); else TAILQ_INSERT_TAIL(&idev->id_configs, newice, ice_link); return (0); } static void isa_set_config_callback(device_t dev, device_t child, isa_config_cb *fn, void *arg) { struct isa_device* idev = DEVTOISA(child); idev->id_config_cb = fn; idev->id_config_arg = arg; } static int isa_pnp_probe(device_t dev, device_t child, struct isa_pnp_id *ids) { struct isa_device* idev = DEVTOISA(child); if (!idev->id_vendorid) return (ENOENT); while (ids && ids->ip_id) { /* * Really ought to support >1 compat id per device. */ if (idev->id_logicalid == ids->ip_id || idev->id_compatid == ids->ip_id) { if (ids->ip_desc) device_set_desc(child, ids->ip_desc); return (0); } ids++; } return (ENXIO); } static int isa_child_pnpinfo_str(device_t bus, device_t child, char *buf, size_t buflen) { -#ifdef ISAPNP struct isa_device *idev = DEVTOISA(child); if (idev->id_vendorid) snprintf(buf, buflen, "pnpid=%s", pnp_eisaformat(idev->id_vendorid)); -#endif return (0); } static int isa_child_location_str(device_t bus, device_t child, char *buf, size_t buflen) { #if 0 /* id_pnphandle isn't there yet */ struct isa_device *idev = DEVTOISA(child); if (idev->id_vendorid) snprintf(buf, buflen, "pnphandle=%d", idev->id_pnphandle); #endif /* Nothing here yet */ *buf = '\0'; return (0); } static device_method_t isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isa_probe), DEVMETHOD(device_attach, isa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_add_child, isa_add_child), DEVMETHOD(bus_print_child, isa_print_child), DEVMETHOD(bus_probe_nomatch, isa_probe_nomatch), DEVMETHOD(bus_read_ivar, isa_read_ivar), DEVMETHOD(bus_write_ivar, isa_write_ivar), DEVMETHOD(bus_child_detached, isa_child_detached), DEVMETHOD(bus_driver_added, isa_driver_added), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_get_resource_list,isa_get_resource_list), DEVMETHOD(bus_alloc_resource, isa_alloc_resource), DEVMETHOD(bus_release_resource, isa_release_resource), DEVMETHOD(bus_set_resource, isa_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_child_pnpinfo_str, isa_child_pnpinfo_str), DEVMETHOD(bus_child_location_str, isa_child_location_str), DEVMETHOD(bus_hinted_child, isa_hinted_child), DEVMETHOD(bus_hint_device_unit, isa_hint_device_unit), /* ISA interface */ DEVMETHOD(isa_add_config, isa_add_config), DEVMETHOD(isa_set_config_callback, isa_set_config_callback), DEVMETHOD(isa_pnp_probe, isa_pnp_probe), { 0, 0 } }; DEFINE_CLASS_0(isa, isa_driver, isa_methods, 0); devclass_t isa_devclass; /* * ISA can be attached to a PCI-ISA bridge, or other locations on some * platforms. */ DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); DRIVER_MODULE(isa, eisab, isa_driver, isa_devclass, 0, 0); MODULE_VERSION(isa, 1); /* * Code common to ISA bridges. */ devclass_t isab_devclass; int isab_attach(device_t dev) { device_t child; child = device_add_child(dev, "isa", 0); if (child != NULL) return (bus_generic_attach(dev)); return (ENXIO); +} + +char * +pnp_eisaformat(uint32_t id) +{ + uint8_t *data; + static char idbuf[8]; + const char hextoascii[] = "0123456789abcdef"; + + id = htole32(id); + data = (uint8_t *)&id; + idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); + idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); + idbuf[2] = '@' + (data[1] & 0x1f); + idbuf[3] = hextoascii[(data[2] >> 4)]; + idbuf[4] = hextoascii[(data[2] & 0xf)]; + idbuf[5] = hextoascii[(data[3] >> 4)]; + idbuf[6] = hextoascii[(data[3] & 0xf)]; + idbuf[7] = 0; + return(idbuf); } Index: head/sys/isa/pnp.c =================================================================== --- head/sys/isa/pnp.c (revision 327119) +++ head/sys/isa/pnp.c (revision 327120) @@ -1,792 +1,772 @@ /* * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1996, Sujal M. Patel * 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. * * from: pnp.c,v 1.11 1999/05/06 22:11:19 peter Exp */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include typedef struct _pnp_id { uint32_t vendor_id; uint32_t serial; u_char checksum; } pnp_id; struct pnp_set_config_arg { int csn; /* Card number to configure */ int ldn; /* Logical device on card */ }; struct pnp_quirk { uint32_t vendor_id; /* Vendor of the card */ uint32_t logical_id; /* ID of the device with quirk */ int type; #define PNP_QUIRK_WRITE_REG 1 /* Need to write a pnp register */ #define PNP_QUIRK_EXTRA_IO 2 /* Has extra io ports */ int arg1; int arg2; }; struct pnp_quirk pnp_quirks[] = { /* * The Gravis UltraSound needs register 0xf2 to be set to 0xff * to enable power. * XXX need to know the logical device id. */ { 0x0100561e /* GRV0001 */, 0, PNP_QUIRK_WRITE_REG, 0xf2, 0xff }, /* * An emu8000 does not give us other than the first * port. */ { 0x26008c0e /* SB16 */, 0x21008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0x42008c0e /* SB32(CTL0042) */, 0x21008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0x44008c0e /* SB32(CTL0044) */, 0x21008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0x49008c0e /* SB32(CTL0049) */, 0x21008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0xf1008c0e /* SB32(CTL00f1) */, 0x21008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0xc1008c0e /* SB64(CTL00c1) */, 0x22008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0xc5008c0e /* SB64(CTL00c5) */, 0x22008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0xe4008c0e /* SB64(CTL00e4) */, 0x22008c0e, PNP_QUIRK_EXTRA_IO, 0x400, 0x800 }, { 0 } }; /* The READ_DATA port that we are using currently */ static int pnp_rd_port; static void pnp_send_initiation_key(void); static int pnp_get_serial(pnp_id *p); static int pnp_isolation_protocol(device_t parent); -char * -pnp_eisaformat(uint32_t id) -{ - uint8_t *data; - static char idbuf[8]; - const char hextoascii[] = "0123456789abcdef"; - - id = htole32(id); - data = (uint8_t *)&id; - idbuf[0] = '@' + ((data[0] & 0x7c) >> 2); - idbuf[1] = '@' + (((data[0] & 0x3) << 3) + ((data[1] & 0xe0) >> 5)); - idbuf[2] = '@' + (data[1] & 0x1f); - idbuf[3] = hextoascii[(data[2] >> 4)]; - idbuf[4] = hextoascii[(data[2] & 0xf)]; - idbuf[5] = hextoascii[(data[3] >> 4)]; - idbuf[6] = hextoascii[(data[3] & 0xf)]; - idbuf[7] = 0; - return(idbuf); -} - static void pnp_write(int d, u_char r) { outb (_PNP_ADDRESS, d); outb (_PNP_WRITE_DATA, r); } /* * Send Initiation LFSR as described in "Plug and Play ISA Specification", * Intel May 94. */ static void pnp_send_initiation_key() { int cur, i; /* Reset the LSFR */ outb(_PNP_ADDRESS, 0); outb(_PNP_ADDRESS, 0); /* yes, we do need it twice! */ cur = 0x6a; outb(_PNP_ADDRESS, cur); for (i = 1; i < 32; i++) { cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); outb(_PNP_ADDRESS, cur); } } /* * Get the device's serial number. Returns 1 if the serial is valid. */ static int pnp_get_serial(pnp_id *p) { int i, bit, valid = 0, sum = 0x6a; u_char *data = (u_char *)p; bzero(data, sizeof(char) * 9); outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION); for (i = 0; i < 72; i++) { bit = inb((pnp_rd_port << 2) | 0x3) == 0x55; DELAY(250); /* Delay 250 usec */ /* Can't Short Circuit the next evaluation, so 'and' is last */ bit = (inb((pnp_rd_port << 2) | 0x3) == 0xaa) && bit; DELAY(250); /* Delay 250 usec */ valid = valid || bit; if (i < 64) sum = (sum >> 1) | (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); } valid = valid && (data[8] == sum); return (valid); } /* * Fill's the buffer with resource info from the device. * Returns the number of characters read. */ static int pnp_get_resource_info(u_char *buffer, int len) { int i, j, count; u_char temp; count = 0; for (i = 0; i < len; i++) { outb(_PNP_ADDRESS, PNP_STATUS); for (j = 0; j < 100; j++) { if ((inb((pnp_rd_port << 2) | 0x3)) & 0x1) break; DELAY(10); } if (j == 100) { printf("PnP device failed to report resource data\n"); return (count); } outb(_PNP_ADDRESS, PNP_RESOURCE_DATA); temp = inb((pnp_rd_port << 2) | 0x3); if (buffer != NULL) buffer[i] = temp; count++; } return (count); } /* * This function is called after the bus has assigned resource * locations for a logical device. */ static void pnp_set_config(void *arg, struct isa_config *config, int enable) { int csn = ((struct pnp_set_config_arg *) arg)->csn; int ldn = ((struct pnp_set_config_arg *) arg)->ldn; int i; /* * First put all cards into Sleep state with the initiation * key, then put our card into Config state. */ pnp_send_initiation_key(); pnp_write(PNP_WAKE, csn); /* * Select our logical device so that we can program it. */ pnp_write(PNP_SET_LDN, ldn); /* * Constrain the number of resources we will try to program */ if (config->ic_nmem > ISA_PNP_NMEM) { printf("too many ISA memory ranges (%d > %d)\n", config->ic_nmem, ISA_PNP_NMEM); config->ic_nmem = ISA_PNP_NMEM; } if (config->ic_nport > ISA_PNP_NPORT) { printf("too many ISA I/O ranges (%d > %d)\n", config->ic_nport, ISA_PNP_NPORT); config->ic_nport = ISA_PNP_NPORT; } if (config->ic_nirq > ISA_PNP_NIRQ) { printf("too many ISA IRQs (%d > %d)\n", config->ic_nirq, ISA_PNP_NIRQ); config->ic_nirq = ISA_PNP_NIRQ; } if (config->ic_ndrq > ISA_PNP_NDRQ) { printf("too many ISA DRQs (%d > %d)\n", config->ic_ndrq, ISA_PNP_NDRQ); config->ic_ndrq = ISA_PNP_NDRQ; } /* * Now program the resources. */ for (i = 0; i < config->ic_nmem; i++) { uint32_t start; uint32_t size; /* XXX: should handle memory control register, 32 bit memory */ if (config->ic_mem[i].ir_size == 0) { pnp_write(PNP_MEM_BASE_HIGH(i), 0); pnp_write(PNP_MEM_BASE_LOW(i), 0); pnp_write(PNP_MEM_RANGE_HIGH(i), 0); pnp_write(PNP_MEM_RANGE_LOW(i), 0); } else { start = config->ic_mem[i].ir_start; size = config->ic_mem[i].ir_size; if (start & 0xff) panic("pnp_set_config: bogus memory assignment"); pnp_write(PNP_MEM_BASE_HIGH(i), (start >> 16) & 0xff); pnp_write(PNP_MEM_BASE_LOW(i), (start >> 8) & 0xff); pnp_write(PNP_MEM_RANGE_HIGH(i), (size >> 16) & 0xff); pnp_write(PNP_MEM_RANGE_LOW(i), (size >> 8) & 0xff); } } for (; i < ISA_PNP_NMEM; i++) { pnp_write(PNP_MEM_BASE_HIGH(i), 0); pnp_write(PNP_MEM_BASE_LOW(i), 0); pnp_write(PNP_MEM_RANGE_HIGH(i), 0); pnp_write(PNP_MEM_RANGE_LOW(i), 0); } for (i = 0; i < config->ic_nport; i++) { uint32_t start; if (config->ic_port[i].ir_size == 0) { pnp_write(PNP_IO_BASE_HIGH(i), 0); pnp_write(PNP_IO_BASE_LOW(i), 0); } else { start = config->ic_port[i].ir_start; pnp_write(PNP_IO_BASE_HIGH(i), (start >> 8) & 0xff); pnp_write(PNP_IO_BASE_LOW(i), (start >> 0) & 0xff); } } for (; i < ISA_PNP_NPORT; i++) { pnp_write(PNP_IO_BASE_HIGH(i), 0); pnp_write(PNP_IO_BASE_LOW(i), 0); } for (i = 0; i < config->ic_nirq; i++) { int irq; /* XXX: interrupt type */ if (config->ic_irqmask[i] == 0) { pnp_write(PNP_IRQ_LEVEL(i), 0); pnp_write(PNP_IRQ_TYPE(i), 2); } else { irq = ffs(config->ic_irqmask[i]) - 1; pnp_write(PNP_IRQ_LEVEL(i), irq); pnp_write(PNP_IRQ_TYPE(i), 2); /* XXX */ } } for (; i < ISA_PNP_NIRQ; i++) { /* * IRQ 0 is not a valid interrupt selection and * represents no interrupt selection. */ pnp_write(PNP_IRQ_LEVEL(i), 0); pnp_write(PNP_IRQ_TYPE(i), 2); } for (i = 0; i < config->ic_ndrq; i++) { int drq; if (config->ic_drqmask[i] == 0) { pnp_write(PNP_DMA_CHANNEL(i), 4); } else { drq = ffs(config->ic_drqmask[i]) - 1; pnp_write(PNP_DMA_CHANNEL(i), drq); } } for (; i < ISA_PNP_NDRQ; i++) { /* * DMA channel 4, the cascade channel is used to * indicate no DMA channel is active. */ pnp_write(PNP_DMA_CHANNEL(i), 4); } pnp_write(PNP_ACTIVATE, enable ? 1 : 0); /* * Wake everyone up again, we are finished. */ pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY); } /* * Process quirks for a logical device.. The card must be in Config state. */ void pnp_check_quirks(uint32_t vendor_id, uint32_t logical_id, int ldn, struct isa_config *config) { struct pnp_quirk *qp; for (qp = &pnp_quirks[0]; qp->vendor_id; qp++) { if (qp->vendor_id == vendor_id && (qp->logical_id == 0 || qp->logical_id == logical_id)) { switch (qp->type) { case PNP_QUIRK_WRITE_REG: pnp_write(PNP_SET_LDN, ldn); pnp_write(qp->arg1, qp->arg2); break; case PNP_QUIRK_EXTRA_IO: if (config == NULL) break; if (qp->arg1 != 0) { config->ic_nport++; config->ic_port[config->ic_nport - 1] = config->ic_port[0]; config->ic_port[config->ic_nport - 1].ir_start += qp->arg1; config->ic_port[config->ic_nport - 1].ir_end += qp->arg1; } if (qp->arg2 != 0) { config->ic_nport++; config->ic_port[config->ic_nport - 1] = config->ic_port[0]; config->ic_port[config->ic_nport - 1].ir_start += qp->arg2; config->ic_port[config->ic_nport - 1].ir_end += qp->arg2; } break; } } } } /* * Scan Resource Data for Logical Devices. * * This function exits as soon as it gets an error reading *ANY* * Resource Data or it reaches the end of Resource Data. In the first * case the return value will be TRUE, FALSE otherwise. */ static int pnp_create_devices(device_t parent, pnp_id *p, int csn, u_char *resources, int len) { u_char tag, *resp, *resinfo, *startres = NULL; int large_len, scanning = len, retval = FALSE; uint32_t logical_id; device_t dev = 0; int ldn = 0; struct pnp_set_config_arg *csnldn; char buf[100]; char *desc = NULL; resp = resources; while (scanning > 0) { tag = *resp++; scanning--; if (PNP_RES_TYPE(tag) != 0) { /* Large resource */ if (scanning < 2) { scanning = 0; continue; } large_len = resp[0] + (resp[1] << 8); resp += 2; if (scanning < large_len) { scanning = 0; continue; } resinfo = resp; resp += large_len; scanning -= large_len; if (PNP_LRES_NUM(tag) == PNP_TAG_ID_ANSI) { if (dev) { /* * This is an optional device * identifier string. Skip it * for now. */ continue; } /* else mandately card identifier string */ if (large_len > sizeof(buf) - 1) large_len = sizeof(buf) - 1; bcopy(resinfo, buf, large_len); /* * Trim trailing spaces. */ while (buf[large_len-1] == ' ') large_len--; buf[large_len] = '\0'; desc = buf; continue; } continue; } /* Small resource */ if (scanning < PNP_SRES_LEN(tag)) { scanning = 0; continue; } resinfo = resp; resp += PNP_SRES_LEN(tag); scanning -= PNP_SRES_LEN(tag); switch (PNP_SRES_NUM(tag)) { case PNP_TAG_LOGICAL_DEVICE: /* * Parse the resources for the previous * logical device (if any). */ if (startres) { pnp_parse_resources(dev, startres, resinfo - startres - 1, ldn); dev = 0; startres = NULL; } /* * A new logical device. Scan for end of * resources. */ bcopy(resinfo, &logical_id, 4); pnp_check_quirks(p->vendor_id, logical_id, ldn, NULL); dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); if (desc) device_set_desc_copy(dev, desc); else device_set_desc_copy(dev, pnp_eisaformat(logical_id)); isa_set_vendorid(dev, p->vendor_id); isa_set_serial(dev, p->serial); isa_set_logicalid(dev, logical_id); isa_set_configattr(dev, ISACFGATTR_CANDISABLE | ISACFGATTR_DYNAMIC); csnldn = malloc(sizeof *csnldn, M_DEVBUF, M_NOWAIT); if (!csnldn) { device_printf(parent, "out of memory\n"); scanning = 0; break; } csnldn->csn = csn; csnldn->ldn = ldn; ISA_SET_CONFIG_CALLBACK(parent, dev, pnp_set_config, csnldn); isa_set_pnp_csn(dev, csn); isa_set_pnp_ldn(dev, ldn); ldn++; startres = resp; break; case PNP_TAG_END: if (!startres) { device_printf(parent, "malformed resources\n"); scanning = 0; break; } pnp_parse_resources(dev, startres, resinfo - startres - 1, ldn); dev = 0; startres = NULL; scanning = 0; break; default: /* Skip this resource */ break; } } return (retval); } /* * Read 'amount' bytes of resources from the card, allocating memory * as needed. If a buffer is already available, it should be passed in * '*resourcesp' and its length in '*spacep'. The number of resource * bytes already in the buffer should be passed in '*lenp'. The memory * allocated will be returned in '*resourcesp' with its size and the * number of bytes of resources in '*spacep' and '*lenp' respectively. * * XXX: Multiple problems here, we forget to free() stuff in one * XXX: error return, and in another case we free (*resourcesp) but * XXX: don't tell the caller. */ static int pnp_read_bytes(int amount, u_char **resourcesp, int *spacep, int *lenp) { u_char *resources = *resourcesp; u_char *newres; int space = *spacep; int len = *lenp; if (space == 0) { space = 1024; resources = malloc(space, M_TEMP, M_NOWAIT); if (!resources) return (ENOMEM); } if (len + amount > space) { int extra = 1024; while (len + amount > space + extra) extra += 1024; newres = malloc(space + extra, M_TEMP, M_NOWAIT); if (!newres) { /* XXX: free resources */ return (ENOMEM); } bcopy(resources, newres, len); free(resources, M_TEMP); resources = newres; space += extra; } if (pnp_get_resource_info(resources + len, amount) != amount) return (EINVAL); len += amount; *resourcesp = resources; *spacep = space; *lenp = len; return (0); } /* * Read all resources from the card, allocating memory as needed. If a * buffer is already available, it should be passed in '*resourcesp' * and its length in '*spacep'. The memory allocated will be returned * in '*resourcesp' with its size and the number of bytes of resources * in '*spacep' and '*lenp' respectively. */ static int pnp_read_resources(u_char **resourcesp, int *spacep, int *lenp) { u_char *resources = *resourcesp; int space = *spacep; int len = 0; int error, done; u_char tag; error = 0; done = 0; while (!done) { error = pnp_read_bytes(1, &resources, &space, &len); if (error) goto out; tag = resources[len-1]; if (PNP_RES_TYPE(tag) == 0) { /* * Small resource, read contents. */ error = pnp_read_bytes(PNP_SRES_LEN(tag), &resources, &space, &len); if (error) goto out; if (PNP_SRES_NUM(tag) == PNP_TAG_END) done = 1; } else { /* * Large resource, read length and contents. */ error = pnp_read_bytes(2, &resources, &space, &len); if (error) goto out; error = pnp_read_bytes(resources[len-2] + (resources[len-1] << 8), &resources, &space, &len); if (error) goto out; } } out: *resourcesp = resources; *spacep = space; *lenp = len; return (error); } /* * Run the isolation protocol. Use pnp_rd_port as the READ_DATA port * value (caller should try multiple READ_DATA locations before giving * up). Upon exiting, all cards are aware that they should use * pnp_rd_port as the READ_DATA port. * * In the first pass, a csn is assigned to each board and pnp_id's * are saved to an array, pnp_devices. In the second pass, each * card is woken up and the device configuration is called. */ static int pnp_isolation_protocol(device_t parent) { int csn; pnp_id id; int found = 0, len; u_char *resources = NULL; int space = 0; int error; /* * Put all cards into the Sleep state so that we can clear * their CSNs. */ pnp_send_initiation_key(); /* * Clear the CSN for all cards. */ pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_RESET_CSN); /* * Move all cards to the Isolation state. */ pnp_write(PNP_WAKE, 0); /* * Tell them where the read point is going to be this time. */ pnp_write(PNP_SET_RD_DATA, pnp_rd_port); for (csn = 1; csn < PNP_MAX_CARDS; csn++) { /* * Start the serial isolation protocol. */ outb(_PNP_ADDRESS, PNP_SERIAL_ISOLATION); DELAY(1000); /* Delay 1 msec */ if (pnp_get_serial(&id)) { /* * We have read the id from a card * successfully. The card which won the * isolation protocol will be in Isolation * mode and all others will be in Sleep. * Program the CSN of the isolated card * (taking it to Config state) and read its * resources, creating devices as we find * logical devices on the card. */ pnp_write(PNP_SET_CSN, csn); if (bootverbose) printf("Reading PnP configuration for %s.\n", pnp_eisaformat(id.vendor_id)); error = pnp_read_resources(&resources, &space, &len); if (error) break; pnp_create_devices(parent, &id, csn, resources, len); found++; } else break; /* * Put this card back to the Sleep state and * simultaneously move all cards which don't have a * CSN yet to Isolation state. */ pnp_write(PNP_WAKE, 0); } /* * Unless we have chosen the wrong read port, all cards will * be in Sleep state. Put them back into WaitForKey for * now. Their resources will be programmed later. */ pnp_write(PNP_CONFIG_CONTROL, PNP_CONFIG_CONTROL_WAIT_FOR_KEY); /* * Cleanup. */ if (resources) free(resources, M_TEMP); return (found); } /* * pnp_identify() * * autoconfiguration of pnp devices. This routine just runs the * isolation protocol over several ports, until one is successful. * * may be called more than once ? * */ static void pnp_identify(driver_t *driver, device_t parent) { int num_pnp_devs; /* Try various READ_DATA ports from 0x203-0x3ff */ for (pnp_rd_port = 0x80; (pnp_rd_port < 0xff); pnp_rd_port += 0x10) { if (bootverbose) printf("pnp_identify: Trying Read_Port at %x\n", (pnp_rd_port << 2) | 0x3); num_pnp_devs = pnp_isolation_protocol(parent); if (num_pnp_devs) break; } if (bootverbose) printf("PNP Identify complete\n"); } static device_method_t pnp_methods[] = { /* Device interface */ DEVMETHOD(device_identify, pnp_identify), { 0, 0 } }; static driver_t pnp_driver = { "pnp", pnp_methods, 1, /* no softc */ }; static devclass_t pnp_devclass; DRIVER_MODULE(pnp, isa, pnp_driver, pnp_devclass, 0, 0); Index: head/sys/isa/vga_isa.c =================================================================== --- head/sys/isa/vga_isa.c (revision 327119) +++ head/sys/isa/vga_isa.c (revision 327120) @@ -1,384 +1,385 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999 Kazutaka YOKOTA * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_vga.h" #include "opt_fb.h" #include "opt_syscons.h" /* should be removed in the future, XXX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ #include #endif #include #include #include #include static void vga_suspend(device_t dev) { vga_softc_t *sc; int nbytes; sc = device_get_softc(dev); /* Save the video state across the suspend. */ if (sc->state_buf != NULL) goto save_palette; nbytes = vidd_save_state(sc->adp, NULL, 0); if (nbytes <= 0) goto save_palette; sc->state_buf = malloc(nbytes, M_TEMP, M_NOWAIT); if (sc->state_buf == NULL) goto save_palette; if (bootverbose) device_printf(dev, "saving %d bytes of video state\n", nbytes); if (vidd_save_state(sc->adp, sc->state_buf, nbytes) != 0) { device_printf(dev, "failed to save state (nbytes=%d)\n", nbytes); free(sc->state_buf, M_TEMP); sc->state_buf = NULL; } save_palette: /* Save the color palette across the suspend. */ if (sc->pal_buf != NULL) return; sc->pal_buf = malloc(256 * 3, M_TEMP, M_NOWAIT); if (sc->pal_buf == NULL) return; if (bootverbose) device_printf(dev, "saving color palette\n"); if (vidd_save_palette(sc->adp, sc->pal_buf) != 0) { device_printf(dev, "failed to save palette\n"); free(sc->pal_buf, M_TEMP); sc->pal_buf = NULL; } } static void vga_resume(device_t dev) { vga_softc_t *sc; sc = device_get_softc(dev); if (sc->state_buf != NULL) { if (vidd_load_state(sc->adp, sc->state_buf) != 0) device_printf(dev, "failed to reload state\n"); free(sc->state_buf, M_TEMP); sc->state_buf = NULL; } if (sc->pal_buf != NULL) { if (vidd_load_palette(sc->adp, sc->pal_buf) != 0) device_printf(dev, "failed to reload palette\n"); free(sc->pal_buf, M_TEMP); sc->pal_buf = NULL; } } #define VGA_SOFTC(unit) \ ((vga_softc_t *)devclass_get_softc(isavga_devclass, unit)) static devclass_t isavga_devclass; #ifdef FB_INSTALL_CDEV static d_open_t isavga_open; static d_close_t isavga_close; static d_read_t isavga_read; static d_write_t isavga_write; static d_ioctl_t isavga_ioctl; static d_mmap_t isavga_mmap; static struct cdevsw isavga_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = isavga_open, .d_close = isavga_close, .d_read = isavga_read, .d_write = isavga_write, .d_ioctl = isavga_ioctl, .d_mmap = isavga_mmap, .d_name = VGA_DRIVER_NAME, }; #endif /* FB_INSTALL_CDEV */ static void isavga_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, VGA_DRIVER_NAME, 0); } static int isavga_probe(device_t dev) { video_adapter_t adp; int error; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); error = vga_probe_unit(device_get_unit(dev), &adp, device_get_flags(dev)); if (error == 0) { device_set_desc(dev, "Generic ISA VGA"); bus_set_resource(dev, SYS_RES_IOPORT, 0, adp.va_io_base, adp.va_io_size); bus_set_resource(dev, SYS_RES_MEMORY, 0, adp.va_mem_base, adp.va_mem_size); + isa_set_vendorid(dev, PNP_EISAID("PNP0900")); #if 0 isa_set_port(dev, adp.va_io_base); isa_set_portsize(dev, adp.va_io_size); isa_set_maddr(dev, adp.va_mem_base); isa_set_msize(dev, adp.va_mem_size); #endif } return (error); } static int isavga_attach(device_t dev) { vga_softc_t *sc; int unit; int rid; int error; unit = device_get_unit(dev); sc = device_get_softc(dev); rid = 0; bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE | RF_SHAREABLE); rid = 0; bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); error = vga_attach_unit(unit, sc, device_get_flags(dev)); if (error) return (error); #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(VGA_MKMINOR(unit), sc->adp, &isavga_cdevsw); if (error) return (error); #endif /* FB_INSTALL_CDEV */ if (0 && bootverbose) vidd_diag(sc->adp, bootverbose); #if 0 /* experimental */ device_add_child(dev, "fb", -1); bus_generic_attach(dev); #endif return (0); } static int isavga_suspend(device_t dev) { int error; error = bus_generic_suspend(dev); if (error != 0) return (error); vga_suspend(dev); return (error); } static int isavga_resume(device_t dev) { vga_resume(dev); return (bus_generic_resume(dev)); } #ifdef FB_INSTALL_CDEV static int isavga_open(struct cdev *dev, int flag, int mode, struct thread *td) { return (vga_open(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, td)); } static int isavga_close(struct cdev *dev, int flag, int mode, struct thread *td) { return (vga_close(dev, VGA_SOFTC(VGA_UNIT(dev)), flag, mode, td)); } static int isavga_read(struct cdev *dev, struct uio *uio, int flag) { return (vga_read(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag)); } static int isavga_write(struct cdev *dev, struct uio *uio, int flag) { return (vga_write(dev, VGA_SOFTC(VGA_UNIT(dev)), uio, flag)); } static int isavga_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { return (vga_ioctl(dev, VGA_SOFTC(VGA_UNIT(dev)), cmd, arg, flag, td)); } static int isavga_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { return (vga_mmap(dev, VGA_SOFTC(VGA_UNIT(dev)), offset, paddr, prot, memattr)); } #endif /* FB_INSTALL_CDEV */ static device_method_t isavga_methods[] = { DEVMETHOD(device_identify, isavga_identify), DEVMETHOD(device_probe, isavga_probe), DEVMETHOD(device_attach, isavga_attach), DEVMETHOD(device_suspend, isavga_suspend), DEVMETHOD(device_resume, isavga_resume), DEVMETHOD_END }; static driver_t isavga_driver = { VGA_DRIVER_NAME, isavga_methods, sizeof(vga_softc_t), }; DRIVER_MODULE(vga, isa, isavga_driver, isavga_devclass, 0, 0); static devclass_t vgapm_devclass; static void vgapm_identify(driver_t *driver, device_t parent) { if (device_get_flags(parent) != 0) device_add_child(parent, "vgapm", 0); } static int vgapm_probe(device_t dev) { device_set_desc(dev, "VGA suspend/resume"); device_quiet(dev); return (BUS_PROBE_DEFAULT); } static int vgapm_attach(device_t dev) { bus_generic_probe(dev); bus_generic_attach(dev); return (0); } static int vgapm_suspend(device_t dev) { device_t vga_dev; int error; error = bus_generic_suspend(dev); if (error != 0) return (error); vga_dev = devclass_get_device(isavga_devclass, 0); if (vga_dev == NULL) return (0); vga_suspend(vga_dev); return (0); } static int vgapm_resume(device_t dev) { device_t vga_dev; vga_dev = devclass_get_device(isavga_devclass, 0); if (vga_dev != NULL) vga_resume(vga_dev); return (bus_generic_resume(dev)); } static device_method_t vgapm_methods[] = { DEVMETHOD(device_identify, vgapm_identify), DEVMETHOD(device_probe, vgapm_probe), DEVMETHOD(device_attach, vgapm_attach), DEVMETHOD(device_suspend, vgapm_suspend), DEVMETHOD(device_resume, vgapm_resume), { 0, 0 } }; static driver_t vgapm_driver = { "vgapm", vgapm_methods, 0 }; DRIVER_MODULE(vgapm, vgapci, vgapm_driver, vgapm_devclass, 0, 0); Index: head/sys/x86/isa/orm.c =================================================================== --- head/sys/x86/isa/orm.c (revision 327119) +++ head/sys/x86/isa/orm.c (revision 327120) @@ -1,190 +1,191 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Nikolai Saoukh * 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. */ #include __FBSDID("$FreeBSD$"); /* * Driver to take care of holes in ISA I/O memory occupied * by option rom(s) */ #include #include #include #include #include #include #include #include #include #include #include #define IOMEM_START 0x0a0000 #define IOMEM_STEP 0x000800 #define IOMEM_END 0x100000 #define ORM_ID 0x00004d3e static struct isa_pnp_id orm_ids[] = { { ORM_ID, NULL }, /* ORM0000 */ { 0, NULL }, }; #define MAX_ROMS 32 struct orm_softc { int rnum; int rid[MAX_ROMS]; struct resource *res[MAX_ROMS]; }; static int orm_probe(device_t dev) { return (ISA_PNP_PROBE(device_get_parent(dev), dev, orm_ids)); } static int orm_attach(device_t dev) { return (0); } static void orm_identify(driver_t* driver, device_t parent) { bus_space_handle_t bh; bus_space_tag_t bt; device_t child; u_int32_t chunk = IOMEM_START; struct resource *res; int rid; u_int32_t rom_size; struct orm_softc *sc; u_int8_t buf[3]; if (resource_disabled("orm", 0)) return; child = BUS_ADD_CHILD(parent, ISA_ORDER_SENSITIVE, "orm", -1); device_set_driver(child, driver); isa_set_logicalid(child, ORM_ID); isa_set_vendorid(child, ORM_ID); sc = device_get_softc(child); sc->rnum = 0; while (sc->rnum < MAX_ROMS && chunk < IOMEM_END) { bus_set_resource(child, SYS_RES_MEMORY, sc->rnum, chunk, IOMEM_STEP); rid = sc->rnum; res = bus_alloc_resource_any(child, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (res == NULL) { bus_delete_resource(child, SYS_RES_MEMORY, sc->rnum); chunk += IOMEM_STEP; continue; } bt = rman_get_bustag(res); bh = rman_get_bushandle(res); bus_space_read_region_1(bt, bh, 0, buf, sizeof(buf)); /* * We need to release and delete the resource since we're * changing its size, or the rom isn't there. There * is a checksum field in the ROM to prevent false * positives. However, some common hardware (IBM thinkpads) * neglects to put a valid checksum in the ROM, so we do * not double check the checksum here. On the ISA bus * areas that have no hardware read back as 0xff, so the * tests to see if we have 0x55 followed by 0xaa are * generally sufficient. */ bus_release_resource(child, SYS_RES_MEMORY, rid, res); bus_delete_resource(child, SYS_RES_MEMORY, sc->rnum); if (buf[0] != 0x55 || buf[1] != 0xAA || (buf[2] & 0x03) != 0) { chunk += IOMEM_STEP; continue; } rom_size = buf[2] << 9; bus_set_resource(child, SYS_RES_MEMORY, sc->rnum, chunk, rom_size); rid = sc->rnum; res = bus_alloc_resource_any(child, SYS_RES_MEMORY, &rid, 0); if (res == NULL) { bus_delete_resource(child, SYS_RES_MEMORY, sc->rnum); chunk += IOMEM_STEP; continue; } sc->rid[sc->rnum] = rid; sc->res[sc->rnum] = res; sc->rnum++; chunk += rom_size; } if (sc->rnum == 0) device_delete_child(parent, child); else if (sc->rnum == 1) device_set_desc(child, "ISA Option ROM"); else device_set_desc(child, "ISA Option ROMs"); + isa_set_vendorid(child, PNP_EISAID("PNP0C80")); } static int orm_detach(device_t dev) { int i; struct orm_softc *sc = device_get_softc(dev); for (i = 0; i < sc->rnum; i++) bus_release_resource(dev, SYS_RES_MEMORY, sc->rid[i], sc->res[i]); return (0); } static device_method_t orm_methods[] = { /* Device interface */ DEVMETHOD(device_identify, orm_identify), DEVMETHOD(device_probe, orm_probe), DEVMETHOD(device_attach, orm_attach), DEVMETHOD(device_detach, orm_detach), { 0, 0 } }; static driver_t orm_driver = { "orm", orm_methods, sizeof (struct orm_softc) }; static devclass_t orm_devclass; DRIVER_MODULE(orm, isa, orm_driver, orm_devclass, 0, 0);