if_ndis_pccard.c revision 188939
1226584Sdim/*-
2226584Sdim * Copyright (c) 2003
3226584Sdim *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4226584Sdim *
5226584Sdim * Redistribution and use in source and binary forms, with or without
6226584Sdim * modification, are permitted provided that the following conditions
7226584Sdim * are met:
8226584Sdim * 1. Redistributions of source code must retain the above copyright
9226584Sdim *    notice, this list of conditions and the following disclaimer.
10226584Sdim * 2. Redistributions in binary form must reproduce the above copyright
11226584Sdim *    notice, this list of conditions and the following disclaimer in the
12226584Sdim *    documentation and/or other materials provided with the distribution.
13226584Sdim * 3. All advertising materials mentioning features or use of this software
14226584Sdim *    must display the following acknowledgement:
15252723Sdim *	This product includes software developed by Bill Paul.
16226584Sdim * 4. Neither the name of the author nor the names of any co-contributors
17226584Sdim *    may be used to endorse or promote products derived from this software
18226584Sdim *    without specific prior written permission.
19226584Sdim *
20252723Sdim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21252723Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22226584Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23245431Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24226584Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25226584Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26226584Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27226584Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28226584Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29226584Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30226584Sdim * THE POSSIBILITY OF SUCH DAMAGE.
31226584Sdim */
32226584Sdim
33226584Sdim#include <sys/cdefs.h>
34226584Sdim__FBSDID("$FreeBSD: head/sys/dev/if_ndis/if_ndis_pccard.c 188939 2009-02-23 18:16:17Z thompsa $");
35226584Sdim
36226584Sdim#include <sys/ctype.h>
37226584Sdim#include <sys/param.h>
38245431Sdim#include <sys/systm.h>
39245431Sdim#include <sys/kernel.h>
40226584Sdim#include <sys/module.h>
41226584Sdim#include <sys/socket.h>
42245431Sdim#include <sys/queue.h>
43226584Sdim#include <sys/sysctl.h>
44226584Sdim
45226584Sdim#include <net/if.h>
46226584Sdim#include <net/if_arp.h>
47245431Sdim#include <net/if_media.h>
48226584Sdim
49226584Sdim#include <machine/bus.h>
50226584Sdim#include <machine/resource.h>
51226584Sdim#include <sys/bus.h>
52226584Sdim#include <sys/rman.h>
53226584Sdim
54226584Sdim#include <net80211/ieee80211_var.h>
55226584Sdim
56226584Sdim#include <legacy/dev/usb/usb.h>
57226584Sdim#include <legacy/dev/usb/usbdi.h>
58226584Sdim
59226584Sdim#include <compat/ndis/pe_var.h>
60226584Sdim#include <compat/ndis/cfg_var.h>
61263509Sdim#include <compat/ndis/resource_var.h>
62226584Sdim#include <compat/ndis/ntoskrnl_var.h>
63226584Sdim#include <compat/ndis/ndis_var.h>
64226584Sdim#include <dev/if_ndis/if_ndisvar.h>
65226584Sdim
66226584Sdim#include <dev/pccard/pccardvar.h>
67226584Sdim#include "card_if.h"
68235633Sdim
69226584SdimMODULE_DEPEND(ndis, pccard, 1, 1, 1);
70226584Sdim
71226584Sdimstatic int ndis_probe_pccard	(device_t);
72226584Sdimstatic int ndis_attach_pccard	(device_t);
73226584Sdimstatic struct resource_list *ndis_get_resource_list
74226584Sdim				(device_t, device_t);
75226584Sdimstatic int ndis_devcompare	(interface_type,
76226584Sdim				 struct ndis_pccard_type *, device_t);
77226584Sdimextern int ndisdrv_modevent	(module_t, int, void *);
78226584Sdimextern int ndis_attach		(device_t);
79226584Sdimextern int ndis_shutdown	(device_t);
80226584Sdimextern int ndis_detach		(device_t);
81226584Sdimextern int ndis_suspend		(device_t);
82226584Sdimextern int ndis_resume		(device_t);
83226584Sdim
84226584Sdimextern unsigned char drv_data[];
85226584Sdim
86226584Sdimstatic device_method_t ndis_methods[] = {
87226584Sdim	/* Device interface */
88226584Sdim	DEVMETHOD(device_probe,		ndis_probe_pccard),
89226584Sdim	DEVMETHOD(device_attach,	ndis_attach_pccard),
90226584Sdim	DEVMETHOD(device_detach,	ndis_detach),
91226584Sdim	DEVMETHOD(device_shutdown,	ndis_shutdown),
92226584Sdim	DEVMETHOD(device_suspend,	ndis_suspend),
93226584Sdim	DEVMETHOD(device_resume,	ndis_resume),
94226584Sdim
95226584Sdim	/* Bus interface. */
96226584Sdim
97226584Sdim	/*
98226584Sdim	 * This is an awful kludge, but we need it becase pccard
99226584Sdim	 * does not implement a bus_get_resource_list() method.
100226584Sdim	 */
101226584Sdim
102226584Sdim	DEVMETHOD(bus_get_resource_list, ndis_get_resource_list),
103226584Sdim
104226584Sdim	{ 0, 0 }
105226584Sdim};
106226584Sdim
107226584Sdimstatic driver_t ndis_driver = {
108226584Sdim	"ndis",
109226584Sdim	ndis_methods,
110226584Sdim	sizeof(struct ndis_softc)
111226584Sdim};
112226584Sdim
113226584Sdimstatic devclass_t ndis_devclass;
114226584Sdim
115226584SdimDRIVER_MODULE(ndis, pccard, ndis_driver, ndis_devclass, ndisdrv_modevent, 0);
116226584Sdim
117226584Sdimstatic int
118226584Sdimndis_devcompare(bustype, t, dev)
119226584Sdim	interface_type		bustype;
120226584Sdim	struct ndis_pccard_type	*t;
121226584Sdim	device_t		dev;
122235633Sdim{
123235633Sdim	const char		*prodstr, *vendstr;
124226584Sdim	int			error;
125235633Sdim
126226584Sdim	if (bustype != PCMCIABus)
127226584Sdim		return(FALSE);
128226584Sdim
129226584Sdim	error = pccard_get_product_str(dev, &prodstr);
130226584Sdim	if (error)
131226584Sdim		return(FALSE);
132226584Sdim	error = pccard_get_vendor_str(dev, &vendstr);
133226584Sdim	if (error)
134226584Sdim		return(FALSE);
135226584Sdim
136226584Sdim	while(t->ndis_name != NULL) {
137226584Sdim		if (strcasecmp(vendstr, t->ndis_vid) == 0 &&
138226584Sdim		    strcasecmp(prodstr, t->ndis_did) == 0) {
139226584Sdim			device_set_desc(dev, t->ndis_name);
140226584Sdim			return(TRUE);
141226584Sdim		}
142226584Sdim		t++;
143226584Sdim	}
144226584Sdim
145226584Sdim	return(FALSE);
146226584Sdim}
147226584Sdim
148226584Sdim/*
149226584Sdim * Probe for an NDIS device. Check the PCI vendor and device
150226584Sdim * IDs against our list and return a device name if we find a match.
151226584Sdim */
152226584Sdimstatic int
153226584Sdimndis_probe_pccard(dev)
154226584Sdim	device_t		dev;
155226584Sdim{
156226584Sdim	driver_object		*drv;
157226584Sdim	struct drvdb_ent	*db;
158226584Sdim
159226584Sdim	drv = windrv_lookup(0, "PCCARD Bus");
160226584Sdim	if (drv == NULL)
161226584Sdim		return(ENXIO);
162226584Sdim
163226584Sdim	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
164226584Sdim
165226584Sdim	if (db != NULL) {
166226584Sdim		/* Create PDO for this device instance */
167226584Sdim		windrv_create_pdo(drv, dev);
168226584Sdim		return(0);
169226584Sdim	}
170226584Sdim
171226584Sdim	return(ENXIO);
172235633Sdim}
173226584Sdim
174226584Sdim/*
175226584Sdim * Attach the interface. Allocate softc structures, do ifmedia
176226584Sdim * setup and ethernet/BPF attach.
177226584Sdim */
178226584Sdimstatic int
179226584Sdimndis_attach_pccard(dev)
180226584Sdim	device_t		dev;
181226584Sdim{
182226584Sdim	struct ndis_softc	*sc;
183226584Sdim	int			unit, error = 0, rid;
184226584Sdim	struct ndis_pccard_type	*t;
185226584Sdim	int			devidx = 0;
186226584Sdim	const char		*prodstr, *vendstr;
187226584Sdim	struct drvdb_ent	*db;
188226584Sdim
189226584Sdim	sc = device_get_softc(dev);
190235633Sdim	unit = device_get_unit(dev);
191226584Sdim	sc->ndis_dev = dev;
192226584Sdim
193226584Sdim	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
194226584Sdim	if (db == NULL)
195226584Sdim		return (ENXIO);
196226584Sdim	sc->ndis_dobj = db->windrv_object;
197226584Sdim	sc->ndis_regvals = db->windrv_regvals;
198226584Sdim	resource_list_init(&sc->ndis_rl);
199226584Sdim
200226584Sdim	sc->ndis_io_rid = 0;
201226584Sdim	sc->ndis_res_io = bus_alloc_resource(dev,
202226584Sdim	    SYS_RES_IOPORT, &sc->ndis_io_rid,
203226584Sdim	    0, ~0, 1, RF_ACTIVE);
204226584Sdim	if (sc->ndis_res_io == NULL) {
205226584Sdim		device_printf(dev,
206226584Sdim		    "couldn't map iospace\n");
207226584Sdim		error = ENXIO;
208226584Sdim		goto fail;
209226584Sdim	}
210226584Sdim	sc->ndis_rescnt++;
211226584Sdim	resource_list_add(&sc->ndis_rl, SYS_RES_IOPORT, rid,
212226584Sdim	    rman_get_start(sc->ndis_res_io), rman_get_end(sc->ndis_res_io),
213226584Sdim	    rman_get_size(sc->ndis_res_io));
214226584Sdim
215226584Sdim	rid = 0;
216226584Sdim	sc->ndis_irq = bus_alloc_resource(dev,
217226584Sdim	    SYS_RES_IRQ, &rid, 0, ~0, 1,
218226584Sdim	    RF_SHAREABLE | RF_ACTIVE);
219226584Sdim	if (sc->ndis_irq == NULL) {
220226584Sdim		device_printf(dev,
221226584Sdim		    "couldn't map interrupt\n");
222226584Sdim		error = ENXIO;
223226584Sdim		goto fail;
224226584Sdim	}
225226584Sdim	sc->ndis_rescnt++;
226226584Sdim	resource_list_add(&sc->ndis_rl, SYS_RES_IRQ, rid,
227226584Sdim	    rman_get_start(sc->ndis_irq), rman_get_start(sc->ndis_irq), 1);
228226584Sdim
229226584Sdim	sc->ndis_iftype = PCMCIABus;
230226584Sdim
231226584Sdim	/* Figure out exactly which device we matched. */
232226584Sdim
233226584Sdim	t = db->windrv_devlist;
234226584Sdim
235226584Sdim	error = pccard_get_product_str(dev, &prodstr);
236226584Sdim	if (error)
237226584Sdim		return(error);
238226584Sdim	error = pccard_get_vendor_str(dev, &vendstr);
239226584Sdim	if (error)
240226584Sdim		return(error);
241226584Sdim
242226584Sdim	while(t->ndis_name != NULL) {
243226584Sdim		if (strcasecmp(vendstr, t->ndis_vid) == 0 &&
244226584Sdim		    strcasecmp(prodstr, t->ndis_did) == 0)
245226584Sdim			break;
246226584Sdim		t++;
247226584Sdim		devidx++;
248226584Sdim	}
249226584Sdim
250226584Sdim	sc->ndis_devidx = devidx;
251226584Sdim
252226584Sdim	error = ndis_attach(dev);
253226584Sdim
254226584Sdimfail:
255226584Sdim	return(error);
256226584Sdim}
257226584Sdim
258226584Sdimstatic struct resource_list *
259226584Sdimndis_get_resource_list(dev, child)
260226584Sdim	device_t		dev;
261226584Sdim	device_t		child;
262226584Sdim{
263226584Sdim	struct ndis_softc	*sc;
264226584Sdim
265226584Sdim	sc = device_get_softc(dev);
266226584Sdim	return (&sc->ndis_rl);
267226584Sdim}
268226584Sdim
269226584Sdim#define NDIS_AM_RID 3
270226584Sdim
271226584Sdimint
272226584Sdimndis_alloc_amem(arg)
273226584Sdim	void			*arg;
274226584Sdim{
275226584Sdim	struct ndis_softc	*sc;
276226584Sdim	int			error, rid;
277226584Sdim
278226584Sdim	if (arg == NULL)
279226584Sdim		return(EINVAL);
280226584Sdim
281226584Sdim	sc = arg;
282226584Sdim	rid = NDIS_AM_RID;
283226584Sdim	sc->ndis_res_am = bus_alloc_resource(sc->ndis_dev, SYS_RES_MEMORY,
284226584Sdim	    &rid, 0UL, ~0UL, 0x1000, RF_ACTIVE);
285226584Sdim
286226584Sdim	if (sc->ndis_res_am == NULL) {
287226584Sdim		device_printf(sc->ndis_dev,
288226584Sdim		    "failed to allocate attribute memory\n");
289226584Sdim		return(ENXIO);
290226584Sdim	}
291226584Sdim	sc->ndis_rescnt++;
292226584Sdim	resource_list_add(&sc->ndis_rl, SYS_RES_MEMORY, rid,
293226584Sdim	    rman_get_start(sc->ndis_res_am), rman_get_end(sc->ndis_res_am),
294226584Sdim	    rman_get_size(sc->ndis_res_am));
295226584Sdim
296226584Sdim	error = CARD_SET_MEMORY_OFFSET(device_get_parent(sc->ndis_dev),
297226584Sdim	    sc->ndis_dev, rid, 0, NULL);
298226584Sdim
299226584Sdim	if (error) {
300226584Sdim		device_printf(sc->ndis_dev,
301226584Sdim		    "CARD_SET_MEMORY_OFFSET() returned 0x%x\n", error);
302226584Sdim		return(error);
303226584Sdim	}
304226584Sdim
305226584Sdim	error = CARD_SET_RES_FLAGS(device_get_parent(sc->ndis_dev),
306226584Sdim	    sc->ndis_dev, SYS_RES_MEMORY, rid, PCCARD_A_MEM_ATTR);
307226584Sdim
308226584Sdim	if (error) {
309226584Sdim		device_printf(sc->ndis_dev,
310226584Sdim		    "CARD_SET_RES_FLAGS() returned 0x%x\n", error);
311226584Sdim		return(error);
312226584Sdim	}
313226584Sdim
314226584Sdim	sc->ndis_am_rid = rid;
315226584Sdim
316226584Sdim	return(0);
317263509Sdim}
318263509Sdim
319226584Sdimvoid
320226584Sdimndis_free_amem(arg)
321226584Sdim	void			*arg;
322226584Sdim{
323226584Sdim	struct ndis_softc	*sc;
324226584Sdim
325226584Sdim	if (arg == NULL)
326226584Sdim		return;
327226584Sdim
328226584Sdim	sc = arg;
329226584Sdim
330226584Sdim	if (sc->ndis_res_am != NULL)
331226584Sdim		bus_release_resource(sc->ndis_dev, SYS_RES_MEMORY,
332226584Sdim		    sc->ndis_am_rid, sc->ndis_res_am);
333226584Sdim	resource_list_free(&sc->ndis_rl);
334226584Sdim
335226584Sdim	return;
336226584Sdim}
337226584Sdim