Index: head/sys/dev/cardbus/cardbus.c =================================================================== --- head/sys/dev/cardbus/cardbus.c (revision 365212) +++ head/sys/dev/cardbus/cardbus.c (revision 365213) @@ -1,373 +1,372 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000,2001 Jonathan Chen. All rights reserved. * Copyright (c) 2003-2008 M. Warner Losh * * 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$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "power_if.h" #include "pcib_if.h" /* sysctl vars */ static SYSCTL_NODE(_hw, OID_AUTO, cardbus, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "CardBus parameters"); int cardbus_debug = 0; SYSCTL_INT(_hw_cardbus, OID_AUTO, debug, CTLFLAG_RWTUN, &cardbus_debug, 0, "CardBus debug"); int cardbus_cis_debug = 0; SYSCTL_INT(_hw_cardbus, OID_AUTO, cis_debug, CTLFLAG_RWTUN, &cardbus_cis_debug, 0, "CardBus CIS debug"); #define DPRINTF(a) if (cardbus_debug) printf a #define DEVPRINTF(x) if (cardbus_debug) device_printf x static int cardbus_attach(device_t cbdev); static int cardbus_attach_card(device_t cbdev); static int cardbus_detach(device_t cbdev); static int cardbus_detach_card(device_t cbdev); static void cardbus_device_setup_regs(pcicfgregs *cfg); static void cardbus_driver_added(device_t cbdev, driver_t *driver); static int cardbus_probe(device_t cbdev); static int cardbus_read_ivar(device_t cbdev, device_t child, int which, uintptr_t *result); /************************************************************************/ /* Probe/Attach */ /************************************************************************/ static int cardbus_probe(device_t cbdev) { device_set_desc(cbdev, "CardBus bus"); return (0); } static int cardbus_attach(device_t cbdev) { struct cardbus_softc *sc; #ifdef PCI_RES_BUS int rid; #endif sc = device_get_softc(cbdev); sc->sc_dev = cbdev; #ifdef PCI_RES_BUS rid = 0; sc->sc_bus = bus_alloc_resource(cbdev, PCI_RES_BUS, &rid, pcib_get_bus(cbdev), pcib_get_bus(cbdev), 1, 0); if (sc->sc_bus == NULL) { device_printf(cbdev, "failed to allocate bus number\n"); return (ENXIO); } #else device_printf(cbdev, "Your bus numbers may be AFU\n"); #endif return (0); } static int cardbus_detach(device_t cbdev) { #ifdef PCI_RES_BUS struct cardbus_softc *sc; #endif cardbus_detach_card(cbdev); #ifdef PCI_RES_BUS sc = device_get_softc(cbdev); device_printf(cbdev, "Freeing up the allocatd bus\n"); (void)bus_release_resource(cbdev, PCI_RES_BUS, 0, sc->sc_bus); #endif return (0); } static int cardbus_suspend(device_t self) { cardbus_detach_card(self); return (0); } static int cardbus_resume(device_t self) { return (0); } /************************************************************************/ /* Attach/Detach card */ /************************************************************************/ static void cardbus_device_setup_regs(pcicfgregs *cfg) { device_t dev = cfg->dev; int i; /* * Some cards power up with garbage in their BARs. This * code clears all that junk out. */ for (i = 0; i < PCIR_MAX_BAR_0; i++) pci_write_config(dev, PCIR_BAR(i), 0, 4); cfg->intline = pci_get_irq(device_get_parent(device_get_parent(dev))); pci_write_config(dev, PCIR_INTLINE, cfg->intline, 1); pci_write_config(dev, PCIR_CACHELNSZ, 0x08, 1); pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); pci_write_config(dev, PCIR_MINGNT, 0x14, 1); pci_write_config(dev, PCIR_MAXLAT, 0x14, 1); } static struct pci_devinfo * cardbus_alloc_devinfo(device_t dev) { struct cardbus_devinfo *dinfo; dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); return (&dinfo->pci); } static int cardbus_attach_card(device_t cbdev) { device_t brdev = device_get_parent(cbdev); device_t child; int bus, domain, slot, func; int cardattached = 0; int cardbusfunchigh = 0; struct cardbus_softc *sc; sc = device_get_softc(cbdev); cardbus_detach_card(cbdev); /* detach existing cards */ POWER_DISABLE_SOCKET(brdev, cbdev); /* Turn the socket off first */ POWER_ENABLE_SOCKET(brdev, cbdev); domain = pcib_get_domain(cbdev); bus = pcib_get_bus(cbdev); slot = 0; mtx_lock(&Giant); /* For each function, set it up and try to attach a driver to it */ for (func = 0; func <= cardbusfunchigh; func++) { struct cardbus_devinfo *dinfo; dinfo = (struct cardbus_devinfo *) pci_read_device(brdev, cbdev, domain, bus, slot, func); if (dinfo == NULL) continue; if (dinfo->pci.cfg.mfdev) cardbusfunchigh = PCI_FUNCMAX; child = device_add_child(cbdev, NULL, -1); if (child == NULL) { DEVPRINTF((cbdev, "Cannot add child!\n")); pci_freecfg((struct pci_devinfo *)dinfo); continue; } dinfo->pci.cfg.dev = child; resource_list_init(&dinfo->pci.resources); device_set_ivars(child, dinfo); cardbus_device_create(sc, dinfo, cbdev, child); if (cardbus_do_cis(cbdev, child) != 0) DEVPRINTF((cbdev, "Warning: Bogus CIS ignored\n")); pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 0); pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci); cardbus_device_setup_regs(&dinfo->pci.cfg); pci_add_resources(cbdev, child, 1, dinfo->mprefetchable); pci_print_verbose(&dinfo->pci); if (device_probe_and_attach(child) == 0) cardattached++; else pci_cfg_save(dinfo->pci.cfg.dev, &dinfo->pci, 1); } mtx_unlock(&Giant); if (cardattached > 0) return (0); /* POWER_DISABLE_SOCKET(brdev, cbdev); */ return (ENOENT); } static void cardbus_child_deleted(device_t cbdev, device_t child) { struct cardbus_devinfo *dinfo = device_get_ivars(child); if (dinfo->pci.cfg.dev != child) device_printf(cbdev, "devinfo dev mismatch\n"); cardbus_device_destroy(dinfo); pci_child_deleted(cbdev, child); } static int cardbus_detach_card(device_t cbdev) { int err = 0; err = bus_generic_detach(cbdev); if (err) return (err); err = device_delete_children(cbdev); if (err) return (err); POWER_DISABLE_SOCKET(device_get_parent(cbdev), cbdev); return (err); } static void cardbus_driver_added(device_t cbdev, driver_t *driver) { int numdevs; device_t *devlist; device_t dev; int i; struct cardbus_devinfo *dinfo; DEVICE_IDENTIFY(driver, cbdev); if (device_get_children(cbdev, &devlist, &numdevs) != 0) return; /* * If there are no drivers attached, but there are children, * then power the card up. */ for (i = 0; i < numdevs; i++) { dev = devlist[i]; if (device_get_state(dev) != DS_NOTPRESENT) break; } if (i > 0 && i == numdevs) POWER_ENABLE_SOCKET(device_get_parent(cbdev), cbdev); for (i = 0; i < numdevs; i++) { dev = devlist[i]; if (device_get_state(dev) != DS_NOTPRESENT) continue; dinfo = device_get_ivars(dev); pci_print_verbose(&dinfo->pci); if (bootverbose) printf("pci%d:%d:%d:%d: reprobing on driver added\n", dinfo->pci.cfg.domain, dinfo->pci.cfg.bus, dinfo->pci.cfg.slot, dinfo->pci.cfg.func); pci_cfg_restore(dinfo->pci.cfg.dev, &dinfo->pci); if (device_probe_and_attach(dev) != 0) pci_cfg_save(dev, &dinfo->pci, 1); } free(devlist, M_TEMP); } /************************************************************************/ /* Other Bus Methods */ /************************************************************************/ static int cardbus_read_ivar(device_t cbdev, device_t child, int which, uintptr_t *result) { struct cardbus_devinfo *dinfo; pcicfgregs *cfg; dinfo = device_get_ivars(child); cfg = &dinfo->pci.cfg; switch (which) { case PCI_IVAR_ETHADDR: /* * The generic accessor doesn't deal with failure, so * we set the return value, then return an error. */ if (dinfo->fepresent & (1 << PCCARD_TPLFE_TYPE_LAN_NID)) { *((uint8_t **) result) = dinfo->funce.lan.nid; break; } *((uint8_t **) result) = NULL; return (EINVAL); default: return (pci_read_ivar(cbdev, child, which, result)); } return 0; } static device_method_t cardbus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cardbus_probe), DEVMETHOD(device_attach, cardbus_attach), DEVMETHOD(device_detach, cardbus_detach), DEVMETHOD(device_suspend, cardbus_suspend), DEVMETHOD(device_resume, cardbus_resume), /* Bus interface */ DEVMETHOD(bus_child_deleted, cardbus_child_deleted), DEVMETHOD(bus_get_dma_tag, bus_generic_get_dma_tag), DEVMETHOD(bus_read_ivar, cardbus_read_ivar), DEVMETHOD(bus_driver_added, cardbus_driver_added), DEVMETHOD(bus_rescan, bus_null_rescan), /* Card Interface */ DEVMETHOD(card_attach_card, cardbus_attach_card), DEVMETHOD(card_detach_card, cardbus_detach_card), /* PCI interface */ DEVMETHOD(pci_alloc_devinfo, cardbus_alloc_devinfo), - {0,0} }; DEFINE_CLASS_1(cardbus, cardbus_driver, cardbus_methods, sizeof(struct cardbus_softc), pci_driver); static devclass_t cardbus_devclass; DRIVER_MODULE(cardbus, cbb, cardbus_driver, cardbus_devclass, 0, 0); MODULE_VERSION(cardbus, 1); Index: head/sys/dev/cardbus/cardbus_cis.c =================================================================== --- head/sys/dev/cardbus/cardbus_cis.c (revision 365212) +++ head/sys/dev/cardbus/cardbus_cis.c (revision 365213) @@ -1,661 +1,660 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000,2001 Jonathan Chen All rights reserved. * Copyright (c) 2005-2008 M. Warner Losh * * * 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$"); /* * CIS Handling for the Cardbus Bus */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int cardbus_cis_debug; #define DPRINTF(a) if (cardbus_cis_debug) printf a #define DEVPRINTF(x) if (cardbus_cis_debug) device_printf x #define CIS_CONFIG_SPACE (struct resource *)~0UL static int decode_tuple_generic(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_linktarget(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_vers_1(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_funcid(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_manfid(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_funce(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_bar(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_unhandled(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int decode_tuple_end(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *); static int cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata); static int cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata); static int cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata); static void cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid, struct resource *res); static struct resource *cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start, int *rid); static int decode_tuple(device_t cbdev, device_t child, int tupleid, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *callbacks, void *); - #define MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC } static char *funcnames[] = { "Multi-Functioned", "Memory", "Serial Port", "Parallel Port", "Fixed Disk", "Video Adaptor", "Network Adaptor", "AIMS", "SCSI", "Security" }; /* * Handler functions for various CIS tuples */ static int decode_tuple_generic(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { int i; if (cardbus_cis_debug) { if (info) printf("TUPLE: %s [%d]:", info->name, len); else printf("TUPLE: Unknown(0x%02x) [%d]:", id, len); for (i = 0; i < len; i++) { if (i % 0x10 == 0 && len > 0x10) printf("\n 0x%02x:", i); printf(" %02x", tupledata[i]); } printf("\n"); } return (0); } static int decode_tuple_linktarget(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { int i; if (cardbus_cis_debug) { printf("TUPLE: %s [%d]:", info->name, len); for (i = 0; i < len; i++) { if (i % 0x10 == 0 && len > 0x10) printf("\n 0x%02x:", i); printf(" %02x", tupledata[i]); } printf("\n"); } if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' || tupledata[2] != 'S') { printf("Invalid data for CIS Link Target!\n"); decode_tuple_generic(cbdev, child, id, len, tupledata, start, off, info, argp); return (EINVAL); } return (0); } static int decode_tuple_vers_1(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { int i; if (cardbus_cis_debug) { printf("Product version: %d.%d\n", tupledata[0], tupledata[1]); printf("Product name: "); for (i = 2; i < len; i++) { if (tupledata[i] == '\0') printf(" | "); else if (tupledata[i] == 0xff) break; else printf("%c", tupledata[i]); } printf("\n"); } return (0); } static int decode_tuple_funcid(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { struct cardbus_devinfo *dinfo = device_get_ivars(child); int numnames = nitems(funcnames); int i; if (cardbus_cis_debug) { printf("Functions: "); for (i = 0; i < len; i++) { if (tupledata[i] < numnames) printf("%s", funcnames[tupledata[i]]); else printf("Unknown(%d)", tupledata[i]); if (i < len - 1) printf(", "); } printf("\n"); } if (len > 0) dinfo->funcid = tupledata[0]; /* use first in list */ return (0); } static int decode_tuple_manfid(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { struct cardbus_devinfo *dinfo = device_get_ivars(child); int i; if (cardbus_cis_debug) { printf("Manufacturer ID: "); for (i = 0; i < len; i++) printf("%02x", tupledata[i]); printf("\n"); } if (len == 5) { dinfo->mfrid = tupledata[1] | (tupledata[2] << 8); dinfo->prodid = tupledata[3] | (tupledata[4] << 8); } return (0); } static int decode_tuple_funce(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { struct cardbus_devinfo *dinfo = device_get_ivars(child); int type, i; if (cardbus_cis_debug) { printf("Function Extension: "); for (i = 0; i < len; i++) printf("%02x", tupledata[i]); printf("\n"); } if (len < 2) /* too short */ return (0); type = tupledata[0]; /* XXX <32 always? */ switch (dinfo->funcid) { case PCCARD_FUNCTION_NETWORK: switch (type) { case PCCARD_TPLFE_TYPE_LAN_NID: if (tupledata[1] > sizeof(dinfo->funce.lan.nid)) { /* ignore, warning? */ return (0); } bcopy(tupledata + 2, dinfo->funce.lan.nid, tupledata[1]); break; } dinfo->fepresent |= 1<mprefetchable |= (1 << PCI_RID2BAR(bar)); /* * The PC Card spec says we're only supposed to honor this * hint when the cardbus bridge is a child of pci0 (the main * bus). The PC Card spec seems to indicate that this should * only be done on x86 based machines, which suggests that on * non-x86 machines the addresses can be anywhere. Since the * hardware can do it on non-x86 machines, it should be able * to do it on x86 machines too. Therefore, we can and should * ignore this hint. Furthermore, the PC Card spec recommends * always allocating memory above 1MB, contradicting the other * part of the PC Card spec, it seems. We make note of it, * but otherwise don't use this information. * * Some Realtek cards have this set in their CIS, but fail * to actually work when mapped this way, and experience * has shown ignoring this big to be a wise choice. * * XXX We should cite chapter and verse for standard refs. */ if (reg & TPL_BAR_REG_BELOW1MB) dinfo->mbelow1mb |= (1 << PCI_RID2BAR(bar)); } return (0); } static int decode_tuple_unhandled(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { /* Make this message suck less XXX */ printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len); return (EINVAL); } static int decode_tuple_end(device_t cbdev, device_t child, int id, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *info, void *argp) { if (cardbus_cis_debug) printf("CIS reading done\n"); return (0); } /* * Functions to read the a tuple from the card */ /* * Read CIS bytes out of the config space. We have to read it 4 bytes at a * time and do the usual mask and shift to return the bytes. The standard * defines the byte order to be little endian. pci_read_config converts it to * host byte order. This is why we have no endian conversion functions: the * shifts wind up being endian neutral. This is also why we avoid the obvious * memcpy optimization. */ static int cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata) { int i, j; uint32_t e; uint32_t loc; loc = start + *off; e = pci_read_config(child, loc & ~0x3, 4); e >>= 8 * (loc & 0x3); *len = 0; for (i = loc, j = -2; j < *len; j++, i++) { if ((i & 0x3) == 0) e = pci_read_config(child, i, 4); if (j == -2) *tupleid = 0xff & e; else if (j == -1) *len = 0xff & e; else tupledata[j] = 0xff & e; e >>= 8; } *off += *len + 2; return (0); } /* * Read the CIS data out of memory. We indirect through the bus space * routines to ensure proper byte ordering conversions when necessary. */ static int cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata) { int ret; *tupleid = bus_read_1(res, start + *off); *len = bus_read_1(res, start + *off + 1); bus_read_region_1(res, *off + start + 2, tupledata, *len); ret = 0; *off += *len + 2; return (ret); } static int cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res, uint32_t start, uint32_t *off, int *tupleid, int *len, uint8_t *tupledata) { if (res == CIS_CONFIG_SPACE) return (cardbus_read_tuple_conf(cbdev, child, start, off, tupleid, len, tupledata)); return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len, tupledata)); } static void cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid, struct resource *res) { if (res != CIS_CONFIG_SPACE) { bus_release_resource(child, SYS_RES_MEMORY, rid, res); bus_delete_resource(child, SYS_RES_MEMORY, rid); } } static struct resource * cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start, int *rid) { struct resource *res; uint32_t space; space = *start & PCIM_CIS_ASI_MASK; switch (space) { case PCIM_CIS_ASI_CONFIG: DEVPRINTF((cbdev, "CIS in PCI config space\n")); /* CIS in PCI config space need no initialization */ return (CIS_CONFIG_SPACE); case PCIM_CIS_ASI_BAR0: case PCIM_CIS_ASI_BAR1: case PCIM_CIS_ASI_BAR2: case PCIM_CIS_ASI_BAR3: case PCIM_CIS_ASI_BAR4: case PCIM_CIS_ASI_BAR5: *rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0); DEVPRINTF((cbdev, "CIS in BAR %#x\n", *rid)); break; case PCIM_CIS_ASI_ROM: *rid = PCIR_BIOS; DEVPRINTF((cbdev, "CIS in option rom\n")); break; default: device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n", space); return (NULL); } /* allocate the memory space to read CIS */ res = bus_alloc_resource_any(child, SYS_RES_MEMORY, rid, rman_make_alignment_flags(4096) | RF_ACTIVE); if (res == NULL) { device_printf(cbdev, "Unable to allocate resource " "to read CIS.\n"); return (NULL); } DEVPRINTF((cbdev, "CIS Mapped to %#jx\n", rman_get_start(res))); /* Flip to the right ROM image if CIS is in ROM */ if (space == PCIM_CIS_ASI_ROM) { uint32_t imagesize; uint32_t imagebase = 0; uint32_t pcidata; uint16_t romsig; int romnum = 0; int imagenum; imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28; for (romnum = 0;; romnum++) { romsig = bus_read_2(res, imagebase + CARDBUS_EXROM_SIGNATURE); if (romsig != 0xaa55) { device_printf(cbdev, "Bad header in rom %d: " "[%x] %04x\n", romnum, imagebase + CARDBUS_EXROM_SIGNATURE, romsig); cardbus_read_tuple_finish(cbdev, child, *rid, res); *rid = 0; return (NULL); } /* * If this was the Option ROM image that we were * looking for, then we are done. */ if (romnum == imagenum) break; /* Find out where the next Option ROM image is */ pcidata = imagebase + bus_read_2(res, imagebase + CARDBUS_EXROM_DATA_PTR); imagesize = bus_read_2(res, pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH); if (imagesize == 0) { /* * XXX some ROMs seem to have this as zero, * can we assume this means 1 block? */ device_printf(cbdev, "Warning, size of Option " "ROM image %d is 0 bytes, assuming 512 " "bytes.\n", romnum); imagesize = 1; } /* Image size is in 512 byte units */ imagesize <<= 9; if ((bus_read_1(res, pcidata + CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) { device_printf(cbdev, "Cannot find CIS in " "Option ROM\n"); cardbus_read_tuple_finish(cbdev, child, *rid, res); *rid = 0; return (NULL); } imagebase += imagesize; } *start = imagebase + (*start & PCIM_CIS_ADDR_MASK); } else { *start = *start & PCIM_CIS_ADDR_MASK; } DEVPRINTF((cbdev, "CIS offset is %#x\n", *start)); return (res); } /* * Dispatch the right handler function per tuple */ static int decode_tuple(device_t cbdev, device_t child, int tupleid, int len, uint8_t *tupledata, uint32_t start, uint32_t *off, struct tuple_callbacks *callbacks, void *argp) { int i; for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) { if (tupleid == callbacks[i].id) return (callbacks[i].func(cbdev, child, tupleid, len, tupledata, start, off, &callbacks[i], argp)); } return (callbacks[i].func(cbdev, child, tupleid, len, tupledata, start, off, NULL, argp)); } int cardbus_parse_cis(device_t cbdev, device_t child, struct tuple_callbacks *callbacks, void *argp) { uint8_t *tupledata; int tupleid = CISTPL_NULL; int len; int expect_linktarget; uint32_t start, off; struct resource *res; int rid; tupledata = malloc(MAXTUPLESIZE, M_DEVBUF, M_WAITOK | M_ZERO); expect_linktarget = TRUE; if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) { DEVPRINTF((cbdev, "Warning: CIS pointer is 0: (no CIS)\n")); free(tupledata, M_DEVBUF); return (0); } DEVPRINTF((cbdev, "CIS pointer is %#x\n", start)); off = 0; res = cardbus_read_tuple_init(cbdev, child, &start, &rid); if (res == NULL) { device_printf(cbdev, "Unable to allocate resources for CIS\n"); free(tupledata, M_DEVBUF); return (ENXIO); } do { if (cardbus_read_tuple(cbdev, child, res, start, &off, &tupleid, &len, tupledata) != 0) { device_printf(cbdev, "Failed to read CIS.\n"); cardbus_read_tuple_finish(cbdev, child, rid, res); free(tupledata, M_DEVBUF); return (ENXIO); } if (expect_linktarget && tupleid != CISTPL_LINKTARGET) { device_printf(cbdev, "Expecting link target, got 0x%x\n", tupleid); cardbus_read_tuple_finish(cbdev, child, rid, res); free(tupledata, M_DEVBUF); return (EINVAL); } expect_linktarget = decode_tuple(cbdev, child, tupleid, len, tupledata, start, &off, callbacks, argp); if (expect_linktarget != 0) { device_printf(cbdev, "Parsing failed with %d\n", expect_linktarget); cardbus_read_tuple_finish(cbdev, child, rid, res); free(tupledata, M_DEVBUF); return (expect_linktarget); } } while (tupleid != CISTPL_END); cardbus_read_tuple_finish(cbdev, child, rid, res); free(tupledata, M_DEVBUF); return (0); } int cardbus_do_cis(device_t cbdev, device_t child) { struct tuple_callbacks init_callbacks[] = { MAKETUPLE(LONGLINK_CB, unhandled), MAKETUPLE(INDIRECT, unhandled), MAKETUPLE(LONGLINK_MFC, unhandled), MAKETUPLE(BAR, bar), MAKETUPLE(LONGLINK_A, unhandled), MAKETUPLE(LONGLINK_C, unhandled), MAKETUPLE(LINKTARGET, linktarget), MAKETUPLE(VERS_1, vers_1), MAKETUPLE(MANFID, manfid), MAKETUPLE(FUNCID, funcid), MAKETUPLE(FUNCE, funce), MAKETUPLE(END, end), MAKETUPLE(GENERIC, generic), }; return (cardbus_parse_cis(cbdev, child, init_callbacks, NULL)); }