if_ndis_pci.c revision 131953
120253Sjoerg/*
220302Sjoerg * Copyright (c) 2003
320302Sjoerg *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
420253Sjoerg *
520253Sjoerg * Redistribution and use in source and binary forms, with or without
620253Sjoerg * modification, are permitted provided that the following conditions
720253Sjoerg * are met:
820253Sjoerg * 1. Redistributions of source code must retain the above copyright
920302Sjoerg *    notice, this list of conditions and the following disclaimer.
1020253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright
1120253Sjoerg *    notice, this list of conditions and the following disclaimer in the
1220253Sjoerg *    documentation and/or other materials provided with the distribution.
1320253Sjoerg * 3. All advertising materials mentioning features or use of this software
1420302Sjoerg *    must display the following acknowledgement:
1520253Sjoerg *	This product includes software developed by Bill Paul.
1620253Sjoerg * 4. Neither the name of the author nor the names of any co-contributors
1720302Sjoerg *    may be used to endorse or promote products derived from this software
1820253Sjoerg *    without specific prior written permission.
1920253Sjoerg *
2020253Sjoerg * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2120253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2220253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2320253Sjoerg * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2420253Sjoerg * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2544229Sdavidn * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2620253Sjoerg * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2720253Sjoerg * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2830259Scharnier * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2930259Scharnier * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3050479Speter * THE POSSIBILITY OF SUCH DAMAGE.
3130259Scharnier */
3230259Scharnier
3330259Scharnier#include <sys/cdefs.h>
3430259Scharnier__FBSDID("$FreeBSD: head/sys/dev/if_ndis/if_ndis_pci.c 131953 2004-07-11 00:19:30Z wpaul $");
3520253Sjoerg
3620253Sjoerg#include <sys/param.h>
3720253Sjoerg#include <sys/systm.h>
3830259Scharnier#include <sys/kernel.h>
3920253Sjoerg#include <sys/module.h>
4020555Sdavidn#include <sys/socket.h>
4120555Sdavidn#include <sys/queue.h>
4220555Sdavidn#include <sys/sysctl.h>
4364918Sgreen
44242349Sbapt#include <net/if.h>
45242349Sbapt#include <net/if_arp.h>
46242349Sbapt#include <net/if_media.h>
4720253Sjoerg
4820253Sjoerg#include <machine/bus.h>
4920253Sjoerg#include <machine/resource.h>
5023318Sache#include <sys/bus.h>
5122394Sdavidn#include <sys/rman.h>
5252512Sdavidn
5324214Sache#include <net80211/ieee80211_var.h>
54284111Sbapt
55284128Sbapt#include <dev/pci/pcireg.h>
56284124Sbapt#include <dev/pci/pcivar.h>
57284133Sbapt
58284118Sbapt#include <compat/ndis/pe_var.h>
5920253Sjoerg#include <compat/ndis/resource_var.h>
6020253Sjoerg#include <compat/ndis/ntoskrnl_var.h>
6120253Sjoerg#include <compat/ndis/ndis_var.h>
6220253Sjoerg#include <compat/ndis/cfg_var.h>
6320253Sjoerg#include <dev/if_ndis/if_ndisvar.h>
6420253Sjoerg
6520253Sjoerg#include "ndis_driver_data.h"
6685145Sache
6720253Sjoerg#ifdef NDIS_PCI_DEV_TABLE
68283961Sbapt
69284118SbaptMODULE_DEPEND(ndis, pci, 1, 1, 1);
70283961SbaptMODULE_DEPEND(ndis, ether, 1, 1, 1);
71283961SbaptMODULE_DEPEND(ndis, wlan, 1, 1, 1);
72284118SbaptMODULE_DEPEND(ndis, ndisapi, 1, 1, 1);
73283961Sbapt
74283961Sbapt/*
75283961Sbapt * Various supported device vendors/types and their names.
76284118Sbapt * These are defined in the ndis_driver_data.h file.
77284118Sbapt */
78283961Sbaptstatic struct ndis_pci_type ndis_devs[] = {
79283961Sbapt#ifdef NDIS_PCI_DEV_TABLE
80284118Sbapt	NDIS_PCI_DEV_TABLE
81283961Sbapt#endif
82283961Sbapt	{ 0, 0, 0, NULL }
83283961Sbapt};
84283961Sbapt
85283961Sbaptstatic int ndis_probe_pci	(device_t);
86283961Sbaptstatic int ndis_attach_pci	(device_t);
87283961Sbaptstatic struct resource_list *ndis_get_resource_list
88283961Sbapt				(device_t, device_t);
89285133Sbaptextern int ndis_attach		(device_t);
90285137Sbaptextern int ndis_shutdown	(device_t);
91285133Sbaptextern int ndis_detach		(device_t);
92285133Sbaptextern int ndis_suspend		(device_t);
93285133Sbaptextern int ndis_resume		(device_t);
94285133Sbapt
95285133Sbaptextern struct mtx_pool *ndis_mtxpool;
96285133Sbapt
97285133Sbaptstatic device_method_t ndis_methods[] = {
98285133Sbapt	/* Device interface */
99285133Sbapt	DEVMETHOD(device_probe,		ndis_probe_pci),
100285133Sbapt	DEVMETHOD(device_attach,	ndis_attach_pci),
101285133Sbapt	DEVMETHOD(device_detach,	ndis_detach),
102285133Sbapt	DEVMETHOD(device_shutdown,	ndis_shutdown),
103285133Sbapt	DEVMETHOD(device_suspend,	ndis_suspend),
104285133Sbapt	DEVMETHOD(device_resume,	ndis_resume),
105285133Sbapt
106285133Sbapt	/* Bus interface */
107285133Sbapt	DEVMETHOD(bus_get_resource_list, ndis_get_resource_list),
108285133Sbapt
109285133Sbapt	{ 0, 0 }
110285137Sbapt};
111285133Sbapt
112285133Sbaptstatic driver_t ndis_driver = {
113285133Sbapt#ifdef NDIS_DEVNAME
114285133Sbapt	NDIS_DEVNAME,
115285133Sbapt#else
116285133Sbapt	"ndis",
117285133Sbapt#endif
118285133Sbapt	ndis_methods,
119285133Sbapt	sizeof(struct ndis_softc)
120285133Sbapt};
121285133Sbapt
122285133Sbaptstatic devclass_t ndis_devclass;
123285133Sbapt
124285133Sbapt#ifdef NDIS_MODNAME
125285133Sbapt#define NDIS_MODNAME_OVERRIDE_PCI(x)					\
126285133Sbapt	DRIVER_MODULE(x, pci, ndis_driver, ndis_devclass, 0, 0)
127285133Sbapt#define NDIS_MODNAME_OVERRIDE_CARDBUS(x)				\
128285133Sbapt	DRIVER_MODULE(x, cardbus, ndis_driver, ndis_devclass, 0, 0)
129285133SbaptNDIS_MODNAME_OVERRIDE_PCI(NDIS_MODNAME);
130285133SbaptNDIS_MODNAME_OVERRIDE_CARDBUS(NDIS_MODNAME);
131285133Sbapt#else
132285133SbaptDRIVER_MODULE(ndis, pci, ndis_driver, ndis_devclass, 0, 0);
133285133SbaptDRIVER_MODULE(ndis, cardbus, ndis_driver, ndis_devclass, 0, 0);
134285133Sbapt#endif
135285133Sbapt
136285133Sbapt/*
137285133Sbapt * Probe for an NDIS device. Check the PCI vendor and device
138285137Sbapt * IDs against our list and return a device name if we find a match.
139285133Sbapt */
140285133Sbaptstatic int
141285133Sbaptndis_probe_pci(dev)
142285133Sbapt	device_t		dev;
143285133Sbapt{
144285133Sbapt	struct ndis_pci_type	*t;
145285133Sbapt
146285133Sbapt	t = ndis_devs;
147285133Sbapt
148285133Sbapt	while(t->ndis_name != NULL) {
149285133Sbapt		if ((pci_get_vendor(dev) == t->ndis_vid) &&
150285133Sbapt		    (pci_get_device(dev) == t->ndis_did) &&
151285395Sbapt		    ((pci_read_config(dev, PCIR_SUBVEND_0, 4) ==
152285395Sbapt		    t->ndis_subsys) || t->ndis_subsys == 0)) {
153285395Sbapt			device_set_desc(dev, t->ndis_name);
154285395Sbapt			return(0);
155285395Sbapt		}
156285395Sbapt		t++;
157285395Sbapt	}
158285395Sbapt
159285395Sbapt	return(ENXIO);
160285395Sbapt}
161285395Sbapt
162285395Sbapt/*
163285395Sbapt * Attach the interface. Allocate softc structures, do ifmedia
164285395Sbapt * setup and ethernet/BPF attach.
16520253Sjoerg */
16620253Sjoergstatic int
16720253Sjoergndis_attach_pci(dev)
16820253Sjoerg	device_t		dev;
16920253Sjoerg{
17020253Sjoerg	struct ndis_softc	*sc;
17120253Sjoerg	int			unit, error = 0, rid;
17220253Sjoerg	struct ndis_pci_type	*t;
17320253Sjoerg	int			devidx = 0, defidx = 0;
17420253Sjoerg	struct resource_list	*rl;
17520253Sjoerg	struct resource_list_entry	*rle;
17620253Sjoerg
17720253Sjoerg	sc = device_get_softc(dev);
17820253Sjoerg	unit = device_get_unit(dev);
17920253Sjoerg	sc->ndis_dev = dev;
18020253Sjoerg
18120253Sjoerg	/*
182124382Siedowse	 * Map control/status registers.
18320253Sjoerg	 */
18420253Sjoerg
18520253Sjoerg	pci_enable_busmaster(dev);
18620253Sjoerg
18720253Sjoerg	rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
18820253Sjoerg	if (rl != NULL) {
18920253Sjoerg		SLIST_FOREACH(rle, rl, link) {
19020253Sjoerg			switch (rle->type) {
19120253Sjoerg			case SYS_RES_IOPORT:
19220253Sjoerg				sc->ndis_io_rid = rle->rid;
19320253Sjoerg				sc->ndis_res_io = bus_alloc_resource(dev,
19420253Sjoerg				    SYS_RES_IOPORT, &sc->ndis_io_rid,
19520253Sjoerg				    0, ~0, 1, RF_ACTIVE);
19620253Sjoerg				if (sc->ndis_res_io == NULL) {
19720253Sjoerg					device_printf(dev,
198284128Sbapt					    "couldn't map iospace\n");
19920253Sjoerg					error = ENXIO;
20052527Sdavidn					goto fail;
20120253Sjoerg				}
20252512Sdavidn				break;
20320253Sjoerg			case SYS_RES_MEMORY:
20420253Sjoerg				if (sc->ndis_res_altmem != NULL &&
20520253Sjoerg				    sc->ndis_res_mem != NULL) {
20620253Sjoerg					device_printf(dev,
207284118Sbapt					    "too many memory resources\n");
20820747Sdavidn					error = ENXIO;
209283961Sbapt					goto fail;
21082868Sdd				}
211167919Sle				if (rle->rid == PCIR_BAR(2)) {
212167919Sle					sc->ndis_altmem_rid = rle->rid;
21320253Sjoerg					sc->ndis_res_altmem =
21420253Sjoerg					    bus_alloc_resource(dev,
21520253Sjoerg					        SYS_RES_MEMORY,
21620253Sjoerg						&sc->ndis_altmem_rid,
21720253Sjoerg						0, ~0, 1, RF_ACTIVE);
21820253Sjoerg					if (sc->ndis_res_altmem == NULL) {
21920253Sjoerg						device_printf(dev,
22020253Sjoerg						    "couldn't map alt "
22120253Sjoerg						    "memory\n");
22220253Sjoerg						error = ENXIO;
22356000Sdavidn						goto fail;
22420253Sjoerg					}
22520253Sjoerg				} else {
22656000Sdavidn					sc->ndis_mem_rid = rle->rid;
22756000Sdavidn					sc->ndis_res_mem =
22856000Sdavidn					    bus_alloc_resource(dev,
22920253Sjoerg					        SYS_RES_MEMORY,
23020253Sjoerg						&sc->ndis_mem_rid,
231284118Sbapt						0, ~0, 1, RF_ACTIVE);
23252512Sdavidn					if (sc->ndis_res_mem == NULL) {
233284149Sbapt						device_printf(dev,
234285396Sbapt						    "couldn't map memory\n");
23520267Sjoerg						error = ENXIO;
23620267Sjoerg						goto fail;
23720253Sjoerg					}
23820253Sjoerg				}
23920253Sjoerg				break;
24020253Sjoerg			case SYS_RES_IRQ:
24120267Sjoerg				rid = rle->rid;
24220253Sjoerg				sc->ndis_irq = bus_alloc_resource(dev,
24321052Sdavidn				    SYS_RES_IRQ, &rid, 0, ~0, 1,
244167919Sle	    			    RF_SHAREABLE | RF_ACTIVE);
245167919Sle				if (sc->ndis_irq == NULL) {
246167919Sle					device_printf(dev,
247167919Sle					    "couldn't map interrupt\n");
248167919Sle					error = ENXIO;
249219408Sjkim					goto fail;
250167919Sle				}
251168044Sle				break;
252167919Sle			default:
25321052Sdavidn				break;
25421052Sdavidn			}
25521052Sdavidn			sc->ndis_rescnt++;
25621052Sdavidn		}
257224535Sdelphij	}
25821052Sdavidn
25921052Sdavidn	/*
26021052Sdavidn	 * If the BIOS did not set up an interrupt for this device,
26121052Sdavidn	 * the resource traversal code above will fail to set up
26221052Sdavidn	 * an IRQ resource. This is usually a bad thing, so try to
26321052Sdavidn	 * force the allocation of an interrupt here. If one was
26430259Scharnier	 * not assigned to us by the BIOS, bus_alloc_resource()
26521052Sdavidn	 * should route one for us.
26621052Sdavidn	 */
26721052Sdavidn	if (sc->ndis_irq == NULL) {
26821052Sdavidn		rid = 0;
26921242Sdavidn		sc->ndis_irq = bus_alloc_resource(dev, SYS_RES_IRQ,
27021242Sdavidn		    &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
27121242Sdavidn		if (sc->ndis_irq == NULL) {
27221242Sdavidn			device_printf(dev, "couldn't route interrupt\n");
27321242Sdavidn			error = ENXIO;
27421242Sdavidn			goto fail;
27521242Sdavidn		}
276282683Sbapt		sc->ndis_rescnt++;
277219408Sjkim	}
27821242Sdavidn
279148584Spjd	/*
280148584Spjd	 * Allocate the parent bus DMA tag appropriate for PCI.
281148584Spjd	 */
282148584Spjd#define NDIS_NSEG_NEW 32
283148584Spjd	error = bus_dma_tag_create(NULL,	/* parent */
28421242Sdavidn			1, 0,			/* alignment, boundary */
28521242Sdavidn			BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
28621242Sdavidn                        BUS_SPACE_MAXADDR,	/* highaddr */
287130633Srobert			NULL, NULL,		/* filter, filterarg */
288130633Srobert			MAXBSIZE, NDIS_NSEG_NEW,/* maxsize, nsegments */
28921242Sdavidn			BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
290252377Skientzle			BUS_DMA_ALLOCNOW,       /* flags */
29121242Sdavidn			NULL, NULL,		/* lockfunc, lockarg */
29221242Sdavidn			&sc->ndis_parent_tag);
293219408Sjkim
29421242Sdavidn        if (error)
29521242Sdavidn                goto fail;
29621242Sdavidn
29730259Scharnier	sc->ndis_iftype = PCIBus;
29821242Sdavidn
29921242Sdavidn	/* Figure out exactly which device we matched. */
30021052Sdavidn
30121242Sdavidn	t = ndis_devs;
302219408Sjkim
30330259Scharnier	while(t->ndis_name != NULL) {
30421052Sdavidn		if ((pci_get_vendor(dev) == t->ndis_vid) &&
30521052Sdavidn		    (pci_get_device(dev) == t->ndis_did)) {
30621052Sdavidn			if (t->ndis_subsys == 0)
30721052Sdavidn				defidx = devidx;
30830259Scharnier			else {
30921052Sdavidn				if (t->ndis_subsys ==
31021052Sdavidn				    pci_read_config(dev, PCIR_SUBVEND_0, 4))
31120253Sjoerg					break;
31220253Sjoerg			}
31320253Sjoerg		}
31421330Sdavidn		t++;
31521330Sdavidn		devidx++;
31621330Sdavidn	}
31720253Sjoerg
31820253Sjoerg	if (ndis_devs[devidx].ndis_name == NULL)
31920253Sjoerg		sc->ndis_devidx = defidx;
32020253Sjoerg	else
32163596Sdavidn		sc->ndis_devidx = devidx;
32263596Sdavidn
32363596Sdavidn	error = ndis_attach(dev);
32463596Sdavidn
32563596Sdavidnfail:
32663596Sdavidn	return(error);
32763596Sdavidn}
32863596Sdavidn
32920253Sjoergstatic struct resource_list *
33020253Sjoergndis_get_resource_list(dev, child)
33120253Sjoerg	device_t		dev;
332284110Sbapt	device_t		child;
33320253Sjoerg{
33420253Sjoerg	struct ndis_softc	*sc;
33552527Sdavidn
33620253Sjoerg	sc = device_get_softc(dev);
33720747Sdavidn	return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev));
33844229Sdavidn}
33961957Sache
34030259Scharnier#endif /* NDIS_PCI_DEV_TABLE */
34120253Sjoerg