Subject: kern/10007: pcmcia xe driver doesn't work on some cards
To: None <gnats-bugs@gnats.netbsd.org>
From: John Kohl <jtk@kolvir.arlington.ma.us>
List: netbsd-bugs
Date: 04/28/2000 20:05:14
>Number:         10007
>Category:       kern
>Synopsis:       pcmcia xe driver doesn't work on some cards
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 28 17:06:00 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     John Kohl
>Release:        NetBSD-1.4X, 2000-04-28
>Organization:
NetBSD Kernel Hackers `R` Us
>Environment:
	
System: NetBSD kolvir.arlington.ma.us 1.4X NetBSD 1.4X (KOLVIR) #4: Sun Apr 9 17:56:09 EDT 2000 jtk@kolvir.arlington.ma.us:/usr/u4/sandbox/src/sys/arch/i386/compile/KOLVIR i386


>Description:
	The if_xe.c driver in the source tree doesn't work on some
cards.  Gregory McGarry <g.mcgarry@qut.edu.au> provided an updated driver so it
works for my card, but other developers have commented that the insides
of the revised driver are still pretty gross.
Unfortunately, my attempts to contact Gregory via e-mail have failed, so
I'm submitting this PR on his behalf.

>How-To-Repeat:
	Try to use a Xircom CE3 card.
>Fix:
Here's Gregory's updated driver, reworked very slightly by me to make
forced detach work.

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	if_xe.c
#	xediff
#
echo x - if_xe.c
sed 's/^X//' >if_xe.c << 'END-of-if_xe.c'
X/*	$NetBSD$	*/
X/*	OpenBSD: if_xe.c,v 1.9 1999/09/16 11:28:42 niklas Exp 	*/
X
X/*
X * Copyright (c) 1999 Niklas Hallqvist, C Stone, Job de Haas
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by Niklas Hallqvist,
X *	C Stone and Job de Haas.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Known Bugs:
X *
X * 1) The name of this driver conflicts with the next68k ethernet driver.
X * 2) Promiscuous mode doesn't work on at least the CE2.
X * 3) Slow. ~450KB/s.  Memory access would be better.
X */
X
X/*
X * A driver for Xircom CreditCard PCMCIA Ethernet adapters.  The
X * following cards are currently known to work with the driver:
X *
X *   Xircom CreditCard Ethernet II (CE2)
X *   Xircom CreditCard 10/100 Ethernet (CE3)
X *   Xircom CreditCard Ethernet + Modem (CEM)
X *   Xircom CreditCard Ethernet + ModemII (CEM2)
X *   Xircom CreditCard Ethernet + Modem 28 (CEM28)
X *   Xircom CreditCard Ethernet + Modem 33 (CEM33)
X *   Xircom CreditCard 10/100 Ethernet + Modem 56 (CEM56)
X *   Xircom RealPort Ethernet
X *   Xircom RealPort 10/100 Ethernet 
X *   Xircom RealPort 10/100 Ethernet + Modem 56 (REM56, REM56G)
X *   Intel EtherExpress Pro/100 PC Card Mobile Adapter 16 (Pro/100 M16A)
X *   Compaq Netelligent 10/100 PC Card (CPQ-10/100)
X */
X
X#include "opt_inet.h"
X#include "bpfilter.h"
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/device.h>
X#include <sys/ioctl.h>
X#include <sys/mbuf.h>
X#include <sys/malloc.h>
X/* #include <sys/kernel.h> */
X#include <sys/socket.h>
X
X#include <net/if.h>
X#include <net/if_dl.h>
X#include <net/if_media.h>
X#include <net/if_types.h>
X#include <net/if_ether.h>
X
X#ifdef INET
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X#include <netinet/if_inarp.h>
X#endif
X
X#ifdef IPX
X#include <netipx/ipx.h>
X#include <netipx/ipx_if.h>
X#endif
X
X#ifdef NS
X#include <netns/ns.h>
X#include <netns/ns_if.h>
X#endif
X
X#if NBPFILTER > 0
X#include <net/bpf.h>
X#include <net/bpfdesc.h>
X#endif
X
X#define ETHER_MIN_LEN 64
X#define ETHER_CRC_LEN 4
X
X/*
X * Maximum number of bytes to read per interrupt.  Linux recommends
X * somewhere between 2000-22000.
X * XXX This is currently a hard maximum.
X */
X#define MAX_BYTES_INTR 12000
X
X#include <dev/mii/mii.h>
X#include <dev/mii/miivar.h>
X
X#include <dev/pcmcia/pcmciareg.h>
X#include <dev/pcmcia/pcmciavar.h>
X#include <dev/pcmcia/pcmciadevs.h>
X
X#if 1
X
X/* these aren't in pcmciadevs yet */
X/* #define PCMCIA_VENDOR_XIRCOM	PCMCIA_VENDOR_TDK */
X#define PCMCIA_VENDOR_COMPAQ	0x0138
X#define PCMCIA_VENDOR_COMPAQ2	0x0183
X#define PCMCIA_VENDOR_INTEL	0x0089
X
X/* XIRCOM is defined as TDK, which should be fixed */
X#define PCMCIA_PRODUCT_XIRCOM_CE		0x0108
X#define PCMCIA_PRODUCT_XIRCOM_CE2 		0x010b
X#define PCMCIA_PRODUCT_XIRCOM_CE3		0x010a
X#define PCMCIA_PRODUCT_COMPAQ2_CPQ_10_100	0x010a
X#define PCMCIA_PRODUCT_INTEL_EEPRO100		0x010a
X#define PCMCIA_PRODUCT_XIRCOM_CEM		0x110a
X#define PCMCIA_PRODUCT_XIRCOM_CEM28		0x110b
X
X#define PCMCIA_STR_XIRCOM_CE		"Xircom CreditCard Ethernet"
X#define PCMCIA_STR_XIRCOM_CE2		"Xircom CreditCard Ethernet II"
X#define PCMCIA_STR_XIRCOM_CE3		"Xircom CreditCard 10/100 Ethernet"
X#define PCMCIA_STR_COMPAQ2_CPQ_10_100	"Compaq Netelligent 10/100 Ethernet"
X#define PCMCIA_STR_INTEL_EEPRO100	"Intel EtherExpress PRO/100"
X#define PCMCIA_STR_XIRCOM_CEM		"Xircom CreditCard Ethernet + Modem"
X#define PCMCIA_STR_XIRCOM_CEM28		"Xircom CreditCard Ethernet + Modem 28"
X
X#endif
X
X#define XE_IOSIZ	16
X
X
X#include <dev/pcmcia/if_xereg.h>
X
X
X#ifdef __GNUC__
X#define INLINE	__inline
X#else
X#define INLINE
X#endif	/* __GNUC__ */
X
X
X#ifdef XEDEBUG
X
X#define XED_CONFIG	0x1
X#define XED_MII		0x2
X#define XED_INTR	0x4
X#define XED_FIFO	0x8
X
X#ifndef XEDEBUG_DEF
X#define XEDEBUG_DEF	(XED_CONFIG | XED_INTR | XED_MII | XED_FIFO)
X#endif	/* XEDEBUG_DEF */
X
X#if 0
Xint xedebug = XEDEBUG_DEF;
X#else
Xint xedebug = 0;
X#endif
X
X#define DPRINTF(cat, x) if (xedebug & (cat)) printf x
X
X#else	/* XEDEBUG */
X#define DPRINTF(cat, x) (void)0
X#endif	/* XEDEBUG */
X
X
Xint	xe_pcmcia_match __P((struct device *, struct cfdata *, void *));
Xvoid	xe_pcmcia_attach __P((struct device *, struct device *, void *));
Xint	xe_pcmcia_detach __P((struct device *, int));
Xint	xe_pcmcia_activate __P((struct device *, enum devact));
X
X/*
X * In case this chipset ever turns up out of pcmcia attachments (very
X * unlikely) do the driver splitup.
X */
Xstruct xe_softc {
X	struct device sc_dev;			/* Generic device info */
X	struct ethercom sc_ethercom;		/* Ethernet common part */
X
X	struct ifmedia sc_media;		/* Media control */
X	struct mii_data sc_mii;			/* MII media information */
X
X	bus_space_tag_t		sc_bst;		/* Bus cookie */
X	bus_space_handle_t	sc_bsh;		/* Bus I/O handle */
X	bus_addr_t		sc_offset;	/* Offset of registers */
X
X	u_int8_t	sc_rev;			/* Chip revision */
X	u_int32_t	sc_flags;		/* Misc. flags */
X	int		sc_all_mcasts;		/* Receive all multicasts */
X	u_int8_t 	sc_enaddr[ETHER_ADDR_LEN];
X};
X
Xstruct xe_pcmcia_softc {
X	struct	xe_softc sc_xe;			/* Generic device info */
X
X	/* PCMCIA-specific goo */
X	struct	pcmcia_function *sc_pf;		/* PCMCIA function */
X	struct	pcmcia_io_handle sc_pcioh;	/* iospace info */
X	int	sc_io_window;			/* io window info */
X	void	*sc_ih;				/* Interrupt handler */
X
X	int	sc_resource;			/* resource allocated */
X#define XE_RES_PCIC	1
X#define XE_RES_IO	2
X#define XE_RES_MI	8
X};
X
Xstruct cfattach xe_pcmcia_ca = {
X	sizeof (struct xe_pcmcia_softc), xe_pcmcia_match, xe_pcmcia_attach,
X	xe_pcmcia_detach, xe_pcmcia_activate
X};
X
Xstatic int xe_pcmcia_cis_quirks __P((struct pcmcia_function *));
Xstatic void xe_cycle_power __P((struct xe_softc *));
Xstatic int xe_ether_ioctl __P((struct ifnet *, u_long cmd, caddr_t));
Xstatic void xe_full_reset __P((struct xe_softc *));
Xstatic void xe_init __P((struct xe_softc *));
Xstatic int xe_intr __P((void *));
Xstatic int xe_ioctl __P((struct ifnet *, u_long, caddr_t));
Xstatic int xe_mdi_read __P((struct device *, int, int));
Xstatic void xe_mdi_write __P((struct device *, int, int, int));
Xstatic int xe_mediachange __P((struct ifnet *));
Xstatic void xe_mediastatus __P((struct ifnet *, struct ifmediareq *));
Xstatic int xe_pcmcia_funce_enaddr __P((struct device *, u_int8_t *));
Xstatic int xe_pcmcia_lan_nid_ciscallback __P((struct pcmcia_tuple *, void *));
Xstatic u_int16_t xe_get __P((struct xe_softc *));
Xstatic void xe_reset __P((struct xe_softc *));
Xstatic void xe_set_address __P((struct xe_softc *));
Xstatic void xe_start __P((struct ifnet *));
Xstatic void xe_statchg __P((struct device *));
Xstatic void xe_stop __P((struct xe_softc *));
Xstatic void xe_watchdog __P((struct ifnet *));
X
X/* flags */
X#define XE_FLAGS_MOHAWK	0x001		/* 100Mb capabilities (has phy) */
X#define XE_FLAGS_DINGO	0x002		/* realport cards ??? */
X#define XE_FLAGS_MODEM	0x004		/* modem also present */
X
Xstruct xe_pcmcia_product {
X	u_int32_t	xpp_vendor;	/* vendor ID */
X	u_int32_t	xpp_product;	/* product ID */
X	int		xpp_expfunc;	/* expected function number */
X	int		xpp_flags;	/* initial softc flags */
X	const char	*xpp_name;	/* device name */
X} xe_pcmcia_products[] = {
X#ifdef NOT_SUPPORTED
X	{ PCMCIA_VENDOR_XIRCOM,		PCMCIA_PRODUCT_XIRCOM_CE,
X	  0,				0,
X	  PCMCIA_STR_XIRCOM_CE },
X#endif
X	{ PCMCIA_VENDOR_XIRCOM,		PCMCIA_PRODUCT_XIRCOM_CE2,
X	  0,				0,
X	  PCMCIA_STR_XIRCOM_CE2 },
X	{ PCMCIA_VENDOR_XIRCOM,		PCMCIA_PRODUCT_XIRCOM_CE3,
X	  0,				XE_FLAGS_MOHAWK,
X	  PCMCIA_STR_XIRCOM_CE3 },
X	{ PCMCIA_VENDOR_COMPAQ2,	PCMCIA_PRODUCT_COMPAQ2_CPQ_10_100,
X	  0,				XE_FLAGS_MOHAWK,
X	  PCMCIA_STR_COMPAQ2_CPQ_10_100 },
X	{ PCMCIA_VENDOR_INTEL,		PCMCIA_PRODUCT_INTEL_EEPRO100,
X	  0,				XE_FLAGS_MOHAWK | XE_FLAGS_MODEM,
X	  PCMCIA_STR_INTEL_EEPRO100 },
X	{ PCMCIA_VENDOR_XIRCOM,		PCMCIA_PRODUCT_XIRCOM_CEM,
X	  0,				XE_FLAGS_MODEM,
X	  PCMCIA_STR_XIRCOM_CEM },
X	{ PCMCIA_VENDOR_XIRCOM,		PCMCIA_PRODUCT_XIRCOM_CEM28,
X	  0,				XE_FLAGS_MODEM,
X	  PCMCIA_STR_XIRCOM_CEM28 },
X	{ 0,				0,
X	  0,				0,
X	  NULL },
X};
X
Xstruct xe_pcmcia_product *xe_pcmcia_lookup __P((struct pcmcia_attach_args *));
X
Xstruct xe_pcmcia_product *
Xxe_pcmcia_lookup(pa)
X        struct pcmcia_attach_args *pa;
X{
X	struct xe_pcmcia_product *xpp;
X
X	for (xpp = xe_pcmcia_products; xpp->xpp_name != NULL; xpp++)
X		if (pa->manufacturer == xpp->xpp_vendor &&
X			pa->product == xpp->xpp_product &&
X			pa->pf->number == xpp->xpp_expfunc)
X			return (xpp);
X	return (NULL);
X}
X
X/*
X * If someone can determine which manufacturers/products require cis_quirks,
X * then the proper infrastucture can be used.  Until then...
X * This also becomes a pain with detaching.
X */
Xstatic int
Xxe_pcmcia_cis_quirks(pf)
X	struct pcmcia_function *pf;
X{
X	struct pcmcia_config_entry *cfe;
X
X	/* Tell the pcmcia framework where the CCR is. */
X	pf->ccr_base = 0x800;
X	pf->ccr_mask = 0x67;
X
X	/* Fake a cfe. */
X	SIMPLEQ_FIRST(&pf->cfe_head) = cfe = (struct pcmcia_config_entry *)
X	    malloc(sizeof *cfe, M_DEVBUF, M_NOWAIT);
X
X	if (cfe == NULL)
X		return -1;
X	bzero(cfe, sizeof(*cfe));
X
X	/*
X	 * XXX Use preprocessor symbols instead.
X	 * Enable ethernet & its interrupts, wiring them to -INT
X	 * No I/O base.
X	 */
X	cfe->number = 0x5;
X	cfe->flags = 0;		/* XXX Check! */
X	cfe->iftype = PCMCIA_IFTYPE_IO;
X	cfe->num_iospace = 0;
X	cfe->num_memspace = 0;
X	cfe->irqmask = 0x8eb0;
X
X	return 0;
X}
X
Xint
Xxe_pcmcia_match(parent, match, aux)
X	struct device *parent;
X	struct cfdata *match;
X	void *aux;
X{
X	struct pcmcia_attach_args *pa = aux;
X	
X	if (pa->pf->function != PCMCIA_FUNCTION_NETWORK)
X		return (0);
X
X	if (xe_pcmcia_lookup(pa) != NULL)
X		return (1);
X	return (0);
X}
X
Xvoid
Xxe_pcmcia_attach(parent, self, aux)
X	struct device *parent, *self;
X	void *aux;
X{
X	struct xe_pcmcia_softc *psc = (struct xe_pcmcia_softc *)self;
X	struct xe_softc *sc = &psc->sc_xe;
X	struct pcmcia_attach_args *pa = aux;
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X	struct xe_pcmcia_product *xpp;
X
X	if (xe_pcmcia_cis_quirks(pa->pf) < 0) {
X		printf(": function enable failed\n");
X		return;
X	}
X
X	/* Enable the card */
X	psc->sc_pf = pa->pf;
X	pcmcia_function_init(psc->sc_pf, psc->sc_pf->cfe_head.sqh_first);
X	if (pcmcia_function_enable(psc->sc_pf)) {
X		printf(": function enable failed\n");
X		goto fail;
X	}
X	psc->sc_resource |= XE_RES_PCIC;
X
X	/* allocate/map ISA I/O space */
X	if (pcmcia_io_alloc(psc->sc_pf, 0, XE_IOSIZ, XE_IOSIZ,
X		&psc->sc_pcioh) != 0) {
X		printf(": i/o allocation failed\n");
X		goto fail;
X	}
X	if (pcmcia_io_map(psc->sc_pf, PCMCIA_WIDTH_IO16, 0, XE_IOSIZ,
X		&psc->sc_pcioh, &psc->sc_io_window)) {
X		printf(": can't map i/o space\n");
X		goto fail;
X	}
X	sc->sc_bst = psc->sc_pcioh.iot;
X	sc->sc_bsh = psc->sc_pcioh.ioh;
X	sc->sc_offset = 0;
X	psc->sc_resource |= XE_RES_IO;
X
X	xpp = xe_pcmcia_lookup(pa);
X	if (xpp == NULL)
X		panic("xe_pcmcia_attach: impossible");
X	sc->sc_flags = xpp->xpp_flags;
X
X	printf(": %s\n", xpp->xpp_name);
X
X	/*
X	 * Configuration as adviced by DINGO documentation.
X	 * Dingo has some extra configuration registers in the CCR space.
X	 */
X	if (sc->sc_flags & XE_FLAGS_DINGO) {
X		struct pcmcia_mem_handle pcmh;
X		int ccr_window;
X		bus_addr_t ccr_offset;
X
X		if (pcmcia_mem_alloc(psc->sc_pf, PCMCIA_CCR_SIZE_DINGO,
X			&pcmh)) {
X			DPRINTF(XED_CONFIG, ("bad mem alloc\n"));
X			goto fail;
X		}
X
X		if (pcmcia_mem_map(psc->sc_pf, PCMCIA_MEM_ATTR,
X			psc->sc_pf->ccr_base, PCMCIA_CCR_SIZE_DINGO,
X			&pcmh, &ccr_offset, &ccr_window)) {
X			DPRINTF(XED_CONFIG, ("bad mem map\n"));
X			pcmcia_mem_free(psc->sc_pf, &pcmh);
X			goto fail;
X		}
X
X		bus_space_write_1(pcmh.memt, pcmh.memh,
X		    ccr_offset + PCMCIA_CCR_DCOR0, PCMCIA_CCR_DCOR0_SFINT);
X		bus_space_write_1(pcmh.memt, pcmh.memh,
X		    ccr_offset + PCMCIA_CCR_DCOR1,
X		    PCMCIA_CCR_DCOR1_FORCE_LEVIREQ | PCMCIA_CCR_DCOR1_D6);
X		bus_space_write_1(pcmh.memt, pcmh.memh,
X		    ccr_offset + PCMCIA_CCR_DCOR2, 0);
X		bus_space_write_1(pcmh.memt, pcmh.memh,
X		    ccr_offset + PCMCIA_CCR_DCOR3, 0);
X		bus_space_write_1(pcmh.memt, pcmh.memh,
X		    ccr_offset + PCMCIA_CCR_DCOR4, 0);
X
X		/* We don't need them anymore and can free them (I think). */
X		pcmcia_mem_unmap(psc->sc_pf, ccr_window);
X		pcmcia_mem_free(psc->sc_pf, &pcmh);
X	}
X
X	/*
X	 * Try to get the ethernet address from FUNCE/LAN_NID tuple.
X	 */
X	xe_pcmcia_funce_enaddr(parent, sc->sc_enaddr);
X	if (!sc->sc_enaddr) {
X		printf("%s: unable to get ethernet address\n",
X			sc->sc_dev.dv_xname);
X		goto fail;
X	}
X
X	printf("%s: Ethernet address %s\n", sc->sc_dev.dv_xname,
X	    ether_sprintf(sc->sc_enaddr));
X
X	ifp = &sc->sc_ethercom.ec_if;
X	memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
X	ifp->if_softc = sc;
X	ifp->if_start = xe_start;
X	ifp->if_ioctl = xe_ioctl;
X	ifp->if_watchdog = xe_watchdog;
X	ifp->if_flags =
X	    IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX | IFF_MULTICAST;
X	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
X
X	/* Reset and initialize the card. */
X	xe_full_reset(sc);
X
X	/*
X	 * Initialize our media structures and probe the MII.
X	 */
X	sc->sc_mii.mii_ifp = ifp;
X	sc->sc_mii.mii_readreg = xe_mdi_read;
X	sc->sc_mii.mii_writereg = xe_mdi_write;
X	sc->sc_mii.mii_statchg = xe_statchg;
X	ifmedia_init(&sc->sc_mii.mii_media, 0, xe_mediachange, xe_mediastatus);
X	DPRINTF(XED_MII | XED_CONFIG,
X	    ("bmsr %x\n", xe_mdi_read(&sc->sc_dev, 0, 1)));
X	mii_attach(self, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
X		MII_OFFSET_ANY, 0);
X	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL)
X		ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO, 0,
X		    NULL);
X	ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
X
X	/*
X	 * Attach the interface.
X	 */
X	if_attach(ifp);
X	ether_ifattach(ifp, sc->sc_enaddr);
X	psc->sc_resource |= XE_RES_MI;
X
X#if NBPFILTER > 0
X	bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
X#endif	/* NBPFILTER > 0 */
X
X	/*
X	 * Reset and initialize the card again for DINGO (as found in Linux
X	 * driver).  Without this Dingo will get a watchdog timeout the first
X	 * time.  The ugly media tickling seems to be necessary for getting
X	 * autonegotiation to work too.
X	 */
X	if (sc->sc_flags & XE_FLAGS_DINGO) {
X		xe_full_reset(sc);
X		xe_init(sc);
X		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
X		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_NONE);
X		xe_stop(sc);
X	}
X
X	/* Establish the interrupt. */
X	psc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, xe_intr, sc);
X	if (psc->sc_ih == NULL) {
X		printf("%s: couldn't establish interrupt\n",
X			sc->sc_dev.dv_xname);
X		goto fail;
X	}
X
X	return;
X
Xfail:
X	if ((psc->sc_resource & XE_RES_IO) != 0) {
X		/* Unmap our i/o windows. */
X		pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window);
X                pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh);
X        }
X        psc->sc_resource &= ~XE_RES_IO;
X	if (psc->sc_resource & XE_RES_PCIC) {
X		pcmcia_function_disable(pa->pf);
X		psc->sc_resource &= ~XE_RES_PCIC;
X	}
X	free(SIMPLEQ_FIRST(&psc->sc_pf->cfe_head), M_DEVBUF);
X
X}
X
Xint
Xxe_pcmcia_detach(self, flags)
X     struct device *self;
X     int flags;
X{
X	struct xe_pcmcia_softc *psc = (struct xe_pcmcia_softc *)self;
X	struct xe_softc *sc = &psc->sc_xe;
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X
X	DPRINTF(XED_CONFIG, ("xe_pcmcia_detach()\n"));
X
X	if ((ifp->if_flags & IFF_RUNNING) == 0)
X		return -1;
X
X	pcmcia_function_disable(psc->sc_pf);
X	psc->sc_resource &= ~XE_RES_PCIC;
X	pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih);
X	ifp->if_flags &= ~IFF_RUNNING;
X	ifp->if_timer = 0;
X
X	if ((psc->sc_resource & XE_RES_MI) != 0) {
X		ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
X#if NBPFILTER > 0
X		bpfdetach(ifp);
X#endif
X		ether_ifdetach(ifp);
X		if_detach(ifp);
X		psc->sc_resource &= ~XE_RES_MI;
X	}
X
X	if ((psc->sc_resource & XE_RES_IO) != 0) {
X		/* Unmap our i/o windows. */
X		pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window);
X                pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh);
X        }
X        free(SIMPLEQ_FIRST(&psc->sc_pf->cfe_head), M_DEVBUF);
X	psc->sc_resource &= ~XE_RES_IO;
X
X	return 0;
X}
X
X
Xint
Xxe_pcmcia_activate(self, act)
X     struct device *self;
X     enum devact act;
X{
X	struct xe_pcmcia_softc *psc = (struct xe_pcmcia_softc *)self;
X	struct xe_softc *sc = &psc->sc_xe;
X	int s, rv=0;
X
X	DPRINTF(XED_CONFIG, ("xe_pcmcia_activate()\n"));
X
X	s = splnet();
X	switch (act) {
X	case DVACT_ACTIVATE:
X		rv = EOPNOTSUPP;
X		break;
X
X	case DVACT_DEACTIVATE:
X		if_deactivate(&sc->sc_ethercom.ec_if);
X		break;
X	}
X	splx(s);
X	return (rv);
X}
X
X
X/*
X * XXX These two functions might be OK to factor out into pcmcia.c since
X * if_sm_pcmcia.c uses similar ones.
X */
Xstatic int
Xxe_pcmcia_funce_enaddr(parent, myla)
X	struct device *parent;
X	u_int8_t *myla;
X{
X	/* XXX The Linux driver has more ways to do this in case of failure. */
X	return (pcmcia_scan_cis(parent, xe_pcmcia_lan_nid_ciscallback, myla));
X}
X
Xstatic int
Xxe_pcmcia_lan_nid_ciscallback(tuple, arg)
X	struct pcmcia_tuple *tuple;
X	void *arg;
X{
X	u_int8_t *myla = arg;
X	int i;
X
X	DPRINTF(XED_CONFIG, ("xe_pcmcia_lan_nid_ciscallback\n"));
X
X	if (tuple->code == PCMCIA_CISTPL_FUNCE) {
X		if (tuple->length < 2)
X			return (0);
X
X		switch (pcmcia_tuple_read_1(tuple, 0)) {
X		case PCMCIA_TPLFE_TYPE_LAN_NID:
X			if (pcmcia_tuple_read_1(tuple, 1) != ETHER_ADDR_LEN)
X				return (0);
X			break;
X
X		case 0x02:
X			/*
X			 * Not sure about this, I don't have a CE2
X			 * that puts the ethernet addr here.
X			 */
X		 	if (pcmcia_tuple_read_1(tuple, 1) != 13)
X				return (0);
X			break;
X
X		default:
X			return (0);
X		}
X
X		for (i = 0; i < ETHER_ADDR_LEN; i++)
X			myla[i] = pcmcia_tuple_read_1(tuple, i + 2);
X		return (1);
X	}
X
X	/* Yet another spot where this might be. */
X	if (tuple->code == 0x89) {
X		pcmcia_tuple_read_1(tuple, 1);
X		for (i = 0; i < ETHER_ADDR_LEN; i++)
X			myla[i] = pcmcia_tuple_read_1(tuple, i + 2);
X		return (1);
X	}
X	return (0);
X}
X
X
Xstatic int
Xxe_intr(arg)
X	void *arg;
X{
X	struct xe_softc *sc = arg;
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X	u_int8_t esr, rsr, isr, rx_status, savedpage;
X	u_int16_t tx_status, recvcount = 0, tempint;
X
X	DPRINTF(XED_CONFIG, ("xe_intr\n"));
X
X#if 0
X	if (!(ifp->if_flags & IFF_RUNNING))
X		return (0);
X#endif
X
X	ifp->if_timer = 0;	/* turn watchdog timer off */
X
X	if (sc->sc_flags & XE_FLAGS_MOHAWK) {
X		/* Disable interrupt (Linux does it). */
X		bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
X		    0);
X	}
X
X	savedpage =
X	    bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + PR);
X
X	PAGE(sc, 0);
X	esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + ESR);
X	isr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + ISR0);
X	rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RSR);
X				
X	/* Check to see if card has been ejected. */
X	if (isr == 0xff) {
X#ifdef DIAGNOSTIC
X		printf("%s: interrupt for dead card\n", sc->sc_dev.dv_xname);
X#endif
X		goto end;
X	}
X
X	PAGE(sc, 40);
X	rx_status =
X	    bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RXST0);
X	tx_status =
X	    bus_space_read_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + TXST0);
X
X	/*
X	 * XXX Linux writes to RXST0 and TXST* here.  My CE2 works just fine
X	 * without it, and I can't see an obvious reason for it.
X	 */
X
X	PAGE(sc, 0);
X	while (esr & FULL_PKT_RCV) {
X		if (!(rsr & RSR_RX_OK))
X			break;
X
X		/* Compare bytes read this interrupt to hard maximum. */
X		if (recvcount > MAX_BYTES_INTR) {
X			DPRINTF(XED_INTR,
X			    ("%s: too many bytes this interrupt\n",
X			    sc->sc_dev.dv_xname));
X			ifp->if_iqdrops++;
X			/* Drop packet. */
X			bus_space_write_2(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + DO0, DO_SKIP_RX_PKT);
X		}
X		tempint = xe_get(sc);	/* XXX doesn't check the error! */
X		recvcount += tempint;
X		ifp->if_ibytes += tempint;
X		esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
X		    sc->sc_offset + ESR);
X		rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
X		    sc->sc_offset + RSR);
X	}
X	
X	/* Packet too long? */
X	if (rsr & RSR_TOO_LONG) {
X		ifp->if_ierrors++;
X		DPRINTF(XED_INTR,
X		    ("%s: packet too long\n", sc->sc_dev.dv_xname));
X	}
X
X	/* CRC error? */
X	if (rsr & RSR_CRCERR) {
X		ifp->if_ierrors++;
X		DPRINTF(XED_INTR,
X		    ("%s: CRC error detected\n", sc->sc_dev.dv_xname));
X	}
X
X	/* Alignment error? */
X	if (rsr & RSR_ALIGNERR) {
X		ifp->if_ierrors++;
X		DPRINTF(XED_INTR,
X		    ("%s: alignment error detected\n", sc->sc_dev.dv_xname));
X	}
X
X	/* Check for rx overrun. */
X	if (rx_status & RX_OVERRUN) {
X		bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
X		    CLR_RX_OVERRUN);
X		DPRINTF(XED_INTR, ("overrun cleared\n"));
X	}
X			
X	/* Try to start more packets transmitting. */
X	if (ifp->if_snd.ifq_head)
X		xe_start(ifp);
X
X	/* Detected excessive collisions? */
X	if ((tx_status & EXCESSIVE_COLL) && ifp->if_opackets > 0) {
X		DPRINTF(XED_INTR,
X		    ("%s: excessive collisions\n", sc->sc_dev.dv_xname));
X		bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
X		    RESTART_TX);
X		ifp->if_oerrors++;
X	}
X	
X	if ((tx_status & TX_ABORT) && ifp->if_opackets > 0)
X		ifp->if_oerrors++;
X
Xend:
X	/* Reenable interrupts. */
X	PAGE(sc, savedpage);
X	bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR,
X	    ENABLE_INT);
X
X	return (1);
X}
X
X
X/*
X * Pull a packet from the card into an mbuf chain.
X */
Xstatic u_int16_t
Xxe_get(sc)
X	struct xe_softc *sc;
X{
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X	struct mbuf *top, **mp, *m;
X	u_int16_t pktlen, len, recvcount = 0;
X	u_int8_t *data;
X	u_int8_t rsr;
X	
X	DPRINTF(XED_CONFIG, ("xe_get\n"));
X
X	PAGE(sc, 0);
X	rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RSR);
X
X	pktlen =
X	    bus_space_read_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + RBC0) &
X	    RBC_COUNT_MASK;
X
X	DPRINTF(XED_CONFIG, ("xe_get: pktlen=%d\n", pktlen));
X
X	if (pktlen == 0) {
X		/*
X		 * XXX At least one CE2 sets RBC0 == 0 occasionally, and only
X		 * when MPE is set.  It is not known why.
X		 */
X		return (0);
X	}
X
X	/* XXX should this be incremented now ? */
X	recvcount += pktlen;
X
X	MGETHDR(m, M_DONTWAIT, MT_DATA);
X	if (m == 0)
X		return (recvcount);
X	m->m_pkthdr.rcvif = ifp;
X	m->m_pkthdr.len = pktlen;
X	len = MHLEN;
X	top = 0;
X	mp = &top;
X	
X	while (pktlen > 0) {
X		if (top) {
X			MGET(m, M_DONTWAIT, MT_DATA);
X			if (m == 0) {
X				m_freem(top);
X				return (recvcount);
X			}
X			len = MLEN;
X		}
X		if (pktlen >= MINCLSIZE) {
X			MCLGET(m, M_DONTWAIT);
X			if (!(m->m_flags & M_EXT)) {
X				m_freem(m);
X				m_freem(top);
X				return (recvcount);
X			}
X			len = MCLBYTES;
X		}
X		if (!top) {
X			caddr_t newdata = (caddr_t)ALIGN(m->m_data +
X			    sizeof (struct ether_header)) -
X			    sizeof (struct ether_header);
X			len -= newdata - m->m_data;
X			m->m_data = newdata;
X		}
X		len = min(pktlen, len);
X		data = mtod(m, u_int8_t *);
X		if (len > 1) {
X		        len &= ~1;
X			bus_space_read_multi_2(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + EDP, data, len>>1);
X		} else
X			*data = bus_space_read_1(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + EDP);
X		m->m_len = len;
X		pktlen -= len;
X		*mp = m;
X		mp = &m->m_next;
X	}
X
X	/* Skip Rx packet. */
X	bus_space_write_2(sc->sc_bst, sc->sc_bsh, sc->sc_offset + DO0,
X	    DO_SKIP_RX_PKT);
X	
X	ifp->if_ipackets++;
X	
X#if NBPFILTER > 0
X	if (ifp->if_bpf)
X		bpf_mtap(ifp->if_bpf, top);
X#endif
X	
X	(*ifp->if_input)(ifp, top);
X	return (recvcount);
X}
X
X
X/*
X * Serial management for the MII.
X * The DELAY's below stem from the fact that the maximum frequency
X * acceptable on the MDC pin is 2.5 MHz and fast processors can easily
X * go much faster than that.
X */
X
X/* Let the MII serial management be idle for one period. */
Xstatic INLINE void xe_mdi_idle __P((struct xe_softc *));
Xstatic INLINE void
Xxe_mdi_idle(sc)
X	struct xe_softc *sc;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X
X	/* Drive MDC low... */
X	bus_space_write_1(bst, bsh, offset + GP2, MDC_LOW);
X	DELAY(1);
X
X	/* and high again. */
X	bus_space_write_1(bst, bsh, offset + GP2, MDC_HIGH);
X	DELAY(1);
X}
X
X/* Pulse out one bit of data. */
Xstatic INLINE void xe_mdi_pulse __P((struct xe_softc *, int));
Xstatic INLINE void
Xxe_mdi_pulse(sc, data)
X	struct xe_softc *sc;
X	int data;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X	u_int8_t bit = data ? MDIO_HIGH : MDIO_LOW;
X
X	/* First latch the data bit MDIO with clock bit MDC low...*/
X	bus_space_write_1(bst, bsh, offset + GP2, bit | MDC_LOW);
X	DELAY(1);
X
X	/* then raise the clock again, preserving the data bit. */
X	bus_space_write_1(bst, bsh, offset + GP2, bit | MDC_HIGH);
X	DELAY(1);
X}
X
X/* Probe one bit of data. */
Xstatic INLINE int xe_mdi_probe __P((struct xe_softc *sc));
Xstatic INLINE int
Xxe_mdi_probe(sc)
X	struct xe_softc *sc;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X	u_int8_t x;
X
X	/* Pull clock bit MDCK low... */
X	bus_space_write_1(bst, bsh, offset + GP2, MDC_LOW);
X	DELAY(1);
X
X	/* Read data and drive clock high again. */
X	x = bus_space_read_1(bst, bsh, offset + GP2) & MDIO;
X	bus_space_write_1(bst, bsh, offset + GP2, MDC_HIGH);
X	DELAY(1);
X
X	return (x);
X}
X
X/* Pulse out a sequence of data bits. */
Xstatic INLINE void xe_mdi_pulse_bits __P((struct xe_softc *, u_int32_t, int));
Xstatic INLINE void
Xxe_mdi_pulse_bits(sc, data, len)
X	struct xe_softc *sc;
X	u_int32_t data;
X	int len;
X{
X	u_int32_t mask;
X
X	for (mask = 1 << (len - 1); mask; mask >>= 1)
X		xe_mdi_pulse (sc, data & mask);
X}
X
X/* Read a PHY register. */
Xstatic int
Xxe_mdi_read(self, phy, reg)
X	struct device *self;
X	int phy;
X	int reg;
X{
X	struct xe_softc *sc = (struct xe_softc *)self;
X	int i;
X	u_int32_t mask;
X	u_int32_t data = 0;
X
X	PAGE(sc, 2);
X	for (i = 0; i < 32; i++)	/* Synchronize. */
X		xe_mdi_pulse(sc, 1);
X	xe_mdi_pulse_bits(sc, 0x06, 4); /* Start + Read opcode */
X	xe_mdi_pulse_bits(sc, phy, 5);	/* PHY address */
X	xe_mdi_pulse_bits(sc, reg, 5);	/* PHY register */
X	xe_mdi_idle(sc);		/* Turn around. */
X	xe_mdi_probe(sc);		/* Drop initial zero bit. */
X
X	for (mask = 1 << 15; mask; mask >>= 1) {
X		if (xe_mdi_probe(sc))
X			data |= mask;
X	}
X	xe_mdi_idle(sc);
X
X	DPRINTF(XED_MII,
X	    ("xe_mdi_read: phy %d reg %d -> %x\n", phy, reg, data));
X
X	return (data);
X}
X
X/* Write a PHY register. */
Xstatic void
Xxe_mdi_write(self, phy, reg, value)
X	struct device *self;
X	int phy;
X	int reg;
X	int value;
X{
X	struct xe_softc *sc = (struct xe_softc *)self;
X	int i;
X
X	PAGE(sc, 2);
X	for (i = 0; i < 32; i++)	/* Synchronize. */
X		xe_mdi_pulse(sc, 1);
X	xe_mdi_pulse_bits(sc, 0x05, 4); /* Start + Write opcode */
X	xe_mdi_pulse_bits(sc, phy, 5);	/* PHY address */
X	xe_mdi_pulse_bits(sc, reg, 5);	/* PHY register */
X	xe_mdi_pulse_bits(sc, 0x02, 2); /* Turn around. */
X	xe_mdi_pulse_bits(sc, value, 16);	/* Write the data */
X	xe_mdi_idle(sc);		/* Idle away. */
X
X	DPRINTF(XED_MII,
X	    ("xe_mdi_write: phy %d reg %d val %x\n", phy, reg, value));
X}
X
Xstatic void
Xxe_statchg(self)
X	struct device *self;
X{
X	/* XXX Update ifp->if_baudrate */
X}
X
X/*
X * Change media according to request.
X */
Xstatic int
Xxe_mediachange(ifp)
X	struct ifnet *ifp;
X{
X	DPRINTF(XED_CONFIG, ("xe_mediachange\n"));
X
X	if (ifp->if_flags & IFF_UP)
X		xe_init(ifp->if_softc);
X	return (0);
X}
X
X/*
X * Notify the world which media we're using.
X */
Xstatic void
Xxe_mediastatus(ifp, ifmr)
X	struct ifnet *ifp;
X	struct ifmediareq *ifmr;
X{
X	struct xe_softc *sc = ifp->if_softc;
X
X	DPRINTF(XED_CONFIG, ("xe_mediastatus\n"));
X
X	mii_pollstat(&sc->sc_mii);
X	ifmr->ifm_status = sc->sc_mii.mii_media_status;
X	ifmr->ifm_active = sc->sc_mii.mii_media_active;
X}
X
Xstatic void
Xxe_reset(sc)
X	struct xe_softc *sc;
X{
X	int s;
X
X	DPRINTF(XED_CONFIG, ("xe_reset\n"));
X
X	s = splnet();
X	xe_stop(sc);
X	xe_full_reset(sc);
X	xe_init(sc);
X	splx(s);
X}
X
Xstatic void
Xxe_watchdog(ifp)
X	struct ifnet *ifp;
X{
X	struct xe_softc *sc = ifp->if_softc;
X
X	printf("%s: device timeout\n", sc->sc_dev.dv_xname);
X	++ifp->if_oerrors;
X
X	xe_reset(sc);
X}
X
Xstatic void
Xxe_stop(sc)
X	register struct xe_softc *sc;
X{
X	DPRINTF(XED_CONFIG, ("xe_stop\n"));
X
X	/* Disable interrupts. */
X	PAGE(sc, 0);
X	bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + CR, 0);
X
X	PAGE(sc, 1);
X	bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + IMR0, 0);
X	
X	/* Power down, wait. */
X	PAGE(sc, 4);
X	bus_space_write_1(sc->sc_bst, sc->sc_bsh, sc->sc_offset + GP1, 0);
X	DELAY(40000);
X	
X	/* Cancel watchdog timer. */
X	sc->sc_ethercom.ec_if.if_timer = 0;
X}
X
Xstatic void
Xxe_init(sc)
X	struct xe_softc *sc;
X{
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X	int s;
X
X	DPRINTF(XED_CONFIG, ("xe_init\n"));
X
X	s = splimp();
X
X	xe_set_address(sc);
X
X	/* Set current media. */
X	mii_mediachg(&sc->sc_mii);
X
X	ifp->if_flags |= IFF_RUNNING;
X	ifp->if_flags &= ~IFF_OACTIVE;
X	splx(s);
X}
X
X/*
X * Start outputting on the interface.
X * Always called as splnet().
X */
Xstatic void
Xxe_start(ifp)
X	struct ifnet *ifp;
X{
X	struct xe_softc *sc = ifp->if_softc;
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X	unsigned int s, len, pad = 0;
X	struct mbuf *m0, *m;
X	u_int16_t space;
X
X	DPRINTF(XED_CONFIG, ("xe_start\n"));
X
X	/* Don't transmit if interface is busy or not running. */
X	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) {
X		DPRINTF(XED_CONFIG, ("xe0: interface busy or not running\n"));
X		return;
X	}
X
X	/* Peek at the next packet. */
X	m0 = ifp->if_snd.ifq_head;
X	if (m0 == 0)
X		return;
X
X	/* We need to use m->m_pkthdr.len, so require the header. */
X	if (!(m0->m_flags & M_PKTHDR))
X		panic("xe_start: no header mbuf");
X
X	len = m0->m_pkthdr.len;
X
X	/* Pad to ETHER_MIN_LEN - ETHER_CRC_LEN. */
X	if (len < ETHER_MIN_LEN - ETHER_CRC_LEN)
X		pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len;
X
X	space = bus_space_read_2(bst, bsh, offset + TSO0) & 0x7fff;
X	if (len + pad + 2 > space) {
X		DPRINTF(XED_FIFO,
X		    ("%s: not enough space in output FIFO (%d > %d)\n",
X		    sc->sc_dev.dv_xname, len + pad + 2, space));
X		return;
X	}
X
X	IF_DEQUEUE(&ifp->if_snd, m0);
X
X#if NBPFILTER > 0
X	if (ifp->if_bpf)
X		bpf_mtap(ifp->if_bpf, m0);
X#endif
X
X	/*
X	 * Do the output at splhigh() so that an interrupt from another device
X	 * won't cause a FIFO underrun.
X	 */
X	s = splhigh();
X
X	PAGE(sc, 0);
X	bus_space_write_2(bst, bsh, offset + TSO2, (u_int16_t)len + pad + 2);
X	bus_space_write_2(bst, bsh, offset + EDP, (u_int16_t)len + pad);
X	for (m = m0; m; ) {
X		if (m->m_len > 1)
X			bus_space_write_multi_2(bst, bsh, offset + EDP,
X			    mtod(m, u_int8_t *), m->m_len>>1);
X		if (m->m_len & 1)
X			bus_space_write_1(bst, bsh, offset + EDP,
X			    *(mtod(m, u_int8_t *) + m->m_len - 1));
X		MFREE(m, m0);
X		m = m0;
X	}
X	if (sc->sc_flags & XE_FLAGS_MOHAWK)
X		bus_space_write_1(bst, bsh, offset + CR, TX_PKT | ENABLE_INT);
X	else {
X		for (; pad > 1; pad -= 2)
X			bus_space_write_2(bst, bsh, offset + EDP, 0);
X		if (pad == 1)
X			bus_space_write_1(bst, bsh, offset + EDP, 0);
X	}
X
X	splx(s);
X
X	ifp->if_timer = 5;
X	++ifp->if_opackets;
X}
X
Xstatic int
Xxe_ether_ioctl(ifp, cmd, data)
X	struct ifnet *ifp;
X	u_long cmd;
X	caddr_t data;
X{
X	struct ifaddr *ifa = (struct ifaddr *)data;
X	struct xe_softc *sc = ifp->if_softc;
X
X
X	DPRINTF(XED_CONFIG, ("xe_ether_ioctl\n"));
X
X	switch (cmd) {
X	case SIOCSIFADDR:
X		ifp->if_flags |= IFF_UP;
X
X		switch (ifa->ifa_addr->sa_family) {
X#ifdef INET
X		case AF_INET:
X			xe_init(sc);
X			arp_ifinit(ifp, ifa);
X			break;
X#endif	/* INET */
X
X#ifdef NS
X		case AF_NS:
X		{
X			struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
X
X			if (ns_nullhost(*ina))
X				ina->x_host = *(union ns_host *)
X					LLADDR(ifp->if_sadl);
X			else
X				bcopy(ina->x_host.c_host,
X					LLADDR(ifp->if_sadl),
X					ifp->if_addrlen);
X			/* Set new address. */
X			xe_init(sc);
X			break;
X		}
X#endif  /* NS */
X
X		default:
X			xe_init(sc);
X			break;
X		}
X		break;
X
X	default:
X		return (EINVAL);
X	}
X
X	return (0);
X}
X
Xstatic int
Xxe_ioctl(ifp, command, data)
X	struct ifnet *ifp;
X	u_long command;
X	caddr_t data;
X{
X	struct xe_softc *sc = ifp->if_softc;
X	struct ifreq *ifr = (struct ifreq *)data;
X	int s, error = 0;
X
X	DPRINTF(XED_CONFIG, ("xe_ioctl\n"));
X
X	s = splimp();
X
X	switch (command) {
X	case SIOCSIFADDR:
X		error = xe_ether_ioctl(ifp, command, data);
X		break;
X
X	case SIOCSIFFLAGS:
X		sc->sc_all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
X				
X		PAGE(sc, 0x42);
X		if ((ifp->if_flags & IFF_PROMISC) ||
X		    (ifp->if_flags & IFF_ALLMULTI))
X			bus_space_write_1(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + SWC1,
X			    SWC1_PROMISC | SWC1_MCAST_PROM);
X		else
X			bus_space_write_1(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + SWC1, 0);
X
X		/*
X		 * If interface is marked up and not running, then start it.
X		 * If it is marked down and running, stop it.
X		 * XXX If it's up then re-initialize it. This is so flags
X		 * such as IFF_PROMISC are handled.
X		 */
X		if (ifp->if_flags & IFF_UP) {
X			xe_full_reset(sc);
X			xe_init(sc);
X		} else {
X			if (ifp->if_flags & IFF_RUNNING)
X				xe_stop(sc);
X		}
X		break;
X
X	case SIOCADDMULTI:
X	case SIOCDELMULTI:
X		sc->sc_all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;
X		error = (command == SIOCADDMULTI) ?
X		    ether_addmulti(ifr, &sc->sc_ethercom) :
X		    ether_delmulti(ifr, &sc->sc_ethercom);
X
X		if (error == ENETRESET) {
X			/*
X			 * Multicast list has changed; set the hardware
X			 * filter accordingly.
X			 */
X			if (!sc->sc_all_mcasts &&
X			    !(ifp->if_flags & IFF_PROMISC))
X				xe_set_address(sc);
X
X			/*
X			 * xe_set_address() can turn on all_mcasts if we run
X			 * out of space, so check it again rather than else {}.
X			 */
X			if (sc->sc_all_mcasts)
X				xe_init(sc);
X			error = 0;
X		}
X		break;
X
X	case SIOCSIFMEDIA:
X	case SIOCGIFMEDIA:
X		error =
X		    ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);
X		break;
X
X	default:
X		error = EINVAL;
X	}
X	splx(s);
X	return (error);
X}
X
Xstatic void
Xxe_set_address(sc)
X	struct xe_softc *sc;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X	struct ethercom *ether = &sc->sc_ethercom;
X	struct ether_multi *enm;
X	struct ether_multistep step;
X	struct ifnet *ifp = &sc->sc_ethercom.ec_if;
X	int i, page, pos, num;
X
X	DPRINTF(XED_CONFIG, ("xe_set_address\n"));
X
X	PAGE(sc, 0x50);
X	for (i = 0; i < 6; i++) {
X		bus_space_write_1(bst, bsh, offset + IA + i,
X		    sc->sc_enaddr[(sc->sc_flags & XE_FLAGS_MOHAWK) ?  5 - i : i]);
X	}
X		
X	if (ether->ec_multicnt > 0) {
X		if (ether->ec_multicnt > 9) {
X			PAGE(sc, 0x42);
X			bus_space_write_1(sc->sc_bst, sc->sc_bsh,
X			    sc->sc_offset + SWC1,
X			    SWC1_PROMISC | SWC1_MCAST_PROM);
X			return;
X		}
X
X		ETHER_FIRST_MULTI(step, ether, enm);
X
X		pos = IA + 6;
X		for (page = 0x50, num = ether->ec_multicnt; num > 0 && enm;
X		    num--) {
X			if (bcmp(enm->enm_addrlo, enm->enm_addrhi,
X			    sizeof (enm->enm_addrlo)) != 0) {
X				/*
X				 * The multicast address is really a range;
X				 * it's easier just to accept all multicasts.
X				 * XXX should we be setting IFF_ALLMULTI here?
X				 */
X				ifp->if_flags |= IFF_ALLMULTI;
X				sc->sc_all_mcasts=1;
X				break;
X			}
X
X			for (i = 0; i < 6; i++) {
X				bus_space_write_1(bst, bsh, offset + pos,
X				    enm->enm_addrlo[
X				    (sc->sc_flags & XE_FLAGS_MOHAWK) ? 5 - i : i]);
X
X				if (++pos > 15) {
X					pos = IA;
X					page++;
X					PAGE(sc, page);
X				}
X			}
X		}
X	}
X}
X
Xstatic void
Xxe_cycle_power (sc)
X	struct xe_softc *sc;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X
X	DPRINTF(XED_CONFIG, ("xe_cycle_power\n"));
X
X	PAGE(sc, 4);
X	DELAY(1);
X	bus_space_write_1(bst, bsh, offset + GP1, 0);
X	DELAY(40000);
X	if (sc->sc_flags & XE_FLAGS_MOHAWK)
X		bus_space_write_1(bst, bsh, offset + GP1, POWER_UP);
X	else
X		/* XXX What is bit 2 (aka AIC)? */
X		bus_space_write_1(bst, bsh, offset + GP1, POWER_UP | 4);
X	DELAY(20000);
X}
X
Xstatic void
Xxe_full_reset (sc)
X	struct xe_softc *sc;
X{
X	bus_space_tag_t bst = sc->sc_bst;
X	bus_space_handle_t bsh = sc->sc_bsh;
X	bus_addr_t offset = sc->sc_offset;
X
X	DPRINTF(XED_CONFIG, ("xe_full_reset\n"));
X
X	/* Do an as extensive reset as possible on all functions. */
X	xe_cycle_power(sc);
X	bus_space_write_1(bst, bsh, offset + CR, SOFT_RESET);
X	DELAY(20000);
X	bus_space_write_1(bst, bsh, offset + CR, 0);
X	DELAY(20000);
X	if (sc->sc_flags & XE_FLAGS_MOHAWK) {
X		PAGE(sc, 4);
X		/*
X		 * Drive GP1 low to power up ML6692 and GP2 high to power up
X		 * the 10Mhz chip.  XXX What chip is that?  The phy?
X		 */
X		bus_space_write_1(bst, bsh, offset + GP0,
X		    GP1_OUT | GP2_OUT | GP2_WR);
X	}
X	DELAY(500000);
X
X	/* Get revision information.  XXX Symbolic constants. */
X	sc->sc_rev = bus_space_read_1(bst, bsh, offset + BV) &
X	    ((sc->sc_flags & XE_FLAGS_MOHAWK) ? 0x70 : 0x30) >> 4;
X
X	/* Media selection.  XXX Maybe manual overriding too? */
X	if (!(sc->sc_flags & XE_FLAGS_MOHAWK)) {
X		PAGE(sc, 4);
X		/*
X		 * XXX I have no idea what this really does, it is from the
X		 * Linux driver.
X		 */
X		bus_space_write_1(bst, bsh, offset + GP0, GP1_OUT);
X	}
X	DELAY(40000);
X
X	/* Setup the ethernet interrupt mask. */
X	PAGE(sc, 1);
X	bus_space_write_1(bst, bsh, offset + IMR0,
X	    ISR_TX_OFLOW | ISR_PKT_TX | ISR_MAC_INT | /* ISR_RX_EARLY | */
X	    ISR_RX_FULL | ISR_RX_PKT_REJ | ISR_FORCED_INT);
X#if 0
X	bus_space_write_1(bst, bsh, offset + IMR0, 0xff);
X#endif
X	if (!(sc->sc_flags & XE_FLAGS_DINGO)) {
X		/* XXX What is this?  Not for Dingo at least. */
X		bus_space_write_1(bst, bsh, offset + IMR1, 1);
X	}
X
X	/*
X	 * Disable source insertion.
X	 * XXX Dingo does not have this bit, but Linux does it unconditionally.
X	 */
X	if (!(sc->sc_flags & XE_FLAGS_DINGO)) {
X		PAGE(sc, 0x42);
X		bus_space_write_1(bst, bsh, offset + SWC0, 0x20);
X	}
X
X	/* Set the local memory dividing line. */
X	if (sc->sc_rev != 1) {
X		PAGE(sc, 2);
X		/* XXX Symbolic constant preferrable. */
X		bus_space_write_2(bst, bsh, offset + RBS0, 0x2000);
X	}
X
X	xe_set_address(sc);
X
X	/*
X	 * Apparently the receive byte pointer can be bad after a reset, so
X	 * we hardwire it correctly.
X	 */
X	PAGE(sc, 0);
X	bus_space_write_2(bst, bsh, offset + DO0, DO_CHG_OFFSET);
X
X	/* Setup ethernet MAC registers. XXX Symbolic constants. */
X	PAGE(sc, 0x40);
X	bus_space_write_1(bst, bsh, offset + RX0MSK,
X	    PKT_TOO_LONG | CRC_ERR | RX_OVERRUN | RX_ABORT | RX_OK);
X	bus_space_write_1(bst, bsh, offset + TX0MSK,
X	    CARRIER_LOST | EXCESSIVE_COLL | TX_UNDERRUN | LATE_COLLISION |
X	    SQE | TX_ABORT | TX_OK);
X	if (!(sc->sc_flags & XE_FLAGS_DINGO))
X		/* XXX From Linux, dunno what 0xb0 means. */
X		bus_space_write_1(bst, bsh, offset + TX1MSK, 0xb0);
X	bus_space_write_1(bst, bsh, offset + RXST0, 0);
X	bus_space_write_1(bst, bsh, offset + TXST0, 0);
X	bus_space_write_1(bst, bsh, offset + TXST1, 0);
X
X	/* Enable MII function if available. */
X	if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
X		PAGE(sc, 2);
X		bus_space_write_1(bst, bsh, offset + MSR,
X		    bus_space_read_1(bst, bsh, offset + MSR) | SELECT_MII);
X		DELAY(20000);
X	} else {
X		PAGE(sc, 0);
X				
X		/* XXX Do we need to do this? */
X		PAGE(sc, 0x42);
X		bus_space_write_1(bst, bsh, offset + SWC1, SWC1_AUTO_MEDIA);
X		DELAY(50000);
X
X		/* XXX Linux probes the media here. */
X	}
X
X	/* Configure the LED registers. */
X	PAGE(sc, 2);
X
X	/* XXX This is not good for 10base2. */
X	bus_space_write_1(bst, bsh, offset + LED,
X	    LED_TX_ACT << LED1_SHIFT | LED_10MB_LINK << LED0_SHIFT);
X	if (sc->sc_flags & XE_FLAGS_DINGO)
X		bus_space_write_1(bst, bsh, offset + LED3,
X		    LED_100MB_LINK << LED3_SHIFT);
X
X	/* Enable receiver and go online. */
X	PAGE(sc, 0x40);
X	bus_space_write_1(bst, bsh, offset + CMD0, ENABLE_RX | ONLINE);
X
X#if 0
X	/* XXX Linux does this here - is it necessary? */
X	PAGE(sc, 1);
X	bus_space_write_1(bst, bsh, offset + IMR0, 0xff);
X	if (!(sc->sc_flags & XE_FLAGS_DINGO)) {
X		/* XXX What is this?  Not for Dingo at least. */
X		bus_space_write_1(bst, bsh, offset + IMR1, 1);
X	}
X#endif
X
X       /* Enable interrupts. */
X	PAGE(sc, 0);
X	bus_space_write_1(bst, bsh, offset + CR, ENABLE_INT);
X
X	/* XXX This is pure magic for me, found in the Linux driver. */
X	if ((sc->sc_flags & (XE_FLAGS_DINGO | XE_FLAGS_MODEM)) == XE_FLAGS_MODEM) {
X		if ((bus_space_read_1(bst, bsh, offset + 0x10) & 0x01) == 0)
X			/* Unmask the master interrupt bit. */
X			bus_space_write_1(bst, bsh, offset + 0x10, 0x11);
X	}
X
X	/*
X	 * The Linux driver says this:
X	 * We should switch back to page 0 to avoid a bug in revision 0
X	 * where regs with offset below 8 can't be read after an access
X	 * to the MAC registers.
X	 */
X	PAGE(sc, 0);
X}
END-of-if_xe.c
echo x - xediff
sed 's/^X//' >xediff << 'END-of-xediff'
XIndex: if_xe.c
X===================================================================
XRCS file: /u3/cvsroot/src/sys/dev/pcmcia/if_xe.c,v
Xretrieving revision 1.2
Xretrieving revision 1.3
Xdiff -c -r1.2 -r1.3
X*** if_xe.c	2000/04/05 01:16:37	1.2
X***************
X*** 193,199 ****
X  	struct device sc_dev;			/* Generic device info */
X  	struct ethercom sc_ethercom;		/* Ethernet common part */
X  
X! 	struct ifmedia sc_media;		/* Media control */
X  	struct mii_data sc_mii;			/* MII media information */
X  
X  	bus_space_tag_t		sc_bst;		/* Bus cookie */
X--- 193,199 ----
X  	struct device sc_dev;			/* Generic device info */
X  	struct ethercom sc_ethercom;		/* Ethernet common part */
X  
X! /*	struct ifmedia sc_media;*/		/* Media control */
X  	struct mii_data sc_mii;			/* MII media information */
X  
X  	bus_space_tag_t		sc_bst;		/* Bus cookie */
X***************
X*** 549,556 ****
X  
X  	DPRINTF(XED_CONFIG, ("xe_pcmcia_detach()\n"));
X  
X! 	if ((ifp->if_flags & IFF_RUNNING) == 0)
X! 		return -1;
X  
X  	pcmcia_function_disable(psc->sc_pf);
X  	psc->sc_resource &= ~XE_RES_PCIC;
X--- 549,558 ----
X  
X  	DPRINTF(XED_CONFIG, ("xe_pcmcia_detach()\n"));
X  
X! 	if ((ifp->if_flags & IFF_RUNNING) != 0) {
X! 	    xe_stop(sc);
X! /*	    return -1;*/
X! 	}
X  
X  	pcmcia_function_disable(psc->sc_pf);
X  	psc->sc_resource &= ~XE_RES_PCIC;
X***************
X*** 558,565 ****
X  	ifp->if_flags &= ~IFF_RUNNING;
X  	ifp->if_timer = 0;
X  
X  	if ((psc->sc_resource & XE_RES_MI) != 0) {
X! 		ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
X  #if NBPFILTER > 0
X  		bpfdetach(ifp);
X  #endif
X--- 560,574 ----
X  	ifp->if_flags &= ~IFF_RUNNING;
X  	ifp->if_timer = 0;
X  
X+ 
X  	if ((psc->sc_resource & XE_RES_MI) != 0) {
X! 	    /* Detach all PHYs */
X! 	    mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
X! 
X! 	    /* Delete all remaining media. */
X! 	    ifmedia_delete_instance(&sc->sc_mii.mii_media, IFM_INST_ANY);
X! 
X! /*		ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);*/
X  #if NBPFILTER > 0
X  		bpfdetach(ifp);
X  #endif
X***************
X*** 1332,1339 ****
X  			xe_full_reset(sc);
X  			xe_init(sc);
X  		} else {
X! 			if (ifp->if_flags & IFF_RUNNING)
X  				xe_stop(sc);
X  		}
X  		break;
X  
X--- 1341,1350 ----
X  			xe_full_reset(sc);
X  			xe_init(sc);
X  		} else {
X! 		    if (ifp->if_flags & IFF_RUNNING) {
X  				xe_stop(sc);
X+ 				ifp->if_flags &= ~IFF_RUNNING;
X+ 		    }
X  		}
X  		break;
X  
END-of-xediff
exit


>Release-Note:
>Audit-Trail:
>Unformatted: