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