if_cs.c revision 121816
137785Smsmith/*
237785Smsmith * Copyright (c) 1997,1998 Maxim Bolotin and Oleg Sharoiko.
337785Smsmith * All rights reserved.
437785Smsmith *
537785Smsmith * Redistribution and use in source and binary forms, with or without
637785Smsmith * modification, are permitted provided that the following conditions
737785Smsmith * are met:
837785Smsmith * 1. Redistributions of source code must retain the above copyright
937785Smsmith *    notice unmodified, this list of conditions, and the following
1037785Smsmith *    disclaimer.
1137785Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1237785Smsmith *    notice, this list of conditions and the following disclaimer in the
1337785Smsmith *    documentation and/or other materials provided with the distribution.
1437785Smsmith *
1537785Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1637785Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1737785Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1837785Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1937785Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2037785Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2137785Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2237785Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2337785Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2437785Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2537785Smsmith * SUCH DAMAGE.
2637785Smsmith *
2737785Smsmith */
2837785Smsmith
29119418Sobrien#include <sys/cdefs.h>
30119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/cs/if_cs.c 121816 2003-10-31 18:32:15Z brooks $");
31119418Sobrien
3237785Smsmith/*
3337785Smsmith *
3437785Smsmith * Device driver for Crystal Semiconductor CS8920 based ethernet
3537785Smsmith *   adapters. By Maxim Bolotin and Oleg Sharoiko, 27-April-1997
3637785Smsmith */
3737785Smsmith
3858816Simp/*
3958816Simp#define	 CS_DEBUG
4058816Simp */
4137785Smsmith
4237785Smsmith#include <sys/param.h>
4337785Smsmith#include <sys/systm.h>
4437785Smsmith#include <sys/malloc.h>
4558816Simp#include <sys/mbuf.h>
4658816Simp#include <sys/socket.h>
4737785Smsmith#include <sys/sockio.h>
4837785Smsmith#include <sys/kernel.h>
4938592Smsmith#include <sys/sysctl.h>
5037785Smsmith#include <sys/syslog.h>
5137785Smsmith
5258816Simp#include <sys/module.h>
5358816Simp#include <sys/bus.h>
5458816Simp#include <machine/bus.h>
5558816Simp#include <sys/rman.h>
5658816Simp#include <machine/resource.h>
5758816Simp
5837785Smsmith#include <net/if.h>
5937785Smsmith#include <net/if_arp.h>
6037785Smsmith#include <net/if_media.h>
6137785Smsmith#include <net/ethernet.h>
6237785Smsmith#include <net/bpf.h>
6337785Smsmith
6472940Simp#include <dev/cs/if_csvar.h>
6558816Simp#include <dev/cs/if_csreg.h>
6637785Smsmith
6737785Smsmith#ifdef  CS_USE_64K_DMA
6837785Smsmith#define CS_DMA_BUFFER_SIZE 65536
6937785Smsmith#else
7037785Smsmith#define CS_DMA_BUFFER_SIZE 16384
7137785Smsmith#endif
7237785Smsmith
7338592Smsmithstatic int	cs_recv_delay = 570;
7438592SmsmithSYSCTL_INT(_machdep, OID_AUTO, cs_recv_delay, CTLFLAG_RW, &cs_recv_delay, 0, "");
7538592Smsmith
7671316Simpstatic void	cs_init		(void *);
7771316Simpstatic int	cs_ioctl	(struct ifnet *, u_long, caddr_t);
7871316Simpstatic void	cs_start	(struct ifnet *);
7971316Simpstatic void	cs_stop		(struct cs_softc *);
8071316Simpstatic void	cs_reset	(struct cs_softc *);
8171316Simpstatic void	cs_watchdog	(struct ifnet *);
8258816Simp
8371316Simpstatic int	cs_mediachange	(struct ifnet *);
8471316Simpstatic void	cs_mediastatus	(struct ifnet *, struct ifmediareq *);
8571316Simpstatic int      cs_mediaset	(struct cs_softc *, int);
8658816Simp
8737785Smsmithstatic void	cs_write_mbufs(struct cs_softc*, struct mbuf*);
8837785Smsmithstatic void	cs_xmit_buf(struct cs_softc*);
8937785Smsmithstatic int	cs_get_packet(struct cs_softc*);
9037785Smsmithstatic void	cs_setmode(struct cs_softc*);
9137785Smsmith
9237785Smsmithstatic int	get_eeprom_data(struct cs_softc *sc, int, int, int *);
9337785Smsmithstatic int	get_eeprom_cksum(int, int, int *);
9437785Smsmithstatic int	wait_eeprom_ready( struct cs_softc *);
9537785Smsmithstatic void	control_dc_dc( struct cs_softc *, int );
9637785Smsmithstatic int	send_test_pkt( struct cs_softc * );
9737785Smsmithstatic int	enable_tp(struct cs_softc *);
9837785Smsmithstatic int	enable_aui(struct cs_softc *);
9937785Smsmithstatic int	enable_bnc(struct cs_softc *);
10037785Smsmithstatic int      cs_duplex_auto(struct cs_softc *);
10137785Smsmith
10271316Simpdevclass_t cs_devclass;
10337785Smsmith
10437785Smsmithstatic int
10537785Smsmithget_eeprom_data( struct cs_softc *sc, int off, int len, int *buffer)
10637785Smsmith{
10737785Smsmith	int i;
10837785Smsmith
10937785Smsmith#ifdef CS_DEBUG
11037785Smsmith	printf(CS_NAME":EEPROM data from %x for %x:\n", off,len);
11137785Smsmith#endif
11237785Smsmith
11337785Smsmith	for (i=0;i<len;i++) {
11437785Smsmith		if (wait_eeprom_ready(sc) < 0) return -1;
11537785Smsmith		/* Send command to EEPROM to read */
11672940Simp		cs_writereg(sc, PP_EECMD, (off + i) | EEPROM_READ_CMD);
11737785Smsmith		if (wait_eeprom_ready(sc)<0)
11872940Simp			return (-1);
11972940Simp		buffer[i] = cs_readreg(sc, PP_EEData);
12037785Smsmith
12137785Smsmith#ifdef CS_DEBUG
12237785Smsmith		printf("%02x %02x ",(unsigned char)buffer[i],
12337785Smsmith					(unsigned char)buffer[i+1]);
12437785Smsmith#endif
12537785Smsmith	}
12637785Smsmith
12737785Smsmith#ifdef CS_DEBUG
12837785Smsmith	printf("\n");
12937785Smsmith#endif
13072940Simp	return (0);
13137785Smsmith}
13237785Smsmith
13337785Smsmithstatic int
13437785Smsmithget_eeprom_cksum(int off, int len, int *buffer)
13537785Smsmith{
13637785Smsmith	int i,cksum=0;
13737785Smsmith
13837785Smsmith	for (i=0;i<len;i++)
13937785Smsmith		cksum+=buffer[i];
14037785Smsmith	cksum &= 0xffff;
14137785Smsmith	if (cksum==0)
14237785Smsmith		return 0;
14337785Smsmith	return -1;
14437785Smsmith}
14537785Smsmith
14637785Smsmithstatic int
14737785Smsmithwait_eeprom_ready(struct cs_softc *sc)
14837785Smsmith{
14937785Smsmith	DELAY ( 30000 );	/* XXX should we do some checks here ? */
15037785Smsmith	return 0;
15137785Smsmith}
15237785Smsmith
15337785Smsmithstatic void
15437785Smsmithcontrol_dc_dc(struct cs_softc *sc, int on_not_off)
15537785Smsmith{
15637785Smsmith	unsigned int self_control = HCB1_ENBL;
15737785Smsmith
15837785Smsmith	if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0) ^ on_not_off)
15937785Smsmith		self_control |= HCB1;
16037785Smsmith	else
16137785Smsmith		self_control &= ~HCB1;
16272940Simp	cs_writereg(sc, PP_SelfCTL, self_control);
16337785Smsmith
16437785Smsmith	DELAY( 500000 );
16537785Smsmith}
16637785Smsmith
16737785Smsmith
16837785Smsmithstatic int
16937785Smsmithcs_duplex_auto(struct cs_softc *sc)
17037785Smsmith{
171104252Sbrooks        int i, error=0;
17237785Smsmith
17372940Simp	cs_writereg(sc, PP_AutoNegCTL,
17472940Simp	    RE_NEG_NOW | ALLOW_FDX | AUTO_NEG_ENABLE);
17572940Simp        for (i=0; cs_readreg(sc, PP_AutoNegST) & AUTO_NEG_BUSY; i++) {
17637785Smsmith                if (i > 40000) {
177104252Sbrooks                        if_printf(&sc->arpcom.ac_if,
178104252Sbrooks                        	"full/half duplex auto negotiation timeout\n");
17937785Smsmith			error = ETIMEDOUT;
18037785Smsmith                        break;
18137785Smsmith                }
18237785Smsmith                DELAY(1000);
18337785Smsmith        }
18437785Smsmith        DELAY( 1000000 );
18537785Smsmith	return error;
18637785Smsmith}
18737785Smsmith
18837785Smsmithstatic int
18937785Smsmithenable_tp(struct cs_softc *sc)
19037785Smsmith{
19137785Smsmith
19272940Simp	cs_writereg(sc, PP_LineCTL, sc->line_ctl & ~AUI_ONLY);
19337785Smsmith	control_dc_dc(sc, 0);
19437785Smsmith	DELAY( 150000 );
19537785Smsmith
19672940Simp	if ((cs_readreg(sc, PP_LineST) & LINK_OK)==0) {
197104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable TP\n");
19837785Smsmith                return EINVAL;
19937785Smsmith	}
20037785Smsmith
20137785Smsmith	return 0;
20237785Smsmith}
20337785Smsmith
20437785Smsmith/*
20537785Smsmith * XXX This was rewritten from Linux driver without any tests.
20637785Smsmith */
20737785Smsmithstatic int
20837785Smsmithsend_test_pkt(struct cs_softc *sc)
20937785Smsmith{
21037785Smsmith	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
21137785Smsmith				0, 46,  /* A 46 in network order */
21237785Smsmith				0, 0,   /* DSAP=0 & SSAP=0 fields */
21337785Smsmith				0xf3, 0 /* Control (Test Req + P bit set) */ };
21438305Smsmith	int i;
21538305Smsmith	u_char ether_address_backup[ETHER_ADDR_LEN];
21637785Smsmith
21738305Smsmith	for (i = 0; i < ETHER_ADDR_LEN; i++) {
21838305Smsmith		ether_address_backup[i] = sc->arpcom.ac_enaddr[i];
21938305Smsmith	}
22038305Smsmith
22172940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) | SERIAL_TX_ON);
22272940Simp	bcopy(test_packet, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
22372940Simp	bcopy(test_packet+ETHER_ADDR_LEN,
22472940Simp	    sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
22572940Simp	cs_outw(sc, TX_CMD_PORT, sc->send_cmd);
22672940Simp	cs_outw(sc, TX_LEN_PORT, sizeof(test_packet));
22737785Smsmith
22837785Smsmith	/* Wait for chip to allocate memory */
22937785Smsmith	DELAY(50000);
23072940Simp	if (!(cs_readreg(sc, PP_BusST) & READY_FOR_TX_NOW)) {
23138305Smsmith		for (i = 0; i < ETHER_ADDR_LEN; i++) {
23238305Smsmith			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
23338305Smsmith		}
23437785Smsmith		return 0;
23538305Smsmith	}
23637785Smsmith
23737785Smsmith	outsw(sc->nic_addr + TX_FRAME_PORT, test_packet, sizeof(test_packet));
23837785Smsmith
23937785Smsmith	DELAY(30000);
24037785Smsmith
24172940Simp	if ((cs_readreg(sc, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
24238305Smsmith		for (i = 0; i < ETHER_ADDR_LEN; i++) {
24338305Smsmith			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
24438305Smsmith		}
24537785Smsmith		return 1;
24638305Smsmith	}
24738305Smsmith	for (i = 0; i < ETHER_ADDR_LEN; i++) {
24838305Smsmith		sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
24938305Smsmith	}
25037785Smsmith	return 0;
25137785Smsmith}
25237785Smsmith
25337785Smsmith/*
25437785Smsmith * XXX This was rewritten from Linux driver without any tests.
25537785Smsmith */
25637785Smsmithstatic int
25737785Smsmithenable_aui(struct cs_softc *sc)
25837785Smsmith{
25937785Smsmith
26037785Smsmith	control_dc_dc(sc, 0);
26172940Simp	cs_writereg(sc, PP_LineCTL,
26272940Simp	    (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
26337785Smsmith
26437785Smsmith	if (!send_test_pkt(sc)) {
265104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable AUI\n");
26637785Smsmith		return EINVAL;
26737785Smsmith        }
26837785Smsmith        return 0;
26937785Smsmith}
27037785Smsmith
27137785Smsmith/*
27237785Smsmith * XXX This was rewritten from Linux driver without any tests.
27337785Smsmith */
27437785Smsmithstatic int
27537785Smsmithenable_bnc(struct cs_softc *sc)
27637785Smsmith{
27737785Smsmith
27837785Smsmith	control_dc_dc(sc, 1);
27972940Simp	cs_writereg(sc, PP_LineCTL,
28072940Simp	    (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
28137785Smsmith
28237785Smsmith	if (!send_test_pkt(sc)) {
283104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable BNC\n");
28437785Smsmith		return EINVAL;
28537785Smsmith        }
28637785Smsmith        return 0;
28737785Smsmith}
28837785Smsmith
28971316Simpint
29058816Simpcs_cs89x0_probe(device_t dev)
29137785Smsmith{
29258816Simp	int i;
29358816Simp	int error;
29458816Simp	u_long irq, junk;
29558816Simp	struct cs_softc *sc = device_get_softc(dev);
29637785Smsmith	unsigned rev_type = 0;
29772940Simp	u_int16_t id;
29858816Simp	char chip_revision;
29937785Smsmith	int eeprom_buff[CHKSUM_LEN];
30037785Smsmith	int chip_type, pp_isaint, pp_isadma;
30137785Smsmith
30258816Simp	error = cs_alloc_port(dev, 0, CS_89x0_IO_PORTS);
30358816Simp	if (error)
30458816Simp		return (error);
30558816Simp
30672940Simp	sc->nic_addr = rman_get_start(sc->port_res);
30758816Simp
30872940Simp	if ((cs_inw(sc, ADD_PORT) & ADD_MASK) != ADD_SIG) {
30937785Smsmith		/* Chip not detected. Let's try to reset it */
31037785Smsmith		if (bootverbose)
31158816Simp			device_printf(dev, "trying to reset the chip.\n");
31272940Simp		cs_outw(sc, ADD_PORT, PP_SelfCTL);
31372940Simp		i = cs_inw(sc, DATA_PORT);
31472940Simp		cs_outw(sc, ADD_PORT, PP_SelfCTL);
31572940Simp		cs_outw(sc, DATA_PORT, i | POWER_ON_RESET);
31672940Simp		if ((cs_inw(sc, ADD_PORT) & ADD_MASK) != ADD_SIG)
31758816Simp			return (ENXIO);
31837785Smsmith	}
31937785Smsmith
32072940Simp	for (i = 0; i < 10000; i++) {
32172940Simp		id = cs_readreg(sc, PP_ChipID);
32272940Simp		if (id == CHIP_EISA_ID_SIG)
32372940Simp			break;
32472940Simp	}
32572940Simp	if (i == 10000)
32658816Simp		return (ENXIO);
32737785Smsmith
32872940Simp	rev_type = cs_readreg(sc, PRODUCT_ID_ADD);
32937785Smsmith	chip_type = rev_type & ~REVISON_BITS;
33037785Smsmith	chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
33137785Smsmith
33237785Smsmith	sc->chip_type = chip_type;
33358816Simp
33437785Smsmith	if(chip_type==CS8900) {
33537785Smsmith		pp_isaint = PP_CS8900_ISAINT;
33637785Smsmith		pp_isadma = PP_CS8900_ISADMA;
33737785Smsmith		sc->send_cmd = TX_CS8900_AFTER_ALL;
33837785Smsmith	} else {
33937785Smsmith		pp_isaint = PP_CS8920_ISAINT;
34037785Smsmith		pp_isadma = PP_CS8920_ISADMA;
34137785Smsmith		sc->send_cmd = TX_CS8920_AFTER_ALL;
34237785Smsmith	}
34337785Smsmith
34437785Smsmith        /*
34537785Smsmith         * Clear some fields so that fail of EEPROM will left them clean
34637785Smsmith         */
34737785Smsmith        sc->auto_neg_cnf = 0;
34837785Smsmith        sc->adapter_cnf  = 0;
34937785Smsmith        sc->isa_config   = 0;
35037785Smsmith
35137785Smsmith	/*
35258816Simp	 * If no interrupt specified (or "?"), use what the board tells us.
35337785Smsmith	 */
35458816Simp	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
35558816Simp
35658816Simp	/*
35758816Simp	 * Get data from EEPROM
35858816Simp	 */
35972940Simp	if((cs_readreg(sc, PP_SelfST) & EEPROM_PRESENT) == 0) {
36058816Simp		device_printf(dev, "No EEPROM, assuming defaults.\n");
36137785Smsmith	} else {
36237785Smsmith		if (get_eeprom_data(sc,START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
36358816Simp			device_printf(dev, "EEPROM read failed, "
36458816Simp				"assuming defaults.\n");
36537785Smsmith		} else {
36637785Smsmith			if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
36758816Simp				device_printf(dev, "EEPROM cheksum bad, "
36858816Simp					"assuming defaults.\n");
36937785Smsmith			} else {
37037785Smsmith                                sc->auto_neg_cnf =
37137785Smsmith                                        eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
37237785Smsmith                                sc->adapter_cnf =
37337785Smsmith                                        eeprom_buff[ADAPTER_CNF_OFFSET/2];
37437785Smsmith                                sc->isa_config =
37537785Smsmith                                        eeprom_buff[ISA_CNF_OFFSET/2];
37637785Smsmith
37737785Smsmith                                for (i=0; i<ETHER_ADDR_LEN/2; i++) {
37837785Smsmith                                        sc->arpcom.ac_enaddr[i*2]=
37937785Smsmith                                                eeprom_buff[i];
38037785Smsmith                                        sc->arpcom.ac_enaddr[i*2+1]=
38137785Smsmith                                                eeprom_buff[i] >> 8;
38237785Smsmith                                }
38337785Smsmith
38437785Smsmith                                /*
38537785Smsmith                                 * If no interrupt specified (or "?"),
38637785Smsmith                                 * use what the board tells us.
38737785Smsmith                                 */
38858816Simp				if (error) {
38958816Simp					irq = sc->isa_config & INT_NO_MASK;
39037785Smsmith                                        if (chip_type==CS8900) {
39158816Simp                                                switch(irq) {
39258816Simp						 case 0:
39358816Simp							irq=10;
39458816Simp							error=0;
39558816Simp							break;
39658816Simp						 case 1:
39758816Simp							irq=11;
39858816Simp							error=0;
39958816Simp							break;
40058816Simp						 case 2:
40158816Simp							irq=12;
40258816Simp							error=0;
40358816Simp							break;
40458816Simp						 case 3:
40558816Simp							irq=5;
40658816Simp							error=0;
40758816Simp							break;
40858816Simp						 default:
40958816Simp							device_printf(dev, "invalid irq in EEPROM.\n");
41058816Simp							error=EINVAL;
41158816Simp                                                }
41258816Simp					} else {
41358816Simp						if (irq>CS8920_NO_INTS) {
41458816Simp							device_printf(dev, "invalid irq in EEPROM.\n");
41558816Simp							error=EINVAL;
41658816Simp						} else {
41758816Simp							error=0;
41837785Smsmith						}
41937785Smsmith					}
42058816Simp
42158816Simp					if (!error)
42258816Simp						bus_set_resource(dev, SYS_RES_IRQ, 0,
42358816Simp								irq, 1);
42458816Simp				}
42537785Smsmith			}
42637785Smsmith                }
42737785Smsmith        }
42837785Smsmith
42958816Simp	if (!error) {
43037785Smsmith                if (chip_type == CS8900) {
43137785Smsmith			switch(irq) {
43258816Simp				case  5:
43358816Simp					irq = 3;
43458816Simp					break;
43558816Simp				case 10:
43658816Simp					irq = 0;
43758816Simp					break;
43858816Simp				case 11:
43958816Simp					irq = 1;
44058816Simp					break;
44158816Simp				case 12:
44258816Simp					irq = 2;
44358816Simp					break;
44458816Simp				default:
44558816Simp					error=EINVAL;
44637785Smsmith			}
44737785Smsmith                } else {
44837785Smsmith                        if (irq > CS8920_NO_INTS) {
44958816Simp                                error = EINVAL;
45037785Smsmith                        }
45137785Smsmith                }
45258816Simp	}
45358816Simp
45458816Simp	if (!error) {
45572940Simp                cs_writereg(sc, pp_isaint, irq);
45637785Smsmith	} else {
45758816Simp	       	device_printf(dev, "Unknown or invalid irq\n");
45858816Simp                return (ENXIO);
45937785Smsmith        }
46037785Smsmith
46137785Smsmith        /*
46237785Smsmith         * Temporary disabled
46337785Smsmith         *
46437785Smsmith        if (drq>0)
46572940Simp		cs_writereg(sc, pp_isadma, drq);
46637785Smsmith	else {
467104252Sbrooks		device_printf(dev, "incorrect drq\n",);
46837785Smsmith		return 0;
46937785Smsmith	}
47037785Smsmith        */
47137785Smsmith
47237785Smsmith	if (bootverbose)
47358816Simp		 device_printf(dev, "CS89%c0%s rev %c media%s%s%s\n",
47437785Smsmith			chip_type==CS8900 ? '0' : '2',
47537785Smsmith			chip_type==CS8920M ? "M" : "",
47637785Smsmith			chip_revision,
47737785Smsmith			(sc->adapter_cnf & A_CNF_10B_T) ? " TP"  : "",
47837785Smsmith			(sc->adapter_cnf & A_CNF_AUI)   ? " AUI" : "",
47958816Simp			(sc->adapter_cnf & A_CNF_10B_2) ? " BNC" : "");
48037785Smsmith
48137785Smsmith        if ((sc->adapter_cnf & A_CNF_EXTND_10B_2) &&
48237785Smsmith            (sc->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
48337785Smsmith                sc->line_ctl = LOW_RX_SQUELCH;
48437785Smsmith        else
48537785Smsmith                sc->line_ctl = 0;
48637785Smsmith
48737785Smsmith
48858816Simp	return 0;
48937785Smsmith}
49037785Smsmith
49137785Smsmith/*
49258816Simp * Allocate a port resource with the given resource id.
49358816Simp */
49458816Simpint cs_alloc_port(device_t dev, int rid, int size)
49558816Simp{
49658816Simp        struct cs_softc *sc = device_get_softc(dev);
49758816Simp        struct resource *res;
49858816Simp
49958816Simp        res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
50058816Simp                                 0ul, ~0ul, size, RF_ACTIVE);
50158816Simp        if (res) {
50258816Simp                sc->port_rid = rid;
50358816Simp                sc->port_res = res;
50458816Simp                sc->port_used = size;
50558816Simp                return (0);
50658816Simp        } else {
50758816Simp                return (ENOENT);
50858816Simp        }
50958816Simp}
51058816Simp
51158816Simp/*
51258816Simp * Allocate a memory resource with the given resource id.
51358816Simp */
51458816Simpint cs_alloc_memory(device_t dev, int rid, int size)
51558816Simp{
51658816Simp        struct cs_softc *sc = device_get_softc(dev);
51758816Simp        struct resource *res;
51858816Simp
51958816Simp        res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
52058816Simp                                 0ul, ~0ul, size, RF_ACTIVE);
52158816Simp        if (res) {
52258816Simp                sc->mem_rid = rid;
52358816Simp                sc->mem_res = res;
52458816Simp                sc->mem_used = size;
52558816Simp                return (0);
52658816Simp        } else {
52758816Simp                return (ENOENT);
52858816Simp        }
52958816Simp}
53058816Simp
53158816Simp/*
53258816Simp * Allocate an irq resource with the given resource id.
53358816Simp */
53458816Simpint cs_alloc_irq(device_t dev, int rid, int flags)
53558816Simp{
53658816Simp        struct cs_softc *sc = device_get_softc(dev);
53758816Simp        struct resource *res;
53858816Simp
53958816Simp        res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
54058816Simp                                 0ul, ~0ul, 1, (RF_ACTIVE | flags));
54158816Simp        if (res) {
54258816Simp                sc->irq_rid = rid;
54358816Simp                sc->irq_res = res;
54458816Simp                return (0);
54558816Simp        } else {
54658816Simp                return (ENOENT);
54758816Simp        }
54858816Simp}
54958816Simp
55058816Simp/*
55158816Simp * Release all resources
55258816Simp */
55358816Simpvoid cs_release_resources(device_t dev)
55458816Simp{
55558816Simp        struct cs_softc *sc = device_get_softc(dev);
55658816Simp
55758816Simp        if (sc->port_res) {
55858816Simp                bus_release_resource(dev, SYS_RES_IOPORT,
55958816Simp                                     sc->port_rid, sc->port_res);
56058816Simp                sc->port_res = 0;
56158816Simp        }
56258816Simp        if (sc->mem_res) {
56358816Simp                bus_release_resource(dev, SYS_RES_MEMORY,
56458816Simp                                     sc->mem_rid, sc->mem_res);
56558816Simp                sc->mem_res = 0;
56658816Simp        }
56758816Simp        if (sc->irq_res) {
56858816Simp                bus_release_resource(dev, SYS_RES_IRQ,
56958816Simp                                     sc->irq_rid, sc->irq_res);
57058816Simp                sc->irq_res = 0;
57158816Simp        }
57258816Simp}
57358816Simp
57458816Simp/*
57537785Smsmith * Install the interface into kernel networking data structures
57637785Smsmith */
57771316Simpint
578121816Sbrookscs_attach(device_t dev)
57937785Smsmith{
58037785Smsmith        int media=0;
581121816Sbrooks	struct cs_softc *sc = device_get_softc(dev);;
58237785Smsmith	struct ifnet *ifp = &(sc->arpcom.ac_if);
58337785Smsmith
58458816Simp	cs_stop( sc );
58558816Simp
586121752Sbrooks	ifp->if_softc=sc;
587121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
588121752Sbrooks	ifp->if_output=ether_output;
589121752Sbrooks	ifp->if_start=cs_start;
590121752Sbrooks	ifp->if_ioctl=cs_ioctl;
591121752Sbrooks	ifp->if_watchdog=cs_watchdog;
592121752Sbrooks	ifp->if_init=cs_init;
593121752Sbrooks	ifp->if_snd.ifq_maxlen= IFQ_MAXLEN;
594121752Sbrooks	/*
595121752Sbrooks	 *  MIB DATA
596121752Sbrooks	 */
597121752Sbrooks	/*
598121752Sbrooks	ifp->if_linkmib=&sc->mibdata;
599121752Sbrooks	ifp->if_linkmiblen=sizeof sc->mibdata;
600121752Sbrooks	*/
60137785Smsmith
602121752Sbrooks	ifp->if_flags=(IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST );
60337785Smsmith
604121752Sbrooks	/*
605121752Sbrooks	 * this code still in progress (DMA support)
606121752Sbrooks	 *
60737785Smsmith
608121752Sbrooks	sc->recv_ring=malloc(CS_DMA_BUFFER_SIZE<<1, M_DEVBUF, M_NOWAIT);
609121752Sbrooks	if (sc->recv_ring == NULL) {
610121816Sbrooks		log(LOG_ERR,
611121816Sbrooks		"%s: Couldn't allocate memory for NIC\n", ifp->if_xname);
612121752Sbrooks		return(0);
613121752Sbrooks	}
614121752Sbrooks	if ((sc->recv_ring-(sc->recv_ring & 0x1FFFF))
615121752Sbrooks	    < (128*1024-CS_DMA_BUFFER_SIZE))
616121752Sbrooks	    sc->recv_ring+=16*1024;
61737785Smsmith
618121752Sbrooks	*/
61937785Smsmith
620121752Sbrooks	sc->buffer=malloc(ETHER_MAX_LEN-ETHER_CRC_LEN,M_DEVBUF,M_NOWAIT);
621121752Sbrooks	if (sc->buffer == NULL) {
622121752Sbrooks		if_printf(ifp, "Couldn't allocate memory for NIC\n");
623121752Sbrooks		return(0);
624121752Sbrooks	}
62537785Smsmith
626121752Sbrooks	/*
627121752Sbrooks	 * Initialize the media structures.
628121752Sbrooks	 */
629121752Sbrooks	ifmedia_init(&sc->media, 0, cs_mediachange, cs_mediastatus);
63037785Smsmith
631121752Sbrooks	if (sc->adapter_cnf & A_CNF_10B_T) {
632121752Sbrooks		ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T, 0, NULL);
633121752Sbrooks		if (sc->chip_type != CS8900) {
634121752Sbrooks			ifmedia_add(&sc->media,
635121752Sbrooks				IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
636121752Sbrooks			ifmedia_add(&sc->media,
637121752Sbrooks				IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
638121752Sbrooks		}
639121752Sbrooks	}
64037785Smsmith
641121752Sbrooks	if (sc->adapter_cnf & A_CNF_10B_2)
642121752Sbrooks		ifmedia_add(&sc->media, IFM_ETHER|IFM_10_2, 0, NULL);
64337785Smsmith
644121752Sbrooks	if (sc->adapter_cnf & A_CNF_AUI)
645121752Sbrooks		ifmedia_add(&sc->media, IFM_ETHER|IFM_10_5, 0, NULL);
64637785Smsmith
647121752Sbrooks	if (sc->adapter_cnf & A_CNF_MEDIA)
648121752Sbrooks		ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
64937785Smsmith
650121752Sbrooks	/* Set default media from EEPROM */
651121752Sbrooks	switch (sc->adapter_cnf & A_CNF_MEDIA_TYPE) {
652121752Sbrooks	case A_CNF_MEDIA_AUTO:  media = IFM_ETHER|IFM_AUTO; break;
653121752Sbrooks	case A_CNF_MEDIA_10B_T: media = IFM_ETHER|IFM_10_T; break;
654121752Sbrooks	case A_CNF_MEDIA_10B_2: media = IFM_ETHER|IFM_10_2; break;
655121752Sbrooks	case A_CNF_MEDIA_AUI:   media = IFM_ETHER|IFM_10_5; break;
656121752Sbrooks	default: if_printf(ifp, "adapter has no media\n");
65737785Smsmith	}
658121752Sbrooks	ifmedia_set(&sc->media, media);
659121752Sbrooks	cs_mediaset(sc, media);
66037785Smsmith
661121752Sbrooks	ether_ifattach(ifp, sc->arpcom.ac_enaddr);
662121752Sbrooks
66337785Smsmith	if (bootverbose)
664104252Sbrooks		if_printf(ifp, "ethernet address %6D\n",
665104252Sbrooks		       sc->arpcom.ac_enaddr, ":");
66637785Smsmith
66758816Simp	return (0);
66837785Smsmith}
66937785Smsmith
67037785Smsmith/*
67137785Smsmith * Initialize the board
67237785Smsmith */
67337785Smsmithstatic void
67437785Smsmithcs_init(void *xsc)
67537785Smsmith{
67637785Smsmith	struct cs_softc *sc=(struct cs_softc *)xsc;
67737785Smsmith	struct ifnet *ifp = &sc->arpcom.ac_if;
67841591Sarchie	int i, s, rx_cfg;
67937785Smsmith
68037785Smsmith	/* address not known */
68137785Smsmith	if (TAILQ_EMPTY(&ifp->if_addrhead)) /* unlikely? XXX */
68237785Smsmith		return;
68337785Smsmith
68437785Smsmith	/*
68537785Smsmith	 * reset whatchdog timer
68637785Smsmith	 */
68737785Smsmith	ifp->if_timer=0;
68837785Smsmith	sc->buf_len = 0;
68937785Smsmith
69037785Smsmith	s=splimp();
69137785Smsmith
69237785Smsmith	/*
69337785Smsmith	 * Hardware initialization of cs
69437785Smsmith	 */
69537785Smsmith
69637785Smsmith	/* Enable receiver and transmitter */
69772940Simp	cs_writereg(sc, PP_LineCTL,
69872940Simp		cs_readreg(sc, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
69937785Smsmith
70037785Smsmith	/* Configure the receiver mode */
70137785Smsmith	cs_setmode(sc);
70237785Smsmith
70337785Smsmith	/*
70437785Smsmith	 * This defines what type of frames will cause interrupts
70537785Smsmith	 * Bad frames should generate interrupts so that the driver
70637785Smsmith	 * could track statistics of discarded packets
70737785Smsmith	 */
70837785Smsmith        rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL | RX_RUNT_ENBL |
70937785Smsmith		 RX_EXTRA_DATA_ENBL;
71037785Smsmith	if (sc->isa_config & STREAM_TRANSFER)
71137785Smsmith		rx_cfg |= RX_STREAM_ENBL;
71272940Simp	cs_writereg(sc, PP_RxCFG, rx_cfg);
71372940Simp	cs_writereg(sc, PP_TxCFG, TX_LOST_CRS_ENBL |
71437785Smsmith		    TX_SQE_ERROR_ENBL | TX_OK_ENBL | TX_LATE_COL_ENBL |
71537785Smsmith		    TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
71672940Simp	cs_writereg(sc, PP_BufCFG, READY_FOR_TX_ENBL |
71737785Smsmith		    RX_MISS_COUNT_OVRFLOW_ENBL | TX_COL_COUNT_OVRFLOW_ENBL |
71837785Smsmith		    TX_UNDERRUN_ENBL /*| RX_DMA_ENBL*/);
71937785Smsmith
72037785Smsmith        /* Write MAC address into IA filter */
72137785Smsmith        for (i=0; i<ETHER_ADDR_LEN/2; i++)
72272940Simp                cs_writereg(sc, PP_IA + i * 2,
72372940Simp		    sc->arpcom.ac_enaddr[i * 2] |
72472940Simp		    (sc->arpcom.ac_enaddr[i * 2 + 1] << 8) );
72537785Smsmith
72637785Smsmith	/*
72737785Smsmith	 * Now enable everything
72837785Smsmith	 */
72937785Smsmith/*
73037785Smsmith#ifdef	CS_USE_64K_DMA
73172940Simp	cs_writereg(sc, PP_BusCTL, ENABLE_IRQ | RX_DMA_SIZE_64K);
73272940Simp#else
73372940Simp        cs_writereg(sc, PP_BusCTL, ENABLE_IRQ);
73437785Smsmith#endif
73537785Smsmith*/
73672940Simp	cs_writereg(sc, PP_BusCTL, ENABLE_IRQ);
73737785Smsmith
73837785Smsmith	/*
73937785Smsmith	 * Set running and clear output active flags
74037785Smsmith	 */
74137785Smsmith	sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
74237785Smsmith	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
74337785Smsmith
74437785Smsmith	/*
74537785Smsmith	 * Start sending process
74637785Smsmith	 */
74737785Smsmith	cs_start(ifp);
74837785Smsmith
74937785Smsmith	(void) splx(s);
75037785Smsmith}
75137785Smsmith
75237785Smsmith/*
753106937Ssam * Get the packet from the board and send it to the upper layer.
75437785Smsmith */
75537785Smsmithstatic int
75637785Smsmithcs_get_packet(struct cs_softc *sc)
75737785Smsmith{
75837785Smsmith	struct ifnet *ifp = &(sc->arpcom.ac_if);
75937785Smsmith	int iobase = sc->nic_addr, status, length;
76037785Smsmith	struct ether_header *eh;
76137785Smsmith	struct mbuf *m;
76237785Smsmith
76337785Smsmith#ifdef CS_DEBUG
76437785Smsmith	int i;
76537785Smsmith#endif
76637785Smsmith
76772940Simp	status = cs_inw(sc, RX_FRAME_PORT);
76872940Simp	length = cs_inw(sc, RX_FRAME_PORT);
76937785Smsmith
77037785Smsmith#ifdef CS_DEBUG
771104252Sbrooks	if_printf(ifp, "rcvd: stat %x, len %d\n",
772104252Sbrooks		status, length);
77337785Smsmith#endif
77437785Smsmith
77537785Smsmith	if (!(status & RX_OK)) {
77637785Smsmith#ifdef CS_DEBUG
777104252Sbrooks		if_printf(ifp, "bad pkt stat %x\n", status);
77837785Smsmith#endif
77937785Smsmith		ifp->if_ierrors++;
78037785Smsmith		return -1;
78137785Smsmith	}
78237785Smsmith
783111119Simp	MGETHDR(m, M_DONTWAIT, MT_DATA);
78437785Smsmith	if (m==NULL)
78537785Smsmith		return -1;
78637785Smsmith
78737785Smsmith	if (length > MHLEN) {
788111119Simp		MCLGET(m, M_DONTWAIT);
78937785Smsmith		if (!(m->m_flags & M_EXT)) {
79037785Smsmith			m_freem(m);
79137785Smsmith			return -1;
79237785Smsmith		}
79337785Smsmith	}
79437785Smsmith
79537785Smsmith	/* Initialize packet's header info */
79637785Smsmith	m->m_pkthdr.rcvif = ifp;
79737785Smsmith	m->m_pkthdr.len = length;
79837785Smsmith	m->m_len = length;
79937785Smsmith
80037785Smsmith	/* Get the data */
80137785Smsmith	insw(iobase + RX_FRAME_PORT, m->m_data, (length+1)>>1);
80237785Smsmith
80337785Smsmith	eh = mtod(m, struct ether_header *);
80437785Smsmith
80537785Smsmith#ifdef CS_DEBUG
80637785Smsmith	for (i=0;i<length;i++)
80737785Smsmith	     printf(" %02x",(unsigned char)*((char *)(m->m_data+i)));
80837785Smsmith	printf( "\n" );
80937785Smsmith#endif
81037785Smsmith
81137785Smsmith	if (status & (RX_IA | RX_BROADCAST) ||
81237785Smsmith	    (ifp->if_flags & IFF_MULTICAST && status & RX_HASHED)) {
81337785Smsmith		/* Feed the packet to the upper layer */
814106937Ssam		(*ifp->if_input)(ifp, m);
81537785Smsmith
81637785Smsmith		ifp->if_ipackets++;
81737785Smsmith
81837785Smsmith		if (length==ETHER_MAX_LEN-ETHER_CRC_LEN)
81938592Smsmith                        DELAY( cs_recv_delay );
82037785Smsmith	} else {
82137785Smsmith		m_freem(m);
82237785Smsmith	}
82337785Smsmith
82437785Smsmith	return 0;
82537785Smsmith}
82637785Smsmith
82737785Smsmith/*
82858816Simp * Handle interrupts
82937785Smsmith */
83058816Simpvoid
83158816Simpcsintr(void *arg)
83237785Smsmith{
83358816Simp	struct cs_softc *sc = (struct cs_softc*) arg;
83437785Smsmith	struct ifnet *ifp = &(sc->arpcom.ac_if);
83541591Sarchie	int status;
83637785Smsmith
83737785Smsmith#ifdef CS_DEBUG
838104252Sbrooks	if_printf(ifp, "Interrupt.\n");
83937785Smsmith#endif
84037785Smsmith
84172940Simp	while ((status=cs_inw(sc, ISQ_PORT))) {
84237785Smsmith
84337785Smsmith#ifdef CS_DEBUG
844104252Sbrooks		if_printf(ifp, "from ISQ: %04x\n", status);
84537785Smsmith#endif
84637785Smsmith
84737785Smsmith		switch (status & ISQ_EVENT_MASK) {
84837785Smsmith                case ISQ_RECEIVER_EVENT:
84937785Smsmith                        cs_get_packet(sc);
85037785Smsmith                        break;
85137785Smsmith
85237785Smsmith                case ISQ_TRANSMITTER_EVENT:
85337785Smsmith                        if (status & TX_OK)
85437785Smsmith                                ifp->if_opackets++;
85537785Smsmith                        else
85637785Smsmith                                ifp->if_oerrors++;
85737785Smsmith                        ifp->if_flags &= ~IFF_OACTIVE;
85837785Smsmith                        ifp->if_timer = 0;
85937785Smsmith                        break;
86037785Smsmith
86137785Smsmith                case ISQ_BUFFER_EVENT:
86237785Smsmith                        if (status & READY_FOR_TX) {
86337785Smsmith                                ifp->if_flags &= ~IFF_OACTIVE;
86437785Smsmith                                ifp->if_timer = 0;
86537785Smsmith                        }
86637785Smsmith
86737785Smsmith                        if (status & TX_UNDERRUN) {
86837785Smsmith                                ifp->if_flags &= ~IFF_OACTIVE;
86937785Smsmith                                ifp->if_timer = 0;
87037785Smsmith                                ifp->if_oerrors++;
87137785Smsmith                        }
87237785Smsmith                        break;
87337785Smsmith
87437785Smsmith                case ISQ_RX_MISS_EVENT:
87537785Smsmith                        ifp->if_ierrors+=(status>>6);
87637785Smsmith                        break;
87737785Smsmith
87837785Smsmith                case ISQ_TX_COL_EVENT:
87937785Smsmith                        ifp->if_collisions+=(status>>6);
88037785Smsmith                        break;
88137785Smsmith                }
88237785Smsmith        }
88337785Smsmith
88437785Smsmith        if (!(ifp->if_flags & IFF_OACTIVE)) {
88537785Smsmith                cs_start(ifp);
88637785Smsmith        }
88737785Smsmith}
88837785Smsmith
88937785Smsmith/*
89037785Smsmith * Save the data in buffer
89137785Smsmith */
89237785Smsmith
89337785Smsmithstatic void
89437785Smsmithcs_write_mbufs( struct cs_softc *sc, struct mbuf *m )
89537785Smsmith{
89637785Smsmith	int len;
89737785Smsmith	struct mbuf *mp;
89837785Smsmith	unsigned char *data, *buf;
89937785Smsmith
90037785Smsmith	for (mp=m, buf=sc->buffer, sc->buf_len=0; mp != NULL; mp=mp->m_next) {
90137785Smsmith		len = mp->m_len;
90237785Smsmith
90337785Smsmith		/*
90437785Smsmith		 * Ignore empty parts
90537785Smsmith		 */
90637785Smsmith		if (!len)
90737785Smsmith		continue;
90837785Smsmith
90937785Smsmith		/*
91037785Smsmith		 * Find actual data address
91137785Smsmith		 */
91237785Smsmith		data = mtod(mp, caddr_t);
91337785Smsmith
91437785Smsmith		bcopy((caddr_t) data, (caddr_t) buf, len);
91537785Smsmith		buf += len;
91637785Smsmith		sc->buf_len += len;
91737785Smsmith	}
91837785Smsmith}
91937785Smsmith
92037785Smsmith
92137785Smsmithstatic void
92237785Smsmithcs_xmit_buf( struct cs_softc *sc )
92337785Smsmith{
92437785Smsmith	outsw(sc->nic_addr+TX_FRAME_PORT, sc->buffer, (sc->buf_len+1)>>1);
92537785Smsmith	sc->buf_len = 0;
92637785Smsmith}
92737785Smsmith
92837785Smsmithstatic void
92937785Smsmithcs_start(struct ifnet *ifp)
93037785Smsmith{
93137785Smsmith	int s, length;
93237785Smsmith	struct mbuf *m, *mp;
93337785Smsmith	struct cs_softc *sc = ifp->if_softc;
93437785Smsmith
93537785Smsmith	s = splimp();
93637785Smsmith
93737785Smsmith	for (;;) {
93837785Smsmith		if (sc->buf_len)
93937785Smsmith			length = sc->buf_len;
94037785Smsmith		else {
94137785Smsmith			IF_DEQUEUE( &ifp->if_snd, m );
94237785Smsmith
94337785Smsmith			if (m==NULL) {
94437785Smsmith				(void) splx(s);
94537785Smsmith				return;
94637785Smsmith			}
94737785Smsmith
94837785Smsmith			for (length=0, mp=m; mp != NULL; mp=mp->m_next)
94937785Smsmith				length += mp->m_len;
95037785Smsmith
95137785Smsmith			/* Skip zero-length packets */
95237785Smsmith			if (length == 0) {
95337785Smsmith				m_freem(m);
95437785Smsmith				continue;
95537785Smsmith			}
95637785Smsmith
95737785Smsmith			cs_write_mbufs(sc, m);
95837785Smsmith
959106937Ssam			BPF_MTAP(ifp, m);
96037785Smsmith
96137785Smsmith			m_freem(m);
96237785Smsmith		}
96337785Smsmith
96437785Smsmith		/*
96537785Smsmith		 * Issue a SEND command
96637785Smsmith		 */
96772940Simp		cs_outw(sc, TX_CMD_PORT, sc->send_cmd);
96872940Simp		cs_outw(sc, TX_LEN_PORT, length );
96937785Smsmith
97037785Smsmith		/*
97137785Smsmith		 * If there's no free space in the buffer then leave
97237785Smsmith		 * this packet for the next time: indicate output active
97337785Smsmith		 * and return.
97437785Smsmith		 */
97572940Simp		if (!(cs_readreg(sc, PP_BusST) & READY_FOR_TX_NOW)) {
97637785Smsmith			ifp->if_timer = sc->buf_len;
97737785Smsmith			(void) splx(s);
97837785Smsmith			ifp->if_flags |= IFF_OACTIVE;
97937785Smsmith			return;
98037785Smsmith		}
98137785Smsmith
98237785Smsmith               	cs_xmit_buf(sc);
98337785Smsmith
98437785Smsmith		/*
98537785Smsmith		 * Set the watchdog timer in case we never hear
98637785Smsmith		 * from board again. (I don't know about correct
98737785Smsmith		 * value for this timeout)
98837785Smsmith		 */
98937785Smsmith		ifp->if_timer = length;
99037785Smsmith
99137785Smsmith		(void) splx(s);
99237785Smsmith		ifp->if_flags |= IFF_OACTIVE;
99337785Smsmith		return;
99437785Smsmith	}
99537785Smsmith}
99637785Smsmith
99737785Smsmith/*
99837785Smsmith * Stop everything on the interface
99937785Smsmith */
100037785Smsmithstatic void
100137785Smsmithcs_stop(struct cs_softc *sc)
100237785Smsmith{
100337785Smsmith	int s = splimp();
100437785Smsmith
100572940Simp	cs_writereg(sc, PP_RxCFG, 0);
100672940Simp	cs_writereg(sc, PP_TxCFG, 0);
100772940Simp	cs_writereg(sc, PP_BufCFG, 0);
100872940Simp	cs_writereg(sc, PP_BusCTL, 0);
100937785Smsmith
101037785Smsmith	sc->arpcom.ac_if.if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
101137785Smsmith	sc->arpcom.ac_if.if_timer = 0;
101237785Smsmith
101337785Smsmith	(void) splx(s);
101437785Smsmith}
101537785Smsmith
101637785Smsmith/*
101737785Smsmith * Reset the interface
101837785Smsmith */
101937785Smsmithstatic void
102037785Smsmithcs_reset(struct cs_softc *sc)
102137785Smsmith{
102237785Smsmith	cs_stop(sc);
102337785Smsmith	cs_init(sc);
102437785Smsmith}
102537785Smsmith
102637785Smsmithstatic void
102737785Smsmithcs_setmode(struct cs_softc *sc)
102837785Smsmith{
102937785Smsmith	struct ifnet *ifp = &(sc->arpcom.ac_if);
103037785Smsmith	int rx_ctl;
103137785Smsmith
103237785Smsmith	/* Stop the receiver while changing filters */
103372940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) & ~SERIAL_RX_ON);
103437785Smsmith
103537785Smsmith	if (ifp->if_flags & IFF_PROMISC) {
103637785Smsmith		/* Turn on promiscuous mode. */
103737785Smsmith		rx_ctl = RX_OK_ACCEPT | RX_PROM_ACCEPT;
103837785Smsmith	} else {
103937785Smsmith		if (ifp->if_flags & IFF_MULTICAST) {
104037785Smsmith			/* Allow receiving frames with multicast addresses */
104137785Smsmith			rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
104237785Smsmith				 RX_OK_ACCEPT | RX_MULTCAST_ACCEPT;
104337785Smsmith			/*
104437785Smsmith			 * Here the reconfiguration of chip's multicast
104537785Smsmith			 * filters should be done but I've no idea about
104637785Smsmith			 * hash transformation in this chip. If you can
104737785Smsmith			 * add this code or describe me the transformation
104837785Smsmith			 * I'd be very glad.
104937785Smsmith			 */
105037785Smsmith		} else {
105137785Smsmith			/*
105237785Smsmith			 * Receive only good frames addressed for us and
105337785Smsmith			 * good broadcasts.
105437785Smsmith			 */
105537785Smsmith			rx_ctl = RX_IA_ACCEPT | RX_BROADCAST_ACCEPT |
105637785Smsmith				 RX_OK_ACCEPT;
105737785Smsmith		}
105837785Smsmith	}
105937785Smsmith
106037785Smsmith	/* Set up the filter */
106172940Simp	cs_writereg(sc, PP_RxCTL, RX_DEF_ACCEPT | rx_ctl);
106237785Smsmith
106337785Smsmith	/* Turn on receiver */
106472940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) | SERIAL_RX_ON);
106537785Smsmith}
106637785Smsmith
106737785Smsmithstatic int
106837785Smsmithcs_ioctl(register struct ifnet *ifp, u_long command, caddr_t data)
106937785Smsmith{
107037785Smsmith	struct cs_softc *sc=ifp->if_softc;
107137785Smsmith	struct ifreq *ifr = (struct ifreq *)data;
107237785Smsmith	int s,error=0;
107337785Smsmith
107437785Smsmith#ifdef CS_DEBUG
1075104252Sbrooks	if_printf(ifp, "ioctl(%lx)\n", command);
107637785Smsmith#endif
107737785Smsmith
107837785Smsmith	s=splimp();
107937785Smsmith
108037785Smsmith	switch (command) {
108137785Smsmith	case SIOCSIFFLAGS:
108237785Smsmith		/*
108337785Smsmith		 * Switch interface state between "running" and
108437785Smsmith		 * "stopped", reflecting the UP flag.
108537785Smsmith                 */
108637785Smsmith                if (sc->arpcom.ac_if.if_flags & IFF_UP) {
108737785Smsmith                        if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)==0) {
108837785Smsmith                                cs_init(sc);
108937785Smsmith                        }
109037785Smsmith                } else {
109137785Smsmith                        if ((sc->arpcom.ac_if.if_flags & IFF_RUNNING)!=0) {
109237785Smsmith                                cs_stop(sc);
109337785Smsmith                        }
109437785Smsmith		}
109537785Smsmith		/*
109637785Smsmith		 * Promiscuous and/or multicast flags may have changed,
109737785Smsmith		 * so reprogram the multicast filter and/or receive mode.
109837785Smsmith		 *
109937785Smsmith		 * See note about multicasts in cs_setmode
110037785Smsmith		 */
110137785Smsmith		cs_setmode(sc);
110237785Smsmith		break;
110337785Smsmith
110437785Smsmith	case SIOCADDMULTI:
110537785Smsmith	case SIOCDELMULTI:
110637785Smsmith	    /*
110737785Smsmith	     * Multicast list has changed; set the hardware filter
110837785Smsmith	     * accordingly.
110937785Smsmith	     *
111037785Smsmith	     * See note about multicasts in cs_setmode
111137785Smsmith	     */
111237785Smsmith	    cs_setmode(sc);
111337785Smsmith	    error = 0;
111437785Smsmith	    break;
111537785Smsmith
111637785Smsmith        case SIOCSIFMEDIA:
111737785Smsmith        case SIOCGIFMEDIA:
111837785Smsmith                error = ifmedia_ioctl(ifp, ifr, &sc->media, command);
111937785Smsmith                break;
112037785Smsmith
112137785Smsmith        default:
1122106937Ssam		ether_ioctl(ifp, command, data);
1123106937Ssam		break;
112437785Smsmith        }
112537785Smsmith
112637785Smsmith	(void) splx(s);
112737785Smsmith	return error;
112837785Smsmith}
112937785Smsmith
113037785Smsmith/*
113137785Smsmith * Device timeout/watchdog routine. Entered if the device neglects to
113237785Smsmith * generate an interrupt after a transmit has been started on it.
113337785Smsmith */
113437785Smsmithstatic void
113537785Smsmithcs_watchdog(struct ifnet *ifp)
113637785Smsmith{
113758816Simp	struct cs_softc *sc = ifp->if_softc;
113837785Smsmith
113937785Smsmith	ifp->if_oerrors++;
1140121816Sbrooks	log(LOG_ERR, "%s: device timeout\n", ifp->if_xname);
114137785Smsmith
114237785Smsmith	/* Reset the interface */
114337785Smsmith	if (ifp->if_flags & IFF_UP)
114437785Smsmith		cs_reset(sc);
114537785Smsmith	else
114637785Smsmith		cs_stop(sc);
114737785Smsmith}
114837785Smsmith
114937785Smsmithstatic int
115037785Smsmithcs_mediachange(struct ifnet *ifp)
115137785Smsmith{
115237785Smsmith	struct cs_softc *sc = ifp->if_softc;
115337785Smsmith	struct ifmedia *ifm = &sc->media;
115437785Smsmith
115537785Smsmith	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
115637785Smsmith		return EINVAL;
115737785Smsmith
115837785Smsmith	return cs_mediaset(sc, ifm->ifm_media);
115937785Smsmith}
116037785Smsmith
116137785Smsmithstatic void
116237785Smsmithcs_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
116337785Smsmith{
116437785Smsmith	int line_status;
116537785Smsmith	struct cs_softc *sc = ifp->if_softc;
116637785Smsmith
116737785Smsmith	ifmr->ifm_active = IFM_ETHER;
116872940Simp	line_status = cs_readreg(sc, PP_LineST);
116937785Smsmith	if (line_status & TENBASET_ON) {
117037785Smsmith		ifmr->ifm_active |= IFM_10_T;
117137785Smsmith		if (sc->chip_type != CS8900) {
117272940Simp			if (cs_readreg(sc, PP_AutoNegST) & FDX_ACTIVE)
117337785Smsmith				ifmr->ifm_active |= IFM_FDX;
117472940Simp			if (cs_readreg(sc, PP_AutoNegST) & HDX_ACTIVE)
117537785Smsmith				ifmr->ifm_active |= IFM_HDX;
117637785Smsmith		}
117737785Smsmith		ifmr->ifm_status = IFM_AVALID;
117837785Smsmith		if (line_status & LINK_OK)
117937785Smsmith			ifmr->ifm_status |= IFM_ACTIVE;
118037785Smsmith	} else {
118138305Smsmith		if (line_status & AUI_ON) {
118272940Simp			cs_writereg(sc, PP_SelfCTL, cs_readreg(sc, PP_SelfCTL) |
118372940Simp			    HCB1_ENBL);
118438305Smsmith			if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0)^
118572940Simp			    (cs_readreg(sc, PP_SelfCTL) & HCB1))
118638305Smsmith				ifmr->ifm_active |= IFM_10_2;
118738305Smsmith			else
118838305Smsmith				ifmr->ifm_active |= IFM_10_5;
118938305Smsmith		}
119037785Smsmith	}
119137785Smsmith}
119237785Smsmith
119337785Smsmithstatic int
119437785Smsmithcs_mediaset(struct cs_softc *sc, int media)
119537785Smsmith{
119637785Smsmith        int error;
119737785Smsmith
119837785Smsmith	/* Stop the receiver & transmitter */
119972940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) &
120072940Simp	    ~(SERIAL_RX_ON | SERIAL_TX_ON));
120137785Smsmith
120237785Smsmith#ifdef CS_DEBUG
1203104252Sbrooks	if_printf(&sc->arpcom.ac_if, "cs_setmedia(%x)\n", media);
120437785Smsmith#endif
120537785Smsmith
120637785Smsmith	switch (IFM_SUBTYPE(media)) {
120737785Smsmith	default:
120837785Smsmith	case IFM_AUTO:
120938592Smsmith		if ((error=enable_tp(sc))==0)
121038592Smsmith			error = cs_duplex_auto(sc);
121143314Sdillon		else if ((error=enable_bnc(sc)) != 0)
121238592Smsmith			error = enable_aui(sc);
121337785Smsmith		break;
121437785Smsmith	case IFM_10_T:
121543314Sdillon		if ((error=enable_tp(sc)) != 0)
121637785Smsmith			break;
121737785Smsmith		if (media & IFM_FDX)
121837785Smsmith			cs_duplex_full(sc);
121937785Smsmith		else if (media & IFM_HDX)
122037785Smsmith			cs_duplex_half(sc);
122137785Smsmith		else
122237785Smsmith			error = cs_duplex_auto(sc);
122337785Smsmith		break;
122437785Smsmith	case IFM_10_2:
122537785Smsmith		error = enable_bnc(sc);
122637785Smsmith		break;
122737785Smsmith	case IFM_10_5:
122837785Smsmith		error = enable_aui(sc);
122937785Smsmith		break;
123037785Smsmith	}
123137785Smsmith
123237785Smsmith	/*
123337785Smsmith	 * Turn the transmitter & receiver back on
123437785Smsmith	 */
123572940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) |
123672940Simp	    SERIAL_RX_ON | SERIAL_TX_ON);
123737785Smsmith
123837785Smsmith	return error;
123937785Smsmith}
1240