if_ndis_pci.c revision 257176
1/*-
2 * Copyright (c) 2003
3 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/dev/if_ndis/if_ndis_pci.c 257176 2013-10-26 17:58:36Z glebius $");
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/kernel.h>
39#include <sys/malloc.h>
40#include <sys/module.h>
41#include <sys/socket.h>
42#include <sys/queue.h>
43#include <sys/sysctl.h>
44
45#include <net/if.h>
46#include <net/if_var.h>
47#include <net/if_arp.h>
48#include <net/if_media.h>
49
50#include <machine/bus.h>
51#include <machine/resource.h>
52#include <sys/bus.h>
53#include <sys/rman.h>
54
55#include <net80211/ieee80211_var.h>
56
57#include <dev/pci/pcireg.h>
58#include <dev/pci/pcivar.h>
59#include <dev/usb/usb.h>
60#include <dev/usb/usbdi.h>
61
62#include <compat/ndis/pe_var.h>
63#include <compat/ndis/cfg_var.h>
64#include <compat/ndis/resource_var.h>
65#include <compat/ndis/ntoskrnl_var.h>
66#include <compat/ndis/ndis_var.h>
67#include <dev/if_ndis/if_ndisvar.h>
68
69MODULE_DEPEND(ndis, pci, 1, 1, 1);
70
71static int ndis_probe_pci	(device_t);
72static int ndis_attach_pci	(device_t);
73static struct resource_list *ndis_get_resource_list
74				(device_t, device_t);
75static int ndis_devcompare	(interface_type,
76				 struct ndis_pci_type *, device_t);
77extern int ndisdrv_modevent	(module_t, int, void *);
78extern int ndis_attach		(device_t);
79extern int ndis_shutdown	(device_t);
80extern int ndis_detach		(device_t);
81extern int ndis_suspend		(device_t);
82extern int ndis_resume		(device_t);
83
84static device_method_t ndis_methods[] = {
85	/* Device interface */
86	DEVMETHOD(device_probe,		ndis_probe_pci),
87	DEVMETHOD(device_attach,	ndis_attach_pci),
88	DEVMETHOD(device_detach,	ndis_detach),
89	DEVMETHOD(device_shutdown,	ndis_shutdown),
90	DEVMETHOD(device_suspend,	ndis_suspend),
91	DEVMETHOD(device_resume,	ndis_resume),
92
93	/* Bus interface */
94	DEVMETHOD(bus_get_resource_list, ndis_get_resource_list),
95
96	{ 0, 0 }
97};
98
99static driver_t ndis_driver = {
100	"ndis",
101	ndis_methods,
102	sizeof(struct ndis_softc)
103};
104
105static devclass_t ndis_devclass;
106
107DRIVER_MODULE(ndis, pci, ndis_driver, ndis_devclass, ndisdrv_modevent, 0);
108
109static int
110ndis_devcompare(bustype, t, dev)
111	interface_type		bustype;
112	struct ndis_pci_type	*t;
113	device_t		dev;
114{
115	uint16_t		vid, did;
116	uint32_t		subsys;
117
118	if (bustype != PCIBus)
119		return(FALSE);
120
121	vid = pci_get_vendor(dev);
122	did = pci_get_device(dev);
123	subsys = pci_get_subdevice(dev);
124	subsys = (subsys << 16) | pci_get_subvendor(dev);
125
126	while(t->ndis_name != NULL) {
127		if ((t->ndis_vid == vid) && (t->ndis_did == did) &&
128		    (t->ndis_subsys == subsys || t->ndis_subsys == 0)) {
129			device_set_desc(dev, t->ndis_name);
130			return(TRUE);
131		}
132		t++;
133	}
134
135	return(FALSE);
136}
137
138/*
139 * Probe for an NDIS device. Check the PCI vendor and device
140 * IDs against our list and return a device name if we find a match.
141 */
142static int
143ndis_probe_pci(dev)
144	device_t		dev;
145{
146	driver_object		*drv;
147	struct drvdb_ent	*db;
148
149	drv = windrv_lookup(0, "PCI Bus");
150
151	if (drv == NULL)
152		return(ENXIO);
153
154	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
155
156	if (db != NULL) {
157		/* Create PDO for this device instance */
158		windrv_create_pdo(drv, dev);
159		return(0);
160	}
161
162	return(ENXIO);
163}
164
165/*
166 * Attach the interface. Allocate softc structures, do ifmedia
167 * setup and ethernet/BPF attach.
168 */
169static int
170ndis_attach_pci(dev)
171	device_t		dev;
172{
173	struct ndis_softc	*sc;
174	int			unit, error = 0, rid;
175	struct ndis_pci_type	*t;
176	int			devidx = 0, defidx = 0;
177	struct resource_list	*rl;
178	struct resource_list_entry	*rle;
179	struct drvdb_ent	*db;
180	uint16_t		vid, did;
181	uint32_t		subsys;
182
183	sc = device_get_softc(dev);
184	unit = device_get_unit(dev);
185	sc->ndis_dev = dev;
186
187	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
188	if (db == NULL)
189		return (ENXIO);
190	sc->ndis_dobj = db->windrv_object;
191	sc->ndis_regvals = db->windrv_regvals;
192
193	/*
194	 * Map control/status registers.
195	 */
196
197	pci_enable_busmaster(dev);
198
199	rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
200	if (rl != NULL) {
201		STAILQ_FOREACH(rle, rl, link) {
202			switch (rle->type) {
203			case SYS_RES_IOPORT:
204				sc->ndis_io_rid = rle->rid;
205				sc->ndis_res_io = bus_alloc_resource_any(dev,
206				    SYS_RES_IOPORT, &sc->ndis_io_rid,
207				    RF_ACTIVE);
208				if (sc->ndis_res_io == NULL) {
209					device_printf(dev,
210					    "couldn't map iospace\n");
211					error = ENXIO;
212					goto fail;
213				}
214				break;
215			case SYS_RES_MEMORY:
216				if (sc->ndis_res_altmem != NULL &&
217				    sc->ndis_res_mem != NULL) {
218					device_printf(dev,
219					    "too many memory resources\n");
220					error = ENXIO;
221					goto fail;
222				}
223				if (sc->ndis_res_mem) {
224					sc->ndis_altmem_rid = rle->rid;
225					sc->ndis_res_altmem =
226					    bus_alloc_resource_any(dev,
227					        SYS_RES_MEMORY,
228						&sc->ndis_altmem_rid,
229						RF_ACTIVE);
230					if (sc->ndis_res_altmem == NULL) {
231						device_printf(dev,
232						    "couldn't map alt "
233						    "memory\n");
234						error = ENXIO;
235						goto fail;
236					}
237				} else {
238					sc->ndis_mem_rid = rle->rid;
239					sc->ndis_res_mem =
240					    bus_alloc_resource_any(dev,
241					        SYS_RES_MEMORY,
242						&sc->ndis_mem_rid,
243						RF_ACTIVE);
244					if (sc->ndis_res_mem == NULL) {
245						device_printf(dev,
246						    "couldn't map memory\n");
247						error = ENXIO;
248						goto fail;
249					}
250				}
251				break;
252			case SYS_RES_IRQ:
253				rid = rle->rid;
254				sc->ndis_irq = bus_alloc_resource_any(dev,
255				    SYS_RES_IRQ, &rid,
256				    RF_SHAREABLE | RF_ACTIVE);
257				if (sc->ndis_irq == NULL) {
258					device_printf(dev,
259					    "couldn't map interrupt\n");
260					error = ENXIO;
261					goto fail;
262				}
263				break;
264			default:
265				break;
266			}
267			sc->ndis_rescnt++;
268		}
269	}
270
271	/*
272	 * If the BIOS did not set up an interrupt for this device,
273	 * the resource traversal code above will fail to set up
274	 * an IRQ resource. This is usually a bad thing, so try to
275	 * force the allocation of an interrupt here. If one was
276	 * not assigned to us by the BIOS, bus_alloc_resource()
277	 * should route one for us.
278	 */
279	if (sc->ndis_irq == NULL) {
280		rid = 0;
281		sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
282		    &rid, RF_SHAREABLE | RF_ACTIVE);
283		if (sc->ndis_irq == NULL) {
284			device_printf(dev, "couldn't route interrupt\n");
285			error = ENXIO;
286			goto fail;
287		}
288		sc->ndis_rescnt++;
289	}
290
291	/*
292	 * Allocate the parent bus DMA tag appropriate for PCI.
293	 */
294#define NDIS_NSEG_NEW 32
295	error = bus_dma_tag_create(bus_get_dma_tag(dev),/* PCI parent */
296			1, 0,			/* alignment, boundary */
297			BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
298                        BUS_SPACE_MAXADDR,	/* highaddr */
299			NULL, NULL,		/* filter, filterarg */
300			MAXBSIZE, NDIS_NSEG_NEW,/* maxsize, nsegments */
301			BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
302			BUS_DMA_ALLOCNOW,       /* flags */
303			NULL, NULL,		/* lockfunc, lockarg */
304			&sc->ndis_parent_tag);
305
306        if (error)
307                goto fail;
308
309	sc->ndis_iftype = PCIBus;
310
311	/* Figure out exactly which device we matched. */
312
313	vid = pci_get_vendor(dev);
314	did = pci_get_device(dev);
315	subsys = pci_get_subdevice(dev);
316	subsys = (subsys << 16) | pci_get_subvendor(dev);
317
318	t = db->windrv_devlist;
319
320	while(t->ndis_name != NULL) {
321		if (t->ndis_vid == vid && t->ndis_did == did) {
322			if (t->ndis_subsys == 0)
323				defidx = devidx;
324			else if (t->ndis_subsys == subsys)
325				break;
326		}
327		t++;
328		devidx++;
329	}
330
331	if (t->ndis_name == NULL)
332		sc->ndis_devidx = defidx;
333	else
334		sc->ndis_devidx = devidx;
335
336	error = ndis_attach(dev);
337
338fail:
339	return(error);
340}
341
342static struct resource_list *
343ndis_get_resource_list(dev, child)
344	device_t		dev;
345	device_t		child;
346{
347	struct ndis_softc	*sc;
348
349	sc = device_get_softc(dev);
350	return (BUS_GET_RESOURCE_LIST(device_get_parent(sc->ndis_dev), dev));
351}
352