if_cs.c revision 111119
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
2937785Smsmith/*
3050477Speter * $FreeBSD: head/sys/dev/cs/if_cs.c 111119 2003-02-19 05:47:46Z imp $
3137785Smsmith *
3237785Smsmith * Device driver for Crystal Semiconductor CS8920 based ethernet
3337785Smsmith *   adapters. By Maxim Bolotin and Oleg Sharoiko, 27-April-1997
3437785Smsmith */
3537785Smsmith
3658816Simp/*
3758816Simp#define	 CS_DEBUG
3858816Simp */
3937785Smsmith
4037785Smsmith#include <sys/param.h>
4137785Smsmith#include <sys/systm.h>
4237785Smsmith#include <sys/malloc.h>
4358816Simp#include <sys/mbuf.h>
4458816Simp#include <sys/socket.h>
4537785Smsmith#include <sys/sockio.h>
4637785Smsmith#include <sys/kernel.h>
4738592Smsmith#include <sys/sysctl.h>
4837785Smsmith#include <sys/syslog.h>
4937785Smsmith
5058816Simp#include <sys/module.h>
5158816Simp#include <sys/bus.h>
5258816Simp#include <machine/bus.h>
5358816Simp#include <sys/rman.h>
5458816Simp#include <machine/resource.h>
5558816Simp
5637785Smsmith#include <net/if.h>
5737785Smsmith#include <net/if_arp.h>
5837785Smsmith#include <net/if_media.h>
5937785Smsmith#include <net/ethernet.h>
6037785Smsmith#include <net/bpf.h>
6137785Smsmith
6272940Simp#include <dev/cs/if_csvar.h>
6358816Simp#include <dev/cs/if_csreg.h>
6437785Smsmith
6537785Smsmith#ifdef  CS_USE_64K_DMA
6637785Smsmith#define CS_DMA_BUFFER_SIZE 65536
6737785Smsmith#else
6837785Smsmith#define CS_DMA_BUFFER_SIZE 16384
6937785Smsmith#endif
7037785Smsmith
7138592Smsmithstatic int	cs_recv_delay = 570;
7238592SmsmithSYSCTL_INT(_machdep, OID_AUTO, cs_recv_delay, CTLFLAG_RW, &cs_recv_delay, 0, "");
7338592Smsmith
7471316Simpstatic void	cs_init		(void *);
7571316Simpstatic int	cs_ioctl	(struct ifnet *, u_long, caddr_t);
7671316Simpstatic void	cs_start	(struct ifnet *);
7771316Simpstatic void	cs_stop		(struct cs_softc *);
7871316Simpstatic void	cs_reset	(struct cs_softc *);
7971316Simpstatic void	cs_watchdog	(struct ifnet *);
8058816Simp
8171316Simpstatic int	cs_mediachange	(struct ifnet *);
8271316Simpstatic void	cs_mediastatus	(struct ifnet *, struct ifmediareq *);
8371316Simpstatic int      cs_mediaset	(struct cs_softc *, int);
8458816Simp
8537785Smsmithstatic void	cs_write_mbufs(struct cs_softc*, struct mbuf*);
8637785Smsmithstatic void	cs_xmit_buf(struct cs_softc*);
8737785Smsmithstatic int	cs_get_packet(struct cs_softc*);
8837785Smsmithstatic void	cs_setmode(struct cs_softc*);
8937785Smsmith
9037785Smsmithstatic int	get_eeprom_data(struct cs_softc *sc, int, int, int *);
9137785Smsmithstatic int	get_eeprom_cksum(int, int, int *);
9237785Smsmithstatic int	wait_eeprom_ready( struct cs_softc *);
9337785Smsmithstatic void	control_dc_dc( struct cs_softc *, int );
9437785Smsmithstatic int	send_test_pkt( struct cs_softc * );
9537785Smsmithstatic int	enable_tp(struct cs_softc *);
9637785Smsmithstatic int	enable_aui(struct cs_softc *);
9737785Smsmithstatic int	enable_bnc(struct cs_softc *);
9837785Smsmithstatic int      cs_duplex_auto(struct cs_softc *);
9937785Smsmith
10071316Simpdevclass_t cs_devclass;
10137785Smsmith
10237785Smsmithstatic int
10337785Smsmithget_eeprom_data( struct cs_softc *sc, int off, int len, int *buffer)
10437785Smsmith{
10537785Smsmith	int i;
10637785Smsmith
10737785Smsmith#ifdef CS_DEBUG
10837785Smsmith	printf(CS_NAME":EEPROM data from %x for %x:\n", off,len);
10937785Smsmith#endif
11037785Smsmith
11137785Smsmith	for (i=0;i<len;i++) {
11237785Smsmith		if (wait_eeprom_ready(sc) < 0) return -1;
11337785Smsmith		/* Send command to EEPROM to read */
11472940Simp		cs_writereg(sc, PP_EECMD, (off + i) | EEPROM_READ_CMD);
11537785Smsmith		if (wait_eeprom_ready(sc)<0)
11672940Simp			return (-1);
11772940Simp		buffer[i] = cs_readreg(sc, PP_EEData);
11837785Smsmith
11937785Smsmith#ifdef CS_DEBUG
12037785Smsmith		printf("%02x %02x ",(unsigned char)buffer[i],
12137785Smsmith					(unsigned char)buffer[i+1]);
12237785Smsmith#endif
12337785Smsmith	}
12437785Smsmith
12537785Smsmith#ifdef CS_DEBUG
12637785Smsmith	printf("\n");
12737785Smsmith#endif
12872940Simp	return (0);
12937785Smsmith}
13037785Smsmith
13137785Smsmithstatic int
13237785Smsmithget_eeprom_cksum(int off, int len, int *buffer)
13337785Smsmith{
13437785Smsmith	int i,cksum=0;
13537785Smsmith
13637785Smsmith	for (i=0;i<len;i++)
13737785Smsmith		cksum+=buffer[i];
13837785Smsmith	cksum &= 0xffff;
13937785Smsmith	if (cksum==0)
14037785Smsmith		return 0;
14137785Smsmith	return -1;
14237785Smsmith}
14337785Smsmith
14437785Smsmithstatic int
14537785Smsmithwait_eeprom_ready(struct cs_softc *sc)
14637785Smsmith{
14737785Smsmith	DELAY ( 30000 );	/* XXX should we do some checks here ? */
14837785Smsmith	return 0;
14937785Smsmith}
15037785Smsmith
15137785Smsmithstatic void
15237785Smsmithcontrol_dc_dc(struct cs_softc *sc, int on_not_off)
15337785Smsmith{
15437785Smsmith	unsigned int self_control = HCB1_ENBL;
15537785Smsmith
15637785Smsmith	if (((sc->adapter_cnf & A_CNF_DC_DC_POLARITY)!=0) ^ on_not_off)
15737785Smsmith		self_control |= HCB1;
15837785Smsmith	else
15937785Smsmith		self_control &= ~HCB1;
16072940Simp	cs_writereg(sc, PP_SelfCTL, self_control);
16137785Smsmith
16237785Smsmith	DELAY( 500000 );
16337785Smsmith}
16437785Smsmith
16537785Smsmith
16637785Smsmithstatic int
16737785Smsmithcs_duplex_auto(struct cs_softc *sc)
16837785Smsmith{
169104252Sbrooks        int i, error=0;
17037785Smsmith
17172940Simp	cs_writereg(sc, PP_AutoNegCTL,
17272940Simp	    RE_NEG_NOW | ALLOW_FDX | AUTO_NEG_ENABLE);
17372940Simp        for (i=0; cs_readreg(sc, PP_AutoNegST) & AUTO_NEG_BUSY; i++) {
17437785Smsmith                if (i > 40000) {
175104252Sbrooks                        if_printf(&sc->arpcom.ac_if,
176104252Sbrooks                        	"full/half duplex auto negotiation timeout\n");
17737785Smsmith			error = ETIMEDOUT;
17837785Smsmith                        break;
17937785Smsmith                }
18037785Smsmith                DELAY(1000);
18137785Smsmith        }
18237785Smsmith        DELAY( 1000000 );
18337785Smsmith	return error;
18437785Smsmith}
18537785Smsmith
18637785Smsmithstatic int
18737785Smsmithenable_tp(struct cs_softc *sc)
18837785Smsmith{
18937785Smsmith
19072940Simp	cs_writereg(sc, PP_LineCTL, sc->line_ctl & ~AUI_ONLY);
19137785Smsmith	control_dc_dc(sc, 0);
19237785Smsmith	DELAY( 150000 );
19337785Smsmith
19472940Simp	if ((cs_readreg(sc, PP_LineST) & LINK_OK)==0) {
195104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable TP\n");
19637785Smsmith                return EINVAL;
19737785Smsmith	}
19837785Smsmith
19937785Smsmith	return 0;
20037785Smsmith}
20137785Smsmith
20237785Smsmith/*
20337785Smsmith * XXX This was rewritten from Linux driver without any tests.
20437785Smsmith */
20537785Smsmithstatic int
20637785Smsmithsend_test_pkt(struct cs_softc *sc)
20737785Smsmith{
20837785Smsmith	char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
20937785Smsmith				0, 46,  /* A 46 in network order */
21037785Smsmith				0, 0,   /* DSAP=0 & SSAP=0 fields */
21137785Smsmith				0xf3, 0 /* Control (Test Req + P bit set) */ };
21238305Smsmith	int i;
21338305Smsmith	u_char ether_address_backup[ETHER_ADDR_LEN];
21437785Smsmith
21538305Smsmith	for (i = 0; i < ETHER_ADDR_LEN; i++) {
21638305Smsmith		ether_address_backup[i] = sc->arpcom.ac_enaddr[i];
21738305Smsmith	}
21838305Smsmith
21972940Simp	cs_writereg(sc, PP_LineCTL, cs_readreg(sc, PP_LineCTL) | SERIAL_TX_ON);
22072940Simp	bcopy(test_packet, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
22172940Simp	bcopy(test_packet+ETHER_ADDR_LEN,
22272940Simp	    sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
22372940Simp	cs_outw(sc, TX_CMD_PORT, sc->send_cmd);
22472940Simp	cs_outw(sc, TX_LEN_PORT, sizeof(test_packet));
22537785Smsmith
22637785Smsmith	/* Wait for chip to allocate memory */
22737785Smsmith	DELAY(50000);
22872940Simp	if (!(cs_readreg(sc, PP_BusST) & READY_FOR_TX_NOW)) {
22938305Smsmith		for (i = 0; i < ETHER_ADDR_LEN; i++) {
23038305Smsmith			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
23138305Smsmith		}
23237785Smsmith		return 0;
23338305Smsmith	}
23437785Smsmith
23537785Smsmith	outsw(sc->nic_addr + TX_FRAME_PORT, test_packet, sizeof(test_packet));
23637785Smsmith
23737785Smsmith	DELAY(30000);
23837785Smsmith
23972940Simp	if ((cs_readreg(sc, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
24038305Smsmith		for (i = 0; i < ETHER_ADDR_LEN; i++) {
24138305Smsmith			sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
24238305Smsmith		}
24337785Smsmith		return 1;
24438305Smsmith	}
24538305Smsmith	for (i = 0; i < ETHER_ADDR_LEN; i++) {
24638305Smsmith		sc->arpcom.ac_enaddr[i] = ether_address_backup[i];
24738305Smsmith	}
24837785Smsmith	return 0;
24937785Smsmith}
25037785Smsmith
25137785Smsmith/*
25237785Smsmith * XXX This was rewritten from Linux driver without any tests.
25337785Smsmith */
25437785Smsmithstatic int
25537785Smsmithenable_aui(struct cs_softc *sc)
25637785Smsmith{
25737785Smsmith
25837785Smsmith	control_dc_dc(sc, 0);
25972940Simp	cs_writereg(sc, PP_LineCTL,
26072940Simp	    (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
26137785Smsmith
26237785Smsmith	if (!send_test_pkt(sc)) {
263104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable AUI\n");
26437785Smsmith		return EINVAL;
26537785Smsmith        }
26637785Smsmith        return 0;
26737785Smsmith}
26837785Smsmith
26937785Smsmith/*
27037785Smsmith * XXX This was rewritten from Linux driver without any tests.
27137785Smsmith */
27237785Smsmithstatic int
27337785Smsmithenable_bnc(struct cs_softc *sc)
27437785Smsmith{
27537785Smsmith
27637785Smsmith	control_dc_dc(sc, 1);
27772940Simp	cs_writereg(sc, PP_LineCTL,
27872940Simp	    (sc->line_ctl & ~AUTO_AUI_10BASET) | AUI_ONLY);
27937785Smsmith
28037785Smsmith	if (!send_test_pkt(sc)) {
281104252Sbrooks		if_printf(&sc->arpcom.ac_if, "failed to enable BNC\n");
28237785Smsmith		return EINVAL;
28337785Smsmith        }
28437785Smsmith        return 0;
28537785Smsmith}
28637785Smsmith
28771316Simpint
28858816Simpcs_cs89x0_probe(device_t dev)
28937785Smsmith{
29058816Simp	int i;
29158816Simp	int error;
29258816Simp	u_long irq, junk;
29358816Simp	struct cs_softc *sc = device_get_softc(dev);
29437785Smsmith	unsigned rev_type = 0;
29572940Simp	u_int16_t id;
29658816Simp	char chip_revision;
29737785Smsmith	int eeprom_buff[CHKSUM_LEN];
29837785Smsmith	int chip_type, pp_isaint, pp_isadma;
29937785Smsmith
30058816Simp	error = cs_alloc_port(dev, 0, CS_89x0_IO_PORTS);
30158816Simp	if (error)
30258816Simp		return (error);
30358816Simp
30472940Simp	sc->nic_addr = rman_get_start(sc->port_res);
30558816Simp
30672940Simp	if ((cs_inw(sc, ADD_PORT) & ADD_MASK) != ADD_SIG) {
30737785Smsmith		/* Chip not detected. Let's try to reset it */
30837785Smsmith		if (bootverbose)
30958816Simp			device_printf(dev, "trying to reset the chip.\n");
31072940Simp		cs_outw(sc, ADD_PORT, PP_SelfCTL);
31172940Simp		i = cs_inw(sc, DATA_PORT);
31272940Simp		cs_outw(sc, ADD_PORT, PP_SelfCTL);
31372940Simp		cs_outw(sc, DATA_PORT, i | POWER_ON_RESET);
31472940Simp		if ((cs_inw(sc, ADD_PORT) & ADD_MASK) != ADD_SIG)
31558816Simp			return (ENXIO);
31637785Smsmith	}
31737785Smsmith
31872940Simp	for (i = 0; i < 10000; i++) {
31972940Simp		id = cs_readreg(sc, PP_ChipID);
32072940Simp		if (id == CHIP_EISA_ID_SIG)
32172940Simp			break;
32272940Simp	}
32372940Simp	if (i == 10000)
32458816Simp		return (ENXIO);
32537785Smsmith
32672940Simp	rev_type = cs_readreg(sc, PRODUCT_ID_ADD);
32737785Smsmith	chip_type = rev_type & ~REVISON_BITS;
32837785Smsmith	chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
32937785Smsmith
33037785Smsmith	sc->chip_type = chip_type;
33158816Simp
33237785Smsmith	if(chip_type==CS8900) {
33337785Smsmith		pp_isaint = PP_CS8900_ISAINT;
33437785Smsmith		pp_isadma = PP_CS8900_ISADMA;
33537785Smsmith		sc->send_cmd = TX_CS8900_AFTER_ALL;
33637785Smsmith	} else {
33737785Smsmith		pp_isaint = PP_CS8920_ISAINT;
33837785Smsmith		pp_isadma = PP_CS8920_ISADMA;
33937785Smsmith		sc->send_cmd = TX_CS8920_AFTER_ALL;
34037785Smsmith	}
34137785Smsmith
34237785Smsmith        /*
34337785Smsmith         * Clear some fields so that fail of EEPROM will left them clean
34437785Smsmith         */
34537785Smsmith        sc->auto_neg_cnf = 0;
34637785Smsmith        sc->adapter_cnf  = 0;
34737785Smsmith        sc->isa_config   = 0;
34837785Smsmith
34937785Smsmith	/*
35058816Simp	 * If no interrupt specified (or "?"), use what the board tells us.
35137785Smsmith	 */
35258816Simp	error = bus_get_resource(dev, SYS_RES_IRQ, 0, &irq, &junk);
35358816Simp
35458816Simp	/*
35558816Simp	 * Get data from EEPROM
35658816Simp	 */
35772940Simp	if((cs_readreg(sc, PP_SelfST) & EEPROM_PRESENT) == 0) {
35858816Simp		device_printf(dev, "No EEPROM, assuming defaults.\n");
35937785Smsmith	} else {
36037785Smsmith		if (get_eeprom_data(sc,START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
36158816Simp			device_printf(dev, "EEPROM read failed, "
36258816Simp				"assuming defaults.\n");
36337785Smsmith		} else {
36437785Smsmith			if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN, eeprom_buff)<0) {
36558816Simp				device_printf(dev, "EEPROM cheksum bad, "
36658816Simp					"assuming defaults.\n");
36737785Smsmith			} else {
36837785Smsmith                                sc->auto_neg_cnf =
36937785Smsmith                                        eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
37037785Smsmith                                sc->adapter_cnf =
37137785Smsmith                                        eeprom_buff[ADAPTER_CNF_OFFSET/2];
37237785Smsmith                                sc->isa_config =
37337785Smsmith                                        eeprom_buff[ISA_CNF_OFFSET/2];
37437785Smsmith
37537785Smsmith                                for (i=0; i<ETHER_ADDR_LEN/2; i++) {
37637785Smsmith                                        sc->arpcom.ac_enaddr[i*2]=
37737785Smsmith                                                eeprom_buff[i];
37837785Smsmith                                        sc->arpcom.ac_enaddr[i*2+1]=
37937785Smsmith                                                eeprom_buff[i] >> 8;
38037785Smsmith                                }
38137785Smsmith
38237785Smsmith                                /*
38337785Smsmith                                 * If no interrupt specified (or "?"),
38437785Smsmith                                 * use what the board tells us.
38537785Smsmith                                 */
38658816Simp				if (error) {
38758816Simp					irq = sc->isa_config & INT_NO_MASK;
38837785Smsmith                                        if (chip_type==CS8900) {
38958816Simp                                                switch(irq) {
39058816Simp						 case 0:
39158816Simp							irq=10;
39258816Simp							error=0;
39358816Simp							break;
39458816Simp						 case 1:
39558816Simp							irq=11;
39658816Simp							error=0;
39758816Simp							break;
39858816Simp						 case 2:
39958816Simp							irq=12;
40058816Simp							error=0;
40158816Simp							break;
40258816Simp						 case 3:
40358816Simp							irq=5;
40458816Simp							error=0;
40558816Simp							break;
40658816Simp						 default:
40758816Simp							device_printf(dev, "invalid irq in EEPROM.\n");
40858816Simp							error=EINVAL;
40958816Simp                                                }
41058816Simp					} else {
41158816Simp						if (irq>CS8920_NO_INTS) {
41258816Simp							device_printf(dev, "invalid irq in EEPROM.\n");
41358816Simp							error=EINVAL;
41458816Simp						} else {
41558816Simp							error=0;
41637785Smsmith						}
41737785Smsmith					}
41858816Simp
41958816Simp					if (!error)
42058816Simp						bus_set_resource(dev, SYS_RES_IRQ, 0,
42158816Simp								irq, 1);
42258816Simp				}
42337785Smsmith			}
42437785Smsmith                }
42537785Smsmith        }
42637785Smsmith
42758816Simp	if (!error) {
42837785Smsmith                if (chip_type == CS8900) {
42937785Smsmith			switch(irq) {
43058816Simp				case  5:
43158816Simp					irq = 3;
43258816Simp					break;
43358816Simp				case 10:
43458816Simp					irq = 0;
43558816Simp					break;
43658816Simp				case 11:
43758816Simp					irq = 1;
43858816Simp					break;
43958816Simp				case 12:
44058816Simp					irq = 2;
44158816Simp					break;
44258816Simp				default:
44358816Simp					error=EINVAL;
44437785Smsmith			}
44537785Smsmith                } else {
44637785Smsmith                        if (irq > CS8920_NO_INTS) {
44758816Simp                                error = EINVAL;
44837785Smsmith                        }
44937785Smsmith                }
45058816Simp	}
45158816Simp
45258816Simp	if (!error) {
45372940Simp                cs_writereg(sc, pp_isaint, irq);
45437785Smsmith	} else {
45558816Simp	       	device_printf(dev, "Unknown or invalid irq\n");
45658816Simp                return (ENXIO);
45737785Smsmith        }
45837785Smsmith
45937785Smsmith        /*
46037785Smsmith         * Temporary disabled
46137785Smsmith         *
46237785Smsmith        if (drq>0)
46372940Simp		cs_writereg(sc, pp_isadma, drq);
46437785Smsmith	else {
465104252Sbrooks		device_printf(dev, "incorrect drq\n",);
46637785Smsmith		return 0;
46737785Smsmith	}
46837785Smsmith        */
46937785Smsmith
47037785Smsmith	if (bootverbose)
47158816Simp		 device_printf(dev, "CS89%c0%s rev %c media%s%s%s\n",
47237785Smsmith			chip_type==CS8900 ? '0' : '2',
47337785Smsmith			chip_type==CS8920M ? "M" : "",
47437785Smsmith			chip_revision,
47537785Smsmith			(sc->adapter_cnf & A_CNF_10B_T) ? " TP"  : "",
47637785Smsmith			(sc->adapter_cnf & A_CNF_AUI)   ? " AUI" : "",
47758816Simp			(sc->adapter_cnf & A_CNF_10B_2) ? " BNC" : "");
47837785Smsmith
47937785Smsmith        if ((sc->adapter_cnf & A_CNF_EXTND_10B_2) &&
48037785Smsmith            (sc->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
48137785Smsmith                sc->line_ctl = LOW_RX_SQUELCH;
48237785Smsmith        else
48337785Smsmith                sc->line_ctl = 0;
48437785Smsmith
48537785Smsmith
48658816Simp	return 0;
48737785Smsmith}
48837785Smsmith
48937785Smsmith/*
49058816Simp * Allocate a port resource with the given resource id.
49158816Simp */
49258816Simpint cs_alloc_port(device_t dev, int rid, int size)
49358816Simp{
49458816Simp        struct cs_softc *sc = device_get_softc(dev);
49558816Simp        struct resource *res;
49658816Simp
49758816Simp        res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
49858816Simp                                 0ul, ~0ul, size, RF_ACTIVE);
49958816Simp        if (res) {
50058816Simp                sc->port_rid = rid;
50158816Simp                sc->port_res = res;
50258816Simp                sc->port_used = size;
50358816Simp                return (0);
50458816Simp        } else {
50558816Simp                return (ENOENT);
50658816Simp        }
50758816Simp}
50858816Simp
50958816Simp/*
51058816Simp * Allocate a memory resource with the given resource id.
51158816Simp */
51258816Simpint cs_alloc_memory(device_t dev, int rid, int size)
51358816Simp{
51458816Simp        struct cs_softc *sc = device_get_softc(dev);
51558816Simp        struct resource *res;
51658816Simp
51758816Simp        res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
51858816Simp                                 0ul, ~0ul, size, RF_ACTIVE);
51958816Simp        if (res) {
52058816Simp                sc->mem_rid = rid;
52158816Simp                sc->mem_res = res;
52258816Simp                sc->mem_used = size;
52358816Simp                return (0);
52458816Simp        } else {
52558816Simp                return (ENOENT);
52658816Simp        }
52758816Simp}
52858816Simp
52958816Simp/*
53058816Simp * Allocate an irq resource with the given resource id.
53158816Simp */
53258816Simpint cs_alloc_irq(device_t dev, int rid, int flags)
53358816Simp{
53458816Simp        struct cs_softc *sc = device_get_softc(dev);
53558816Simp        struct resource *res;
53658816Simp
53758816Simp        res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
53858816Simp                                 0ul, ~0ul, 1, (RF_ACTIVE | flags));
53958816Simp        if (res) {
54058816Simp                sc->irq_rid = rid;
54158816Simp                sc->irq_res = res;
54258816Simp                return (0);
54358816Simp        } else {
54458816Simp                return (ENOENT);
54558816Simp        }
54658816Simp}
54758816Simp
54858816Simp/*
54958816Simp * Release all resources
55058816Simp */
55158816Simpvoid cs_release_resources(device_t dev)
55258816Simp{
55358816Simp        struct cs_softc *sc = device_get_softc(dev);
55458816Simp
55558816Simp        if (sc->port_res) {
55658816Simp                bus_release_resource(dev, SYS_RES_IOPORT,
55758816Simp                                     sc->port_rid, sc->port_res);
55858816Simp                sc->port_res = 0;
55958816Simp        }
56058816Simp        if (sc->mem_res) {
56158816Simp                bus_release_resource(dev, SYS_RES_MEMORY,
56258816Simp                                     sc->mem_rid, sc->mem_res);
56358816Simp                sc->mem_res = 0;
56458816Simp        }
56558816Simp        if (sc->irq_res) {
56658816Simp                bus_release_resource(dev, SYS_RES_IRQ,
56758816Simp                                     sc->irq_rid, sc->irq_res);
56858816Simp                sc->irq_res = 0;
56958816Simp        }
57058816Simp}
57158816Simp
57258816Simp/*
57337785Smsmith * Install the interface into kernel networking data structures
57437785Smsmith */
57571316Simpint
57637785Smsmithcs_attach(struct cs_softc *sc, int unit, int flags)
57737785Smsmith{
57837785Smsmith        int media=0;
57937785Smsmith	struct ifnet *ifp = &(sc->arpcom.ac_if);
58037785Smsmith
58158816Simp	cs_stop( sc );
58258816Simp
58337785Smsmith	if (!ifp->if_name) {
58437785Smsmith		ifp->if_softc=sc;
58537785Smsmith		ifp->if_unit=unit;
58658816Simp		ifp->if_name="cs";
58737785Smsmith		ifp->if_output=ether_output;
58837785Smsmith		ifp->if_start=cs_start;
58937785Smsmith		ifp->if_ioctl=cs_ioctl;
59037785Smsmith		ifp->if_watchdog=cs_watchdog;
59137785Smsmith		ifp->if_init=cs_init;
59237785Smsmith		ifp->if_snd.ifq_maxlen= IFQ_MAXLEN;
59337785Smsmith		/*
59437785Smsmith                 *  MIB DATA
59537785Smsmith                 */
59637785Smsmith                /*
59737785Smsmith		ifp->if_linkmib=&sc->mibdata;
59837785Smsmith		ifp->if_linkmiblen=sizeof sc->mibdata;
59937785Smsmith                */
60037785Smsmith
60137785Smsmith		ifp->if_flags=(IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST );
60237785Smsmith
60337785Smsmith		/*
60437785Smsmith		 * this code still in progress (DMA support)
60537785Smsmith		 *
60637785Smsmith
60737785Smsmith		sc->recv_ring=malloc(CS_DMA_BUFFER_SIZE<<1, M_DEVBUF, M_NOWAIT);
60837785Smsmith		if (sc->recv_ring == NULL) {
60937785Smsmith			log(LOG_ERR,CS_NAME
61037785Smsmith			"%d: Couldn't allocate memory for NIC\n", unit);
61137785Smsmith			return(0);
61237785Smsmith		}
61337785Smsmith		if ((sc->recv_ring-(sc->recv_ring & 0x1FFFF))
61437785Smsmith		    < (128*1024-CS_DMA_BUFFER_SIZE))
61537785Smsmith		    sc->recv_ring+=16*1024;
61637785Smsmith
61737785Smsmith		*/
61837785Smsmith
61937785Smsmith		sc->buffer=malloc(ETHER_MAX_LEN-ETHER_CRC_LEN,M_DEVBUF,M_NOWAIT);
62037785Smsmith		if (sc->buffer == NULL) {
621104252Sbrooks                        if_printf(ifp, "Couldn't allocate memory for NIC\n");
62237785Smsmith                        return(0);
62337785Smsmith		}
62437785Smsmith
62537785Smsmith		/*
62637785Smsmith		 * Initialize the media structures.
62737785Smsmith		 */
62837785Smsmith		ifmedia_init(&sc->media, 0, cs_mediachange, cs_mediastatus);
62937785Smsmith
63037785Smsmith		if (sc->adapter_cnf & A_CNF_10B_T) {
63137785Smsmith			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_T, 0, NULL);
63237785Smsmith			if (sc->chip_type != CS8900) {
63337785Smsmith				ifmedia_add(&sc->media,
63437785Smsmith					IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
63537785Smsmith				ifmedia_add(&sc->media,
63637785Smsmith					IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
63737785Smsmith			}
63837785Smsmith		}
63937785Smsmith
64037785Smsmith		if (sc->adapter_cnf & A_CNF_10B_2)
64137785Smsmith			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_2, 0, NULL);
64237785Smsmith
64337785Smsmith		if (sc->adapter_cnf & A_CNF_AUI)
64437785Smsmith			ifmedia_add(&sc->media, IFM_ETHER|IFM_10_5, 0, NULL);
64537785Smsmith
64637785Smsmith                if (sc->adapter_cnf & A_CNF_MEDIA)
64737785Smsmith                        ifmedia_add(&sc->media, IFM_ETHER|IFM_AUTO, 0, NULL);
64837785Smsmith
64937785Smsmith                /* Set default media from EEPROM */
65037785Smsmith                switch (sc->adapter_cnf & A_CNF_MEDIA_TYPE) {
65137785Smsmith                case A_CNF_MEDIA_AUTO:  media = IFM_ETHER|IFM_AUTO; break;
65237785Smsmith                case A_CNF_MEDIA_10B_T: media = IFM_ETHER|IFM_10_T; break;
65337785Smsmith                case A_CNF_MEDIA_10B_2: media = IFM_ETHER|IFM_10_2; break;
65437785Smsmith                case A_CNF_MEDIA_AUI:   media = IFM_ETHER|IFM_10_5; break;
655104252Sbrooks                default: if_printf(ifp, "adapter has no media\n");
65637785Smsmith                }
65737785Smsmith                ifmedia_set(&sc->media, media);
65837785Smsmith		cs_mediaset(sc, media);
65937785Smsmith
660106937Ssam		ether_ifattach(ifp, sc->arpcom.ac_enaddr);
66137785Smsmith	}
66237785Smsmith
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++;
114037785Smsmith	log(LOG_ERR, CS_NAME"%d: device timeout\n", ifp->if_unit);
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