if_wi_pccard.c revision 101245
142575Speter/*
2261363Sgshapiro * Copyright (c) 1997, 1998, 1999
364562Sgshapiro *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
442575Speter *
542575Speter * Redistribution and use in source and binary forms, with or without
642575Speter * modification, are permitted provided that the following conditions
742575Speter * are met:
842575Speter * 1. Redistributions of source code must retain the above copyright
942575Speter *    notice, this list of conditions and the following disclaimer.
1042575Speter * 2. Redistributions in binary form must reproduce the above copyright
1164562Sgshapiro *    notice, this list of conditions and the following disclaimer in the
1242575Speter *    documentation and/or other materials provided with the distribution.
13266692Sgshapiro * 3. All advertising materials mentioning features or use of this software
1490792Sgshapiro *    must display the following acknowledgement:
15110560Sgshapiro *	This product includes software developed by Bill Paul.
16110560Sgshapiro * 4. Neither the name of the author nor the names of any co-contributors
1777349Sgshapiro *    may be used to endorse or promote products derived from this software
1890792Sgshapiro *    without specific prior written permission.
1990792Sgshapiro *
2090792Sgshapiro * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2190792Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2290792Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2390792Sgshapiro * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24168515Sgshapiro * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2564562Sgshapiro * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2677349Sgshapiro * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2777349Sgshapiro * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2877349Sgshapiro * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2977349Sgshapiro * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3077349Sgshapiro * THE POSSIBILITY OF SUCH DAMAGE.
3177349Sgshapiro */
3277349Sgshapiro
3377349Sgshapiro/*
3477349Sgshapiro * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD.
3577349Sgshapiro *
3677349Sgshapiro * Written by Bill Paul <wpaul@ctr.columbia.edu>
3777349Sgshapiro * Electrical Engineering Department
3890792Sgshapiro * Columbia University, New York City
3990792Sgshapiro */
4077349Sgshapiro
4177349Sgshapiro#include "opt_wi.h"
4277349Sgshapiro
4377349Sgshapiro#include <sys/param.h>
4490792Sgshapiro#include <sys/kernel.h>
45141858Sgshapiro#include <sys/socket.h>
4642575Speter#include <sys/systm.h>
4742575Speter#include <sys/module.h>
4890792Sgshapiro#include <sys/bus.h>
4942575Speter
5042575Speter#include <machine/bus.h>
5142575Speter#include <machine/resource.h>
5242575Speter#include <sys/rman.h>
5342575Speter
5442575Speter#include <net/if.h>
5542575Speter#include <net/if_arp.h>
5642575Speter#include <net/ethernet.h>
5742575Speter#include <net/if_dl.h>
5842575Speter#include <net/if_media.h>
5942575Speter#include <net/if_types.h>
6042575Speter#include <net/if_ieee80211.h>
6142575Speter
6242575Speter#include <dev/pccard/pccardvar.h>
6342575Speter#if __FreeBSD_version >= 500000
6490792Sgshapiro#include <dev/pccard/pccarddevs.h>
6564562Sgshapiro#endif
6642575Speter
6764562Sgshapiro#include <dev/wi/if_wavelan_ieee.h>
6842575Speter#include <dev/wi/wi_hostap.h>
6942575Speter#include <dev/wi/if_wivar.h>
7090792Sgshapiro#include <dev/wi/if_wireg.h>
7142575Speter#ifdef WI_SYMBOL_FIRMWARE
7242575Speter#include <dev/wi/spectrum24t_cf.h>
73168515Sgshapiro#endif
7442575Speter
7542575Speter#include "card_if.h"
7642575Speter
7742575Speter#if !defined(lint)
7842575Speterstatic const char rcsid[] =
7942575Speter  "$FreeBSD: head/sys/dev/wi/if_wi_pccard.c 101245 2002-08-03 00:19:58Z imp $";
8042575Speter#endif
8142575Speter
8242575Speterstatic int wi_pccard_probe(device_t);
8342575Speterstatic int wi_pccard_attach(device_t);
8442575Speter
8542575Speter#ifdef WI_SYMBOL_FIRMWARE
8642575Speter/* support to download firmware for symbol CF card */
8742575Speterstatic int wi_pcmcia_load_firm(struct wi_softc *, const void *, int, const void *, int);
8842575Speterstatic int wi_pcmcia_write_firm(struct wi_softc *, const void *, int, const void *, int);
8942575Speterstatic int wi_pcmcia_set_hcr(struct wi_softc *, int);
9042575Speter#endif
9142575Speter
92110560Sgshapiro#if __FreeBSD_version < 500000
93110560Sgshapirostatic device_method_t wi_pccard_methods[] = {
94110560Sgshapiro	/* Device interface */
95110560Sgshapiro	DEVMETHOD(device_probe,		wi_pccard_probe),
96110560Sgshapiro	DEVMETHOD(device_attach,	wi_pccard_attach),
97110560Sgshapiro	DEVMETHOD(device_detach,	wi_generic_detach),
9842575Speter	DEVMETHOD(device_shutdown,	wi_shutdown),
9964562Sgshapiro
100168515Sgshapiro	{ 0, 0 }
10142575Speter};
10290792Sgshapiro
103168515Sgshapiro#else
10442575Speterstatic int wi_pccard_match(device_t);
10542575Speter
106168515Sgshapirostatic device_method_t wi_pccard_methods[] = {
10742575Speter	/* Device interface */
10864562Sgshapiro	DEVMETHOD(device_probe,		pccard_compat_probe),
10943730Speter	DEVMETHOD(device_attach,	pccard_compat_attach),
11042575Speter	DEVMETHOD(device_detach,	wi_generic_detach),
11142575Speter	DEVMETHOD(device_shutdown,	wi_shutdown),
11242575Speter
11342575Speter	/* Card interface */
11471345Sgshapiro	DEVMETHOD(card_compat_match,	wi_pccard_match),
11542575Speter	DEVMETHOD(card_compat_probe,	wi_pccard_probe),
11671345Sgshapiro	DEVMETHOD(card_compat_attach,	wi_pccard_attach),
11771345Sgshapiro
11871345Sgshapiro	{ 0, 0 }
11971345Sgshapiro};
12071345Sgshapiro
12171345Sgshapiro#endif
12271345Sgshapiro
12371345Sgshapirostatic driver_t wi_pccard_driver = {
12471345Sgshapiro	"wi",
12542575Speter	wi_pccard_methods,
12664562Sgshapiro	sizeof(struct wi_softc)
12742575Speter};
12871345Sgshapiro
12971345SgshapiroDRIVER_MODULE(if_wi, pccard, wi_pccard_driver, wi_devclass, 0, 0);
13090792Sgshapiro
13171345Sgshapiro#if __FreeBSD_version >= 500000
13271345Sgshapirostatic const struct pccard_product wi_pccard_products[] = {
13390792Sgshapiro	PCMCIA_CARD(3COM, 3CRWE737A, 0),
13490792Sgshapiro	PCMCIA_CARD(3COM, 3CRWE777A, 0),
13542575Speter	PCMCIA_CARD(ACTIONTEC, HWC01170, 0),
13642575Speter	PCMCIA_CARD(ADDTRON, AWP100, 0),
13742575Speter	PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
13842575Speter	PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
13942575Speter	PCMCIA_CARD(COMPAQ, NC5004, 0),
14042575Speter	PCMCIA_CARD(CONTEC, FX_DS110_PCC, 0),
14142575Speter	PCMCIA_CARD(COREGA, WIRELESS_LAN_PCC_11, 0),
14264562Sgshapiro	PCMCIA_CARD(COREGA, WIRELESS_LAN_PCCA_11, 0),
14390792Sgshapiro	PCMCIA_CARD(COREGA, WIRELESS_LAN_PCCB_11, 0),
14442575Speter	PCMCIA_CARD(ELSA, XI300_IEEE, 0),
14542575Speter	PCMCIA_CARD(ELSA, XI325_IEEE, 0),
14642575Speter	PCMCIA_CARD(ELSA, XI800_IEEE, 0),
14742575Speter	PCMCIA_CARD(EMTAC, WLAN, 0),
14842575Speter	PCMCIA_CARD(ERICSSON, WIRELESSLAN, 0),
14942575Speter	PCMCIA_CARD(GEMTEK, WLAN, 0),
15064562Sgshapiro	PCMCIA_CARD(HWN, AIRWAY80211, 0),
15190792Sgshapiro	PCMCIA_CARD(INTEL, PRO_WLAN_2011, 0),
15242575Speter	PCMCIA_CARD(INTERSIL, PRISM2, 0),
15342575Speter	PCMCIA_CARD(IODATA2, WNB11PCM, 0),
15442575Speter	PCMCIA_CARD(LINKSYS2, IWN, 0),
15590792Sgshapiro	PCMCIA_CARD(LINKSYS2, IWN2, 0),
15642575Speter	/* Now that we do PRISM detection, I don't think we need these - imp */
15742575Speter	PCMCIA_CARD2(LUCENT, WAVELAN_IEEE, NANOSPEED_PRISM2, 0),
15890792Sgshapiro	PCMCIA_CARD2(LUCENT, WAVELAN_IEEE, NEC_CMZ_RT_WP, 0),
15942575Speter	PCMCIA_CARD2(LUCENT, WAVELAN_IEEE, NTT_ME_WLAN, 0),
16042575Speter	PCMCIA_CARD2(LUCENT, WAVELAN_IEEE, SMC_2632W, 0),
16142575Speter	/* Must be after other LUCENT ones because it is less specific */
16242575Speter	PCMCIA_CARD(LUCENT, WAVELAN_IEEE, 0),
16342575Speter	PCMCIA_CARD(NETGEAR2, MA401RA, 0),
16442575Speter	PCMCIA_CARD(NOKIA, C110_WLAN, 0),
16542575Speter	PCMCIA_CARD(PROXIM, RANGELANDS_8430, 0),
16642575Speter	PCMCIA_CARD(SAMSUNG, SWL_2000N, 0),
16742575Speter	PCMCIA_CARD(SIMPLETECH, SPECTRUM24_ALT, 0),
16842575Speter	PCMCIA_CARD(SOCKET, LP_WLAN_CF, 0),
16942575Speter	PCMCIA_CARD(SYMBOL, LA4100, 0),
17042575Speter	PCMCIA_CARD(TDK, LAK_CD011WL, 0),
17142575Speter	{ NULL }
17242575Speter};
17342575Speter
17442575Speterstatic int
17590792Sgshapirowi_pccard_match(dev)
17664562Sgshapiro	device_t	dev;
17742575Speter{
17842575Speter	const struct pccard_product *pp;
17942575Speter
18042575Speter	if ((pp = pccard_product_lookup(dev, wi_pccard_products,
18142575Speter	    sizeof(wi_pccard_products[0]), NULL)) != NULL) {
18242575Speter		device_set_desc(dev, pp->pp_name);
18342575Speter		return 0;
18442575Speter	}
18542575Speter	return ENXIO;
18642575Speter}
18742575Speter#endif
18871345Sgshapiro
18971345Sgshapirostatic int
19064562Sgshapirowi_pccard_probe(dev)
19142575Speter	device_t	dev;
19242575Speter{
19342575Speter	struct wi_softc	*sc;
19442575Speter	int		error;
19542575Speter
19642575Speter	sc = device_get_softc(dev);
19742575Speter	sc->wi_gone = 0;
19842575Speter	sc->wi_bus_type = WI_BUS_PCCARD;
19990792Sgshapiro
20042575Speter	error = wi_alloc(dev, 0);
20142575Speter	if (error)
20242575Speter		return (error);
20390792Sgshapiro
20442575Speter	wi_free(dev);
20542575Speter
20690792Sgshapiro	/* Make sure interrupts are disabled. */
20742575Speter	CSR_WRITE_2(sc, WI_INT_EN, 0);
20842575Speter	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
20942575Speter
21042575Speter	return (0);
21142575Speter}
21242575Speter
21342575Speterstatic int
21442575Speterwi_pccard_attach(device_t dev)
21542575Speter{
21642575Speter	struct wi_softc		*sc;
21742575Speter	int			error;
21842575Speter	uint32_t		vendor;
21942575Speter	uint32_t		product;
22042575Speter
22142575Speter	sc = device_get_softc(dev);
22290792Sgshapiro
22342575Speter	error = wi_alloc(dev, 0);
22442575Speter	if (error) {
22542575Speter		device_printf(dev, "wi_alloc() failed! (%d)\n", error);
22690792Sgshapiro		return (error);
22742575Speter	}
22890792Sgshapiro
22942575Speter	/*
23042575Speter	 * The cute little Symbol LA4100-series CF cards need to have
23142575Speter	 * code downloaded to them.
23264562Sgshapiro	 */
23342575Speter	pccard_get_vendor(dev, &vendor);
23442575Speter	pccard_get_product(dev, &product);
23542575Speter	if (vendor == PCMCIA_VENDOR_SYMBOL &&
23642575Speter	    product == PCMCIA_PRODUCT_SYMBOL_LA4100) {
23742575Speter#ifdef WI_SYMBOL_FIRMWARE
23842575Speter		if (wi_pcmcia_load_firm(sc,
23942575Speter		    spectrum24t_primsym, sizeof(spectrum24t_primsym),
24042575Speter		    spectrum24t_secsym, sizeof(spectrum24t_secsym))) {
24142575Speter			device_printf(dev, "couldn't load firmware\n");
24264562Sgshapiro		}
24364562Sgshapiro#else
24490792Sgshapiro		device_printf(dev,
24564562Sgshapiro		    "Symbol LA4100 needs 'option WI_SYMBOL_FIRMWARE'\n");
24664562Sgshapiro		return (ENXIO);
247141858Sgshapiro#endif
24864562Sgshapiro	}
24977349Sgshapiro
25077349Sgshapiro	return (wi_generic_attach(dev));
25177349Sgshapiro}
25277349Sgshapiro
25377349Sgshapiro#ifdef WI_SYMBOL_FIRMWARE
25477349Sgshapiro
25577349Sgshapiro/*
25664562Sgshapiro * Special routines to download firmware for Symbol CF card.
25764562Sgshapiro * XXX: This should be modified generic into any PRISM-2 based card.
25864562Sgshapiro */
25942575Speter
26042575Speter#define	WI_SBCF_PDIADDR		0x3100
26142575Speter
26242575Speter/* unaligned load little endian */
26342575Speter#define	GETLE32(p)	((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24))
26464562Sgshapiro#define	GETLE16(p)	((p)[0] | ((p)[1]<<8))
26590792Sgshapiro
26690792Sgshapirostatic int
26790792Sgshapirowi_pcmcia_load_firm(struct wi_softc *sc, const void *primsym, int primlen,
26890792Sgshapiro    const void *secsym, int seclen)
26942575Speter{
27042575Speter	uint8_t ebuf[256];
27142575Speter	int i;
27242575Speter
27342575Speter	/* load primary code and run it */
27442575Speter	wi_pcmcia_set_hcr(sc, WI_HCR_EEHOLD);
27590792Sgshapiro	if (wi_pcmcia_write_firm(sc, primsym, primlen, NULL, 0))
27664562Sgshapiro		return EIO;
27764562Sgshapiro	wi_pcmcia_set_hcr(sc, WI_HCR_RUN);
27864562Sgshapiro	for (i = 0; ; i++) {
27964562Sgshapiro		if (i == 10)
28064562Sgshapiro			return ETIMEDOUT;
28164562Sgshapiro		tsleep(sc, PWAIT, "wiinit", 1);
28264562Sgshapiro		if (CSR_READ_2(sc, WI_CNTL) == WI_CNTL_AUX_ENA_STAT)
28364562Sgshapiro			break;
28464562Sgshapiro		/* write the magic key value to unlock aux port */
28564562Sgshapiro		CSR_WRITE_2(sc, WI_PARAM0, WI_AUX_KEY0);
28664562Sgshapiro		CSR_WRITE_2(sc, WI_PARAM1, WI_AUX_KEY1);
28790792Sgshapiro		CSR_WRITE_2(sc, WI_PARAM2, WI_AUX_KEY2);
28890792Sgshapiro		CSR_WRITE_2(sc, WI_CNTL, WI_CNTL_AUX_ENA_CNTL);
28964562Sgshapiro	}
29064562Sgshapiro
29190792Sgshapiro	/* issue read EEPROM command: XXX copied from wi_cmd() */
29290792Sgshapiro	CSR_WRITE_2(sc, WI_PARAM0, 0);
29342575Speter	CSR_WRITE_2(sc, WI_PARAM1, 0);
29442575Speter	CSR_WRITE_2(sc, WI_PARAM2, 0);
29542575Speter	CSR_WRITE_2(sc, WI_COMMAND, WI_CMD_READEE);
29642575Speter        for (i = 0; i < WI_TIMEOUT; i++) {
29764562Sgshapiro                if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD)
29842575Speter                        break;
29964562Sgshapiro                DELAY(1);
30042575Speter        }
30190792Sgshapiro        CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD);
30290792Sgshapiro
30342575Speter	CSR_WRITE_2(sc, WI_AUX_PAGE, WI_SBCF_PDIADDR / WI_AUX_PGSZ);
304249729Sgshapiro	CSR_WRITE_2(sc, WI_AUX_OFFSET, WI_SBCF_PDIADDR % WI_AUX_PGSZ);
30542575Speter	CSR_READ_MULTI_STREAM_2(sc, WI_AUX_DATA,
30690792Sgshapiro	    (uint16_t *)ebuf, sizeof(ebuf) / 2);
30764562Sgshapiro	if (GETLE16(ebuf) > sizeof(ebuf))
30842575Speter		return EIO;
30990792Sgshapiro	if (wi_pcmcia_write_firm(sc, secsym, seclen, ebuf + 4, GETLE16(ebuf)))
31042575Speter		return EIO;
31142575Speter	return 0;
31290792Sgshapiro}
31342575Speter
31490792Sgshapirostatic int
31542575Speterwi_pcmcia_write_firm(struct wi_softc *sc, const void *buf, int buflen,
31642575Speter    const void *ebuf, int ebuflen)
31742575Speter{
31842575Speter	const uint8_t *p, *ep, *q, *eq;
31942575Speter	char *tp;
32042575Speter	uint32_t addr, id, eid;
32142575Speter	int i, len, elen, nblk, pdrlen;
322168515Sgshapiro
32342575Speter	/*
32442575Speter	 * Parse the header of the firmware image.
32564562Sgshapiro	 */
32642575Speter	p = buf;
32742575Speter	ep = p + buflen;
32842575Speter	while (p < ep && *p++ != ' ');	/* FILE: */
32964562Sgshapiro	while (p < ep && *p++ != ' ');	/* filename */
33042575Speter	while (p < ep && *p++ != ' ');	/* type of the firmware */
33164562Sgshapiro	nblk = strtoul(p, &tp, 10);
33242575Speter	p = tp;
33390792Sgshapiro	pdrlen = strtoul(p + 1, &tp, 10);
33442575Speter	p = tp;
33542575Speter	while (p < ep && *p++ != 0x1a);	/* skip rest of header */
33642575Speter
33764562Sgshapiro	/*
33842575Speter	 * Block records: address[4], length[2], data[length];
33942575Speter	 */
34042575Speter	for (i = 0; i < nblk; i++) {
34142575Speter		addr = GETLE32(p);	p += 4;
34242575Speter		len  = GETLE16(p);	p += 2;
34342575Speter		CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
34464562Sgshapiro		CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
34542575Speter		CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
34642575Speter		    (const uint16_t *)p, len / 2);
34742575Speter		p += len;
34864562Sgshapiro	}
34942575Speter
35090792Sgshapiro	/*
35164562Sgshapiro	 * PDR: id[4], address[4], length[4];
35242575Speter	 */
35342575Speter	for (i = 0; i < pdrlen; ) {
35442575Speter		id   = GETLE32(p);	p += 4; i += 4;
35590792Sgshapiro		addr = GETLE32(p);	p += 4; i += 4;
35664562Sgshapiro		len  = GETLE32(p);	p += 4; i += 4;
35742575Speter		/* replace PDR entry with the values from EEPROM, if any */
35842575Speter		for (q = ebuf, eq = q + ebuflen; q < eq; q += elen * 2) {
35942575Speter			elen = GETLE16(q);	q += 2;
36042575Speter			eid  = GETLE16(q);	q += 2;
36164562Sgshapiro			elen--;		/* elen includes eid */
36290792Sgshapiro			if (eid == 0)
36364562Sgshapiro				break;
36464562Sgshapiro			if (eid != id)
36564562Sgshapiro				continue;
36690792Sgshapiro			CSR_WRITE_2(sc, WI_AUX_PAGE, addr / WI_AUX_PGSZ);
36790792Sgshapiro			CSR_WRITE_2(sc, WI_AUX_OFFSET, addr % WI_AUX_PGSZ);
36890792Sgshapiro			CSR_WRITE_MULTI_STREAM_2(sc, WI_AUX_DATA,
36990792Sgshapiro			    (const uint16_t *)q, len / 2);
37090792Sgshapiro			break;
37164562Sgshapiro		}
37264562Sgshapiro	}
37364562Sgshapiro	return 0;
37464562Sgshapiro}
37564562Sgshapiro
37664562Sgshapirostatic int
37790792Sgshapirowi_pcmcia_set_hcr(struct wi_softc *sc, int mode)
37890792Sgshapiro{
37990792Sgshapiro	uint16_t hcr;
38064562Sgshapiro
38190792Sgshapiro	CSR_WRITE_2(sc, WI_COR, WI_COR_RESET);
38290792Sgshapiro	tsleep(sc, PWAIT, "wiinit", 1);
38390792Sgshapiro	hcr = CSR_READ_2(sc, WI_HCR);
38490792Sgshapiro	hcr = (hcr & WI_HCR_4WIRE) | (mode & ~WI_HCR_4WIRE);
38564562Sgshapiro	CSR_WRITE_2(sc, WI_HCR, hcr);
38690792Sgshapiro	tsleep(sc, PWAIT, "wiinit", 1);
38742575Speter	CSR_WRITE_2(sc, WI_COR, WI_COR_IOMODE);
38842575Speter	tsleep(sc, PWAIT, "wiinit", 1);
38990792Sgshapiro	return 0;
39090792Sgshapiro}
39190792Sgshapiro#endif
39290792Sgshapiro