/*- * Copyright (c) 1997, 1998, 1999 * Bill Paul . 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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. * * $FreeBSD$ */ /* * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int wi_pci_probe(device_t); static int wi_pci_attach(device_t); static int wi_pci_suspend(device_t); static int wi_pci_resume(device_t); static device_method_t wi_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wi_pci_probe), DEVMETHOD(device_attach, wi_pci_attach), DEVMETHOD(device_detach, wi_detach), DEVMETHOD(device_shutdown, wi_shutdown), DEVMETHOD(device_suspend, wi_pci_suspend), DEVMETHOD(device_resume, wi_pci_resume), { 0, 0 } }; static driver_t wi_pci_driver = { "wi", wi_pci_methods, sizeof(struct wi_softc) }; static struct { unsigned int vendor,device; int bus_type; char *desc; } pci_ids[] = { /* Sorted by description */ {0x10b7, 0x7770, WI_BUS_PCI_PLX, "3Com Airconnect"}, {0x16ab, 0x1101, WI_BUS_PCI_PLX, "GLPRISM2 WaveLAN"}, {0x1260, 0x3872, WI_BUS_PCI_NATIVE, "Intersil Prism3"}, {0x1260, 0x3873, WI_BUS_PCI_NATIVE, "Intersil Prism2.5"}, {0x16ab, 0x1102, WI_BUS_PCI_PLX, "Linksys WDT11"}, {0x1385, 0x4100, WI_BUS_PCI_PLX, "Netgear MA301"}, {0x1638, 0x1100, WI_BUS_PCI_PLX, "PRISM2STA WaveLAN"}, {0x111a, 0x1023, WI_BUS_PCI_PLX, "Siemens SpeedStream"}, {0x10b5, 0x9050, WI_BUS_PCI_PLX, "SMC 2602W"}, {0x16ec, 0x3685, WI_BUS_PCI_PLX, "US Robotics 2415"}, {0x4033, 0x7001, WI_BUS_PCI_PLX, "Addtron AWA-100 PCI"}, {0, 0, 0, NULL} }; DRIVER_MODULE(wi, pci, wi_pci_driver, wi_devclass, 0, 0); MODULE_DEPEND(wi, pci, 1, 1, 1); MODULE_DEPEND(wi, wlan, 1, 1, 1); static int wi_pci_probe(dev) device_t dev; { struct wi_softc *sc; int i; sc = device_get_softc(dev); for(i=0; pci_ids[i].vendor != 0; i++) { if ((pci_get_vendor(dev) == pci_ids[i].vendor) && (pci_get_device(dev) == pci_ids[i].device)) { sc->wi_bus_type = pci_ids[i].bus_type; device_set_desc(dev, pci_ids[i].desc); return (BUS_PROBE_DEFAULT); } } return(ENXIO); } static int wi_pci_attach(device_t dev) { struct wi_softc *sc; u_int32_t command, wanted; u_int16_t reg; int error; int timeout; sc = device_get_softc(dev); command = pci_read_config(dev, PCIR_COMMAND, 4); wanted = PCIM_CMD_PORTEN|PCIM_CMD_MEMEN; command |= wanted; pci_write_config(dev, PCIR_COMMAND, command, 4); command = pci_read_config(dev, PCIR_COMMAND, 4); if ((command & wanted) != wanted) { device_printf(dev, "wi_pci_attach() failed to enable pci!\n"); return (ENXIO); } if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) { error = wi_alloc(dev, WI_PCI_IORES); if (error) return (error); /* Make sure interrupts are disabled. */ CSR_WRITE_2(sc, WI_INT_EN, 0); CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); /* We have to do a magic PLX poke to enable interrupts */ sc->local_rid = WI_PCI_LOCALRES; sc->local = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->local_rid, RF_ACTIVE); sc->wi_localtag = rman_get_bustag(sc->local); sc->wi_localhandle = rman_get_bushandle(sc->local); command = bus_space_read_4(sc->wi_localtag, sc->wi_localhandle, WI_LOCAL_INTCSR); command |= WI_LOCAL_INTEN; bus_space_write_4(sc->wi_localtag, sc->wi_localhandle, WI_LOCAL_INTCSR, command); bus_release_resource(dev, SYS_RES_IOPORT, sc->local_rid, sc->local); sc->local = NULL; sc->mem_rid = WI_PCI_MEMRES; sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "couldn't allocate memory\n"); wi_free(dev); return (ENXIO); } sc->wi_bmemtag = rman_get_bustag(sc->mem); sc->wi_bmemhandle = rman_get_bushandle(sc->mem); /* * From Linux driver: * Write COR to enable PC card * This is a subset of the protocol that the pccard bus code * would do. */ CSM_WRITE_1(sc, WI_COR_OFFSET, WI_COR_VALUE); reg = CSM_READ_1(sc, WI_COR_OFFSET); if (reg != WI_COR_VALUE) { device_printf(dev, "CSM_READ_1(WI_COR_OFFSET) " "wanted %d, got %d\n", WI_COR_VALUE, reg); wi_free(dev); return (ENXIO); } } else { error = wi_alloc(dev, WI_PCI_LMEMRES); if (error) return (error); CSR_WRITE_2(sc, WI_HFA384X_PCICOR_OFF, 0x0080); DELAY(250000); CSR_WRITE_2(sc, WI_HFA384X_PCICOR_OFF, 0x0000); DELAY(500000); timeout=2000000; while ((--timeout > 0) && (CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) DELAY(10); if (timeout == 0) { device_printf(dev, "couldn't reset prism2.5 core.\n"); wi_free(dev); return(ENXIO); } } CSR_WRITE_2(sc, WI_HFA384X_SWSUPPORT0_OFF, WI_PRISM2STA_MAGIC); reg = CSR_READ_2(sc, WI_HFA384X_SWSUPPORT0_OFF); if (reg != WI_PRISM2STA_MAGIC) { device_printf(dev, "CSR_READ_2(WI_HFA384X_SWSUPPORT0_OFF) " "wanted %d, got %d\n", WI_PRISM2STA_MAGIC, reg); wi_free(dev); return (ENXIO); } error = wi_attach(dev); if (error != 0) wi_free(dev); return (error); } static int wi_pci_suspend(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; wi_stop(ifp, 1); return (0); } static int wi_pci_resume(device_t dev) { struct wi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) return (0); if (ifp->if_flags & IFF_UP) { ifp->if_init(ifp->if_softc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ifp->if_start(ifp); } return (0); }