1139749Simp/*-
2126706Swpaul * Copyright (c) 2003
3126706Swpaul *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4126706Swpaul *
5126706Swpaul * Redistribution and use in source and binary forms, with or without
6126706Swpaul * modification, are permitted provided that the following conditions
7126706Swpaul * are met:
8126706Swpaul * 1. Redistributions of source code must retain the above copyright
9126706Swpaul *    notice, this list of conditions and the following disclaimer.
10126706Swpaul * 2. Redistributions in binary form must reproduce the above copyright
11126706Swpaul *    notice, this list of conditions and the following disclaimer in the
12126706Swpaul *    documentation and/or other materials provided with the distribution.
13126706Swpaul * 3. All advertising materials mentioning features or use of this software
14126706Swpaul *    must display the following acknowledgement:
15126706Swpaul *	This product includes software developed by Bill Paul.
16126706Swpaul * 4. Neither the name of the author nor the names of any co-contributors
17126706Swpaul *    may be used to endorse or promote products derived from this software
18126706Swpaul *    without specific prior written permission.
19126706Swpaul *
20126706Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21126706Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22126706Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23126706Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24126706Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25126706Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26126706Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27126706Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28126706Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29126706Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30126706Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
31126706Swpaul */
32126706Swpaul
33126706Swpaul#include <sys/cdefs.h>
34126706Swpaul__FBSDID("$FreeBSD$");
35126706Swpaul
36126706Swpaul#include <sys/ctype.h>
37126706Swpaul#include <sys/param.h>
38126706Swpaul#include <sys/systm.h>
39126706Swpaul#include <sys/kernel.h>
40129972Swpaul#include <sys/module.h>
41126706Swpaul#include <sys/socket.h>
42126706Swpaul#include <sys/queue.h>
43126706Swpaul#include <sys/sysctl.h>
44126706Swpaul
45126706Swpaul#include <net/if.h>
46126706Swpaul#include <net/if_arp.h>
47126706Swpaul#include <net/if_media.h>
48126706Swpaul
49126706Swpaul#include <machine/bus.h>
50126706Swpaul#include <machine/resource.h>
51126706Swpaul#include <sys/bus.h>
52126706Swpaul#include <sys/rman.h>
53126706Swpaul
54189488Sweongyo#include <dev/usb/usb.h>
55194677Sthompsa#include <dev/usb/usbdi.h>
56189488Sweongyo
57126706Swpaul#include <net80211/ieee80211_var.h>
58126706Swpaul
59126706Swpaul#include <compat/ndis/pe_var.h>
60145485Swpaul#include <compat/ndis/cfg_var.h>
61126706Swpaul#include <compat/ndis/resource_var.h>
62126706Swpaul#include <compat/ndis/ntoskrnl_var.h>
63126706Swpaul#include <compat/ndis/ndis_var.h>
64126706Swpaul#include <dev/if_ndis/if_ndisvar.h>
65126706Swpaul
66126706Swpaul#include <dev/pccard/pccardvar.h>
67126706Swpaul#include "card_if.h"
68126706Swpaul
69126706SwpaulMODULE_DEPEND(ndis, pccard, 1, 1, 1);
70126706Swpaul
71126706Swpaulstatic int ndis_probe_pccard	(device_t);
72126706Swpaulstatic int ndis_attach_pccard	(device_t);
73131953Swpaulstatic struct resource_list *ndis_get_resource_list
74131953Swpaul				(device_t, device_t);
75146016Swpaulstatic int ndis_devcompare	(interface_type,
76146016Swpaul				 struct ndis_pccard_type *, device_t);
77141524Swpaulextern int ndisdrv_modevent	(module_t, int, void *);
78126706Swpaulextern int ndis_attach		(device_t);
79126706Swpaulextern int ndis_shutdown	(device_t);
80126706Swpaulextern int ndis_detach		(device_t);
81126706Swpaulextern int ndis_suspend		(device_t);
82126706Swpaulextern int ndis_resume		(device_t);
83126706Swpaul
84141524Swpaulextern unsigned char drv_data[];
85126706Swpaul
86126706Swpaulstatic device_method_t ndis_methods[] = {
87126706Swpaul	/* Device interface */
88126706Swpaul	DEVMETHOD(device_probe,		ndis_probe_pccard),
89126706Swpaul	DEVMETHOD(device_attach,	ndis_attach_pccard),
90126706Swpaul	DEVMETHOD(device_detach,	ndis_detach),
91126706Swpaul	DEVMETHOD(device_shutdown,	ndis_shutdown),
92126706Swpaul	DEVMETHOD(device_suspend,	ndis_suspend),
93126706Swpaul	DEVMETHOD(device_resume,	ndis_resume),
94126706Swpaul
95131953Swpaul	/* Bus interface. */
96131953Swpaul
97131953Swpaul	/*
98131953Swpaul	 * This is an awful kludge, but we need it becase pccard
99131953Swpaul	 * does not implement a bus_get_resource_list() method.
100131953Swpaul	 */
101131953Swpaul
102131953Swpaul	DEVMETHOD(bus_get_resource_list, ndis_get_resource_list),
103131953Swpaul
104126706Swpaul	{ 0, 0 }
105126706Swpaul};
106126706Swpaul
107126706Swpaulstatic driver_t ndis_driver = {
108126706Swpaul	"ndis",
109126706Swpaul	ndis_methods,
110126706Swpaul	sizeof(struct ndis_softc)
111126706Swpaul};
112126706Swpaul
113126706Swpaulstatic devclass_t ndis_devclass;
114126706Swpaul
115141524SwpaulDRIVER_MODULE(ndis, pccard, ndis_driver, ndis_devclass, ndisdrv_modevent, 0);
116126706Swpaul
117126706Swpaulstatic int
118146016Swpaulndis_devcompare(bustype, t, dev)
119146016Swpaul	interface_type		bustype;
120145485Swpaul	struct ndis_pccard_type	*t;
121126706Swpaul	device_t		dev;
122126706Swpaul{
123126706Swpaul	const char		*prodstr, *vendstr;
124126706Swpaul	int			error;
125142399Swpaul
126146016Swpaul	if (bustype != PCMCIABus)
127146016Swpaul		return(FALSE);
128146016Swpaul
129126706Swpaul	error = pccard_get_product_str(dev, &prodstr);
130126706Swpaul	if (error)
131145485Swpaul		return(FALSE);
132126706Swpaul	error = pccard_get_vendor_str(dev, &vendstr);
133126706Swpaul	if (error)
134145485Swpaul		return(FALSE);
135126706Swpaul
136126706Swpaul	while(t->ndis_name != NULL) {
137168423Spjd		if (strcasecmp(vendstr, t->ndis_vid) == 0 &&
138168423Spjd		    strcasecmp(prodstr, t->ndis_did) == 0) {
139126706Swpaul			device_set_desc(dev, t->ndis_name);
140145485Swpaul			return(TRUE);
141126706Swpaul		}
142126706Swpaul		t++;
143126706Swpaul	}
144126706Swpaul
145145485Swpaul	return(FALSE);
146145485Swpaul}
147145485Swpaul
148145485Swpaul/*
149145485Swpaul * Probe for an NDIS device. Check the PCI vendor and device
150145485Swpaul * IDs against our list and return a device name if we find a match.
151145485Swpaul */
152145485Swpaulstatic int
153145485Swpaulndis_probe_pccard(dev)
154145485Swpaul	device_t		dev;
155145485Swpaul{
156145485Swpaul	driver_object		*drv;
157145485Swpaul	struct drvdb_ent	*db;
158145485Swpaul
159145485Swpaul	drv = windrv_lookup(0, "PCCARD Bus");
160145485Swpaul	if (drv == NULL)
161145485Swpaul		return(ENXIO);
162145485Swpaul
163145485Swpaul	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
164145485Swpaul
165145485Swpaul	if (db != NULL) {
166145485Swpaul		/* Create PDO for this device instance */
167145485Swpaul		windrv_create_pdo(drv, dev);
168145485Swpaul		return(0);
169145485Swpaul	}
170145485Swpaul
171126706Swpaul	return(ENXIO);
172126706Swpaul}
173126706Swpaul
174126706Swpaul/*
175126706Swpaul * Attach the interface. Allocate softc structures, do ifmedia
176126706Swpaul * setup and ethernet/BPF attach.
177126706Swpaul */
178126706Swpaulstatic int
179126706Swpaulndis_attach_pccard(dev)
180126706Swpaul	device_t		dev;
181126706Swpaul{
182126706Swpaul	struct ndis_softc	*sc;
183126706Swpaul	int			unit, error = 0, rid;
184126706Swpaul	struct ndis_pccard_type	*t;
185126706Swpaul	int			devidx = 0;
186126706Swpaul	const char		*prodstr, *vendstr;
187145485Swpaul	struct drvdb_ent	*db;
188126706Swpaul
189126706Swpaul	sc = device_get_softc(dev);
190126706Swpaul	unit = device_get_unit(dev);
191126706Swpaul	sc->ndis_dev = dev;
192145485Swpaul
193145485Swpaul	db = windrv_match((matchfuncptr)ndis_devcompare, dev);
194145485Swpaul	if (db == NULL)
195145485Swpaul		return (ENXIO);
196146015Swpaul	sc->ndis_dobj = db->windrv_object;
197146015Swpaul	sc->ndis_regvals = db->windrv_regvals;
198131953Swpaul	resource_list_init(&sc->ndis_rl);
199126706Swpaul
200126706Swpaul	sc->ndis_io_rid = 0;
201216486Sjhb	sc->ndis_res_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
202216486Sjhb	    &sc->ndis_io_rid, RF_ACTIVE);
203126706Swpaul	if (sc->ndis_res_io == NULL) {
204126706Swpaul		device_printf(dev,
205126706Swpaul		    "couldn't map iospace\n");
206126706Swpaul		error = ENXIO;
207126706Swpaul		goto fail;
208126706Swpaul	}
209126706Swpaul	sc->ndis_rescnt++;
210229922Sdim	resource_list_add(&sc->ndis_rl, SYS_RES_IOPORT, sc->ndis_io_rid,
211131953Swpaul	    rman_get_start(sc->ndis_res_io), rman_get_end(sc->ndis_res_io),
212131953Swpaul	    rman_get_size(sc->ndis_res_io));
213126706Swpaul
214126706Swpaul	rid = 0;
215216486Sjhb	sc->ndis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
216126706Swpaul	    RF_SHAREABLE | RF_ACTIVE);
217126706Swpaul	if (sc->ndis_irq == NULL) {
218126706Swpaul		device_printf(dev,
219126706Swpaul		    "couldn't map interrupt\n");
220126706Swpaul		error = ENXIO;
221126706Swpaul		goto fail;
222126706Swpaul	}
223126706Swpaul	sc->ndis_rescnt++;
224131953Swpaul	resource_list_add(&sc->ndis_rl, SYS_RES_IRQ, rid,
225131953Swpaul	    rman_get_start(sc->ndis_irq), rman_get_start(sc->ndis_irq), 1);
226126706Swpaul
227126706Swpaul	sc->ndis_iftype = PCMCIABus;
228126706Swpaul
229126706Swpaul	/* Figure out exactly which device we matched. */
230126706Swpaul
231145485Swpaul	t = db->windrv_devlist;
232126706Swpaul
233126706Swpaul	error = pccard_get_product_str(dev, &prodstr);
234126706Swpaul	if (error)
235126706Swpaul		return(error);
236126706Swpaul	error = pccard_get_vendor_str(dev, &vendstr);
237126706Swpaul	if (error)
238126706Swpaul		return(error);
239126706Swpaul
240126706Swpaul	while(t->ndis_name != NULL) {
241168423Spjd		if (strcasecmp(vendstr, t->ndis_vid) == 0 &&
242168423Spjd		    strcasecmp(prodstr, t->ndis_did) == 0)
243126706Swpaul			break;
244126706Swpaul		t++;
245126706Swpaul		devidx++;
246126706Swpaul	}
247126706Swpaul
248126706Swpaul	sc->ndis_devidx = devidx;
249126706Swpaul
250126706Swpaul	error = ndis_attach(dev);
251126706Swpaul
252126706Swpaulfail:
253126706Swpaul	return(error);
254126706Swpaul}
255126706Swpaul
256131953Swpaulstatic struct resource_list *
257131953Swpaulndis_get_resource_list(dev, child)
258131953Swpaul	device_t		dev;
259131953Swpaul	device_t		child;
260131953Swpaul{
261131953Swpaul	struct ndis_softc	*sc;
262131953Swpaul
263131953Swpaul	sc = device_get_softc(dev);
264131953Swpaul	return (&sc->ndis_rl);
265131953Swpaul}
266131953Swpaul
267126706Swpaul#define NDIS_AM_RID 3
268126706Swpaul
269126706Swpaulint
270126706Swpaulndis_alloc_amem(arg)
271126706Swpaul	void			*arg;
272126706Swpaul{
273126706Swpaul	struct ndis_softc	*sc;
274126706Swpaul	int			error, rid;
275126706Swpaul
276126706Swpaul	if (arg == NULL)
277126706Swpaul		return(EINVAL);
278126706Swpaul
279126706Swpaul	sc = arg;
280126706Swpaul	rid = NDIS_AM_RID;
281126706Swpaul	sc->ndis_res_am = bus_alloc_resource(sc->ndis_dev, SYS_RES_MEMORY,
282126706Swpaul	    &rid, 0UL, ~0UL, 0x1000, RF_ACTIVE);
283126706Swpaul
284126706Swpaul	if (sc->ndis_res_am == NULL) {
285126706Swpaul		device_printf(sc->ndis_dev,
286126706Swpaul		    "failed to allocate attribute memory\n");
287126706Swpaul		return(ENXIO);
288126706Swpaul	}
289131953Swpaul	sc->ndis_rescnt++;
290131953Swpaul	resource_list_add(&sc->ndis_rl, SYS_RES_MEMORY, rid,
291131953Swpaul	    rman_get_start(sc->ndis_res_am), rman_get_end(sc->ndis_res_am),
292131953Swpaul	    rman_get_size(sc->ndis_res_am));
293126706Swpaul
294126706Swpaul	error = CARD_SET_MEMORY_OFFSET(device_get_parent(sc->ndis_dev),
295126706Swpaul	    sc->ndis_dev, rid, 0, NULL);
296126706Swpaul
297126706Swpaul	if (error) {
298126706Swpaul		device_printf(sc->ndis_dev,
299126706Swpaul		    "CARD_SET_MEMORY_OFFSET() returned 0x%x\n", error);
300126706Swpaul		return(error);
301126706Swpaul	}
302126706Swpaul
303126706Swpaul	error = CARD_SET_RES_FLAGS(device_get_parent(sc->ndis_dev),
304126706Swpaul	    sc->ndis_dev, SYS_RES_MEMORY, rid, PCCARD_A_MEM_ATTR);
305126706Swpaul
306126706Swpaul	if (error) {
307126706Swpaul		device_printf(sc->ndis_dev,
308126706Swpaul		    "CARD_SET_RES_FLAGS() returned 0x%x\n", error);
309126706Swpaul		return(error);
310126706Swpaul	}
311126706Swpaul
312131953Swpaul	sc->ndis_am_rid = rid;
313131953Swpaul
314126706Swpaul	return(0);
315126706Swpaul}
316131953Swpaul
317131953Swpaulvoid
318131953Swpaulndis_free_amem(arg)
319131953Swpaul	void			*arg;
320131953Swpaul{
321131953Swpaul	struct ndis_softc	*sc;
322131953Swpaul
323131953Swpaul	if (arg == NULL)
324131953Swpaul		return;
325131953Swpaul
326131953Swpaul	sc = arg;
327131953Swpaul
328131953Swpaul	if (sc->ndis_res_am != NULL)
329131953Swpaul		bus_release_resource(sc->ndis_dev, SYS_RES_MEMORY,
330131953Swpaul		    sc->ndis_am_rid, sc->ndis_res_am);
331131953Swpaul	resource_list_free(&sc->ndis_rl);
332131953Swpaul
333131953Swpaul	return;
334131953Swpaul}
335