if_ndis_pci.c revision 232854
11558Srgrimes/*- 21558Srgrimes * Copyright (c) 2003 31558Srgrimes * Bill Paul <wpaul@windriver.com>. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 3. All advertising materials mentioning features or use of this software 141558Srgrimes * must display the following acknowledgement: 151558Srgrimes * This product includes software developed by Bill Paul. 161558Srgrimes * 4. Neither the name of the author nor the names of any co-contributors 171558Srgrimes * may be used to endorse or promote products derived from this software 181558Srgrimes * without specific prior written permission. 191558Srgrimes * 201558Srgrimes * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 241558Srgrimes * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 251558Srgrimes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 261558Srgrimes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 271558Srgrimes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 281558Srgrimes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 291558Srgrimes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 301558Srgrimes * THE POSSIBILITY OF SUCH DAMAGE. 311558Srgrimes */ 321558Srgrimes 331558Srgrimes#include <sys/cdefs.h> 341558Srgrimes__FBSDID("$FreeBSD: head/sys/dev/if_ndis/if_ndis_pci.c 232854 2012-03-12 08:03:51Z scottl $"); 3538039Scharnier 361558Srgrimes#include <sys/param.h> 371558Srgrimes#include <sys/systm.h> 381558Srgrimes#include <sys/kernel.h> 391558Srgrimes#include <sys/module.h> 401558Srgrimes#include <sys/socket.h> 4138039Scharnier#include <sys/queue.h> 421558Srgrimes#include <sys/sysctl.h> 4338039Scharnier 4438039Scharnier#include <net/if.h> 4550476Speter#include <net/if_arp.h> 461558Srgrimes#include <net/if_media.h> 471558Srgrimes 48108375Sdillon#include <machine/bus.h> 49108375Sdillon#include <machine/resource.h> 50108375Sdillon#include <sys/bus.h> 51108375Sdillon#include <sys/rman.h> 52108375Sdillon 5338039Scharnier#include <net80211/ieee80211_var.h> 5438039Scharnier 551558Srgrimes#include <dev/pci/pcireg.h> 561558Srgrimes#include <dev/pci/pcivar.h> 5778732Sdd#include <dev/usb/usb.h> 5838039Scharnier#include <dev/usb/usbdi.h> 596661Sphk 60108375Sdillon#include <compat/ndis/pe_var.h> 611558Srgrimes#include <compat/ndis/cfg_var.h> 62108375Sdillon#include <compat/ndis/resource_var.h> 63108375Sdillon#include <compat/ndis/ntoskrnl_var.h> 64108375Sdillon#include <compat/ndis/ndis_var.h> 656661Sphk#include <dev/if_ndis/if_ndisvar.h> 66108375Sdillon 67108375SdillonMODULE_DEPEND(ndis, pci, 1, 1, 1); 686661Sphk 696661Sphkstatic int ndis_probe_pci (device_t); 701558Srgrimesstatic int ndis_attach_pci (device_t); 7192806Sobrienstatic struct resource_list *ndis_get_resource_list 72108375Sdillon (device_t, device_t); 7392806Sobrienstatic int ndis_devcompare (interface_type, 741558Srgrimes struct ndis_pci_type *, device_t); 75108375Sdillonextern int ndisdrv_modevent (module_t, int, void *); 761558Srgrimesextern int ndis_attach (device_t); 77108375Sdillonextern int ndis_shutdown (device_t); 78108375Sdillonextern int ndis_detach (device_t); 79108375Sdillonextern int ndis_suspend (device_t); 80108375Sdillonextern int ndis_resume (device_t); 81108375Sdillon 82108375Sdillonstatic device_method_t ndis_methods[] = { 83108375Sdillon /* Device interface */ 84108375Sdillon DEVMETHOD(device_probe, ndis_probe_pci), 851558Srgrimes DEVMETHOD(device_attach, ndis_attach_pci), 86108375Sdillon DEVMETHOD(device_detach, ndis_detach), 87108375Sdillon DEVMETHOD(device_shutdown, ndis_shutdown), 88108375Sdillon DEVMETHOD(device_suspend, ndis_suspend), 89108375Sdillon DEVMETHOD(device_resume, ndis_resume), 90108375Sdillon 91108375Sdillon /* Bus interface */ 92108375Sdillon DEVMETHOD(bus_get_resource_list, ndis_get_resource_list), 93108375Sdillon 94108375Sdillon { 0, 0 } 95108375Sdillon}; 961558Srgrimes 97108375Sdillonstatic driver_t ndis_driver = { 98108375Sdillon "ndis", 99108375Sdillon ndis_methods, 100108375Sdillon sizeof(struct ndis_softc) 1011558Srgrimes}; 102108375Sdillon 103108375Sdillonstatic devclass_t ndis_devclass; 104108375Sdillon 105108375SdillonDRIVER_MODULE(ndis, pci, ndis_driver, ndis_devclass, ndisdrv_modevent, 0); 106108375Sdillon 107108375Sdillonstatic int 108108375Sdillonndis_devcompare(bustype, t, dev) 109108375Sdillon interface_type bustype; 110108375Sdillon struct ndis_pci_type *t; 111108375Sdillon device_t dev; 112108375Sdillon{ 113108375Sdillon uint16_t vid, did; 114108375Sdillon uint32_t subsys; 115108375Sdillon 116108375Sdillon if (bustype != PCIBus) 117108375Sdillon return(FALSE); 118108375Sdillon 119108375Sdillon vid = pci_get_vendor(dev); 120108375Sdillon did = pci_get_device(dev); 121108375Sdillon subsys = pci_get_subdevice(dev); 122108375Sdillon subsys = (subsys << 16) | pci_get_subvendor(dev); 123108375Sdillon 124108375Sdillon while(t->ndis_name != NULL) { 125108375Sdillon if ((t->ndis_vid == vid) && (t->ndis_did == did) && 126108375Sdillon (t->ndis_subsys == subsys || t->ndis_subsys == 0)) { 127108375Sdillon device_set_desc(dev, t->ndis_name); 1281558Srgrimes return(TRUE); 1291558Srgrimes } 130108375Sdillon t++; 1311558Srgrimes } 132108375Sdillon 1331558Srgrimes return(FALSE); 1341558Srgrimes} 1351558Srgrimes 136108375Sdillon/* 137108375Sdillon * Probe for an NDIS device. Check the PCI vendor and device 138108375Sdillon * IDs against our list and return a device name if we find a match. 139108375Sdillon */ 140108375Sdillonstatic int 141108375Sdillonndis_probe_pci(dev) 142108375Sdillon device_t dev; 143108375Sdillon{ 144108375Sdillon driver_object *drv; 145108375Sdillon struct drvdb_ent *db; 146108375Sdillon 147108375Sdillon drv = windrv_lookup(0, "PCI Bus"); 148108375Sdillon 149108375Sdillon if (drv == NULL) 150108375Sdillon return(ENXIO); 151108375Sdillon 152108375Sdillon db = windrv_match((matchfuncptr)ndis_devcompare, dev); 153108375Sdillon 154108375Sdillon if (db != NULL) { 155108375Sdillon /* Create PDO for this device instance */ 1561558Srgrimes windrv_create_pdo(drv, dev); 157108375Sdillon return(0); 158107913Sdillon } 159108375Sdillon 160108375Sdillon return(ENXIO); 161108375Sdillon} 1621558Srgrimes 163108375Sdillon/* 164108375Sdillon * Attach the interface. Allocate softc structures, do ifmedia 165108375Sdillon * setup and ethernet/BPF attach. 166108375Sdillon */ 167108375Sdillonstatic int 168108375Sdillonndis_attach_pci(dev) 1691558Srgrimes device_t dev; 1701558Srgrimes{ 1711558Srgrimes struct ndis_softc *sc; 172108375Sdillon int unit, error = 0, rid; 173108375Sdillon struct ndis_pci_type *t; 1741558Srgrimes int devidx = 0, defidx = 0; 175108375Sdillon struct resource_list *rl; 1761558Srgrimes struct resource_list_entry *rle; 1771558Srgrimes struct drvdb_ent *db; 1781558Srgrimes uint16_t vid, did; 17926740Scharnier uint32_t subsys; 1801558Srgrimes 1811558Srgrimes sc = device_get_softc(dev); 18226740Scharnier unit = device_get_unit(dev); 1831558Srgrimes sc->ndis_dev = dev; 1841558Srgrimes 1851558Srgrimes db = windrv_match((matchfuncptr)ndis_devcompare, dev); 1861558Srgrimes if (db == NULL) 1871558Srgrimes return (ENXIO); 1881558Srgrimes sc->ndis_dobj = db->windrv_object; 1891558Srgrimes sc->ndis_regvals = db->windrv_regvals; 19026740Scharnier 191108375Sdillon /* 1921558Srgrimes * Map control/status registers. 193108375Sdillon */ 194108375Sdillon 195108375Sdillon pci_enable_busmaster(dev); 196108375Sdillon 197108375Sdillon rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); 198108375Sdillon if (rl != NULL) { 199108375Sdillon STAILQ_FOREACH(rle, rl, link) { 200108375Sdillon switch (rle->type) { 201108375Sdillon case SYS_RES_IOPORT: 202108375Sdillon sc->ndis_io_rid = rle->rid; 203108375Sdillon sc->ndis_res_io = bus_alloc_resource_any(dev, 204108375Sdillon SYS_RES_IOPORT, &sc->ndis_io_rid, 2051558Srgrimes RF_ACTIVE); 2061558Srgrimes if (sc->ndis_res_io == NULL) { 207107913Sdillon device_printf(dev, 208108375Sdillon "couldn't map iospace\n"); 209108375Sdillon error = ENXIO; 210107913Sdillon goto fail; 211108375Sdillon } 212108375Sdillon break; 213108425Smike case SYS_RES_MEMORY: 214108375Sdillon if (sc->ndis_res_altmem != NULL && 215108375Sdillon sc->ndis_res_mem != NULL) { 216108375Sdillon device_printf(dev, 217108375Sdillon "too many memory resources\n"); 218108375Sdillon error = ENXIO; 219108375Sdillon goto fail; 220108375Sdillon } 221108375Sdillon if (sc->ndis_res_mem) { 222108375Sdillon sc->ndis_altmem_rid = rle->rid; 223108375Sdillon sc->ndis_res_altmem = 224108375Sdillon bus_alloc_resource_any(dev, 225108375Sdillon SYS_RES_MEMORY, 226108375Sdillon &sc->ndis_altmem_rid, 227108375Sdillon RF_ACTIVE); 228108375Sdillon if (sc->ndis_res_altmem == NULL) { 229108375Sdillon device_printf(dev, 230108375Sdillon "couldn't map alt " 231108459Smike "memory\n"); 232108375Sdillon error = ENXIO; 233108375Sdillon goto fail; 234108375Sdillon } 235108375Sdillon } else { 236108375Sdillon sc->ndis_mem_rid = rle->rid; 237108375Sdillon sc->ndis_res_mem = 238108375Sdillon bus_alloc_resource_any(dev, 239108375Sdillon SYS_RES_MEMORY, 240108375Sdillon &sc->ndis_mem_rid, 241108375Sdillon RF_ACTIVE); 242108375Sdillon if (sc->ndis_res_mem == NULL) { 243108375Sdillon device_printf(dev, 244108375Sdillon "couldn't map memory\n"); 245108375Sdillon error = ENXIO; 246108375Sdillon goto fail; 247108375Sdillon } 248108375Sdillon } 249108375Sdillon break; 250108375Sdillon case SYS_RES_IRQ: 251108375Sdillon rid = rle->rid; 252108375Sdillon sc->ndis_irq = bus_alloc_resource_any(dev, 253108375Sdillon SYS_RES_IRQ, &rid, 254108375Sdillon RF_SHAREABLE | RF_ACTIVE); 255108375Sdillon if (sc->ndis_irq == NULL) { 256108375Sdillon device_printf(dev, 257108375Sdillon "couldn't map interrupt\n"); 258108375Sdillon error = ENXIO; 259108375Sdillon goto fail; 260108375Sdillon } 261108375Sdillon break; 262108375Sdillon default: 263108375Sdillon break; 264108375Sdillon } 265108375Sdillon sc->ndis_rescnt++; 266108375Sdillon } 267108375Sdillon } 268108375Sdillon 269108375Sdillon /* 270108375Sdillon * If the BIOS did not set up an interrupt for this device, 271108375Sdillon * the resource traversal code above will fail to set up 272108375Sdillon * an IRQ resource. This is usually a bad thing, so try to 273108375Sdillon * force the allocation of an interrupt here. If one was 274108375Sdillon * not assigned to us by the BIOS, bus_alloc_resource() 275108375Sdillon * should route one for us. 276107913Sdillon */ 277 if (sc->ndis_irq == NULL) { 278 rid = 0; 279 sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 280 &rid, RF_SHAREABLE | RF_ACTIVE); 281 if (sc->ndis_irq == NULL) { 282 device_printf(dev, "couldn't route interrupt\n"); 283 error = ENXIO; 284 goto fail; 285 } 286 sc->ndis_rescnt++; 287 } 288 289 /* 290 * Allocate the parent bus DMA tag appropriate for PCI. 291 */ 292#define NDIS_NSEG_NEW 32 293 error = bus_dma_tag_create(bus_get_dma_tag(dev),/* PCI parent */ 294 1, 0, /* alignment, boundary */ 295 BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ 296 BUS_SPACE_MAXADDR, /* highaddr */ 297 NULL, NULL, /* filter, filterarg */ 298 MAXBSIZE, NDIS_NSEG_NEW,/* maxsize, nsegments */ 299 BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 300 BUS_DMA_ALLOCNOW, /* flags */ 301 NULL, NULL, /* lockfunc, lockarg */ 302 &sc->ndis_parent_tag); 303 304 if (error) 305 goto fail; 306 307 sc->ndis_iftype = PCIBus; 308 309 /* Figure out exactly which device we matched. */ 310 311 vid = pci_get_vendor(dev); 312 did = pci_get_device(dev); 313 subsys = pci_get_subdevice(dev); 314 subsys = (subsys << 16) | pci_get_subvendor(dev); 315 316 t = db->windrv_devlist; 317 318 while(t->ndis_name != NULL) { 319 if (t->ndis_vid == vid && t->ndis_did == did) { 320 if (t->ndis_subsys == 0) 321 defidx = devidx; 322 else if (t->ndis_subsys == subsys) 323 break; 324 } 325 t++; 326 devidx++; 327 } 328 329 if (t->ndis_name == NULL) 330 sc->ndis_devidx = defidx; 331 else 332 sc->ndis_devidx = devidx; 333 334 error = ndis_attach(dev); 335 336fail: 337 return(error); 338} 339 340static struct resource_list * 341ndis_get_resource_list(dev, child) 342 device_t dev; 343 device_t child; 344{ 345 struct ndis_softc *sc; 346 347 sc = device_get_softc(dev); 348 return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev)); 349} 350