NetBSD/sys/arch/arm32/isa/if_cs_isa.c
1998-06-08 20:23:07 +00:00

3080 lines
96 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* $NetBSD: if_cs_isa.c,v 1.2 1998/06/08 20:23:08 tv Exp $ */
/*
* Copyright 1997
* Digital Equipment Corporation. All rights reserved.
*
* This software is furnished under license and may be used and
* copied only in accordance with the following terms and conditions.
* Subject to these conditions, you may download, copy, install,
* use, modify and distribute this software in source and/or binary
* form. No title or ownership is transferred hereby.
*
* 1) Any source code used, modified or distributed must reproduce
* and retain this copyright notice and list of conditions as
* they appear in the source file.
*
* 2) No right is granted to use any trade name, trademark, or logo of
* Digital Equipment Corporation. Neither the "Digital Equipment
* Corporation" name nor any trademark or logo of Digital Equipment
* Corporation may be used to endorse or promote products derived
* from this software without the prior written permission of
* Digital Equipment Corporation.
*
* 3) This software is provided "AS-IS" and any express or implied
* warranties, including but not limited to, any implied warranties
* of merchantability, fitness for a particular purpose, or
* non-infringement are disclaimed. In no event shall DIGITAL be
* liable for any damages whatsoever, and in particular, DIGITAL
* shall not be liable for special, indirect, consequential, or
* incidental damages or damages for lost profits, loss of
* revenue or loss of use, whether such damages arise in contract,
* negligence, tort, under statute, in equity, at law or otherwise,
* even if advised of the possibility of such damage.
*/
/*
**++
** FACILITY
**
** Device Driver for the Crystal CS8900 ISA Ethernet Controller.
**
** ABSTRACT
**
** This module provides standard ethernet access for INET protocols
** only.
**
** AUTHORS
**
** Peter Dettori SEA - Software Engineering.
**
** CREATION DATE:
**
** 13-Feb-1997.
**
** MODIFICATION HISTORY (Digital):
**
** Revision 1.27 1998/01/20 17:59:40 cgd
** update for moved headers
**
** Revision 1.26 1998/01/12 19:29:36 cgd
** use arm32/isa versions of isadma code.
**
** Revision 1.25 1997/12/12 01:35:27 cgd
** convert to use new arp code (from Brini)
**
** Revision 1.24 1997/12/10 22:31:56 cgd
** trim some fat (get rid of ability to explicitly supply enet addr, since
** it was never used and added a bunch of code which really doesn't belong in
** an enet driver), and clean up slightly.
**
** Revision 1.23 1997/10/06 16:42:12 cgd
** copyright notices
**
** Revision 1.22 1997/06/20 19:38:01 chaiken
** fixes some smartcard problems
**
** Revision 1.21 1997/06/10 02:56:20 grohn
** Added call to ledNetActive
**
** Revision 1.20 1997/06/05 00:47:06 dettori
** Changed csProcessRxDMA to reset and re-initialise the
** ethernet chip when DMA gets out of sync, or mbufs
** can't be allocated.
**
** Revision 1.19 1997/06/03 03:09:58 dettori
** Turn off txInProgress flag when a transmit underrun
** occurs.
**
** Revision 1.18 1997/06/02 00:04:35 dettori
** redefined the transmit table to get around the nfs_timer bug while we are
** looking into it further.
**
** Also changed interrupts from EDGE to LEVEL.
**
** Revision 1.17 1997/05/27 23:31:01 dettori
** Pulled out changes to DMAMODE defines.
**
** Revision 1.16 1997/05/23 04:25:16 cgd
** reformat log so it fits in 80cols
**
** Revision 1.15 1997/05/23 04:22:18 cgd
** remove the existing copyright notice (which Peter Dettori indicated
** was incorrect, copied from an existing NetBSD file only so that the
** file would have a copyright notice on it, and which he'd intended to
** replace). Replace it with a Digital copyright notice, cloned from
** ess.c. It's not really correct either (it indicates that the source
** is Digital confidential!), but is better than nothing and more
** correct than what was there before.
**
** Revision 1.14 1997/05/23 04:12:50 cgd
** use an adaptive transmit start algorithm: start by telling the chip
** to start transmitting after 381 bytes have been fed to it. if that
** gets transmit underruns, ramp down to 1021 bytes then "whole
** packet." If successful at a given level for a while, try the next
** more agressive level. This code doesn't ever try to start
** transmitting after 5 bytes have been sent to the NIC, because
** that underruns rather regularly. The back-off and ramp-up mechanism
** could probably be tuned a little bit, but this works well enough to
** support > 1MB/s transmit rates on a clear ethernet (which is about
** 20-25% better than the driver had previously been getting).
**
** Revision 1.13 1997/05/22 21:06:54 cgd
** redo csCopyTxFrame() from scratch. It had a fatal flaw: it was blindly
** casting from u_int8_t * to u_int16_t * without worrying about alignment
** issues. This would cause bogus data to be spit out for mbufs with
** misaligned data. For instance, it caused the following bits to appear
** on the wire:
** ... etBND 1S2C .SHA(K) R ...
** 11112222333344445555
** which should have appeared as:
** ... NetBSD 1.2C (SHARK) ...
** 11112222333344445555
** Note the apparent 'rotate' of the bytes in the word, which was due to
** incorrect unaligned accesses. This data corruption was the cause of
** incoming telnet/rlogin hangs.
**
** Revision 1.12 1997/05/22 01:55:32 cgd
** reformat log so it fits in 80cols
**
** Revision 1.11 1997/05/22 01:50:27 cgd
** * enable input packet address checking in the BPF+IFF_PROMISCUOUS case,
** so packets aimed at other hosts don't get sent to ether_input().
** * Add a static const char *rcsid initialized with an RCS Id tag, so that
** you can easily tell (`strings`) what version of the driver is in your
** kernel binary.
** * get rid of ether_cmp(). It was inconsistently used, not necessarily
** safe, and not really a performance win anyway. (It was only used when
** setting up the multicast logical address filter, which is an
** infrequent event. It could have been used in the IFF_PROMISCUOUS
** address check above, but the benefit of it vs. bcmp or memcmp would be
** inconsequential, there.) Use bcmp() instead. Eventually, this should
** use memcmp(), so that the compiler can optimize it into inline code.
** * restructure csStartOuput to avoid the following bugs in the case where
** txWait was being set:
** * it would accidentally drop the outgoing packet if told to wait
** but the outgoing packet queue was empty.
** * it would bpf_mtap() the outgoing packet multiple times (once for
** each time it was told to wait), and would also recalculate
** the length of the outgoing packet each time it was told to
** wait.
** While there, rename txWait to txLoop, since with the new structure of
** the code, the latter name makes more sense.
**
** Revision 1.10 1997/05/19 02:03:20 cgd
** Set RX_CTL in csSetLadrFilt(), rather than csInitChip(). csInitChip()
** is the only caller of csSetLadrFilt(), and always calls it, so this
** ends up being logically the same. In csSetLadrFilt(), if IFF_PROMISC
** is set, enable promiscuous mode (and set IFF_ALLMULTI), otherwise behave
** as before.
**
** Revision 1.9 1997/05/19 01:45:37 cgd
** create a new function, csEtherInput(), which does received-packet
** BPF and ether_input processing. This code used to be in three places,
** and centralizing it will make adding IFF_PROMISC support much easier.
** Also, in csCopyTxFrame(), put it some (currently disabled) code to
** do copies with bus_space_write_region_2(). It's more correct, and
** potentially more efficient. That function needs to be gutted (to
** deal properly with alignment issues, which it currently does wrong),
** however, and the change doesn't gain much, so there's no point in
** enabling it now.
**
** Revision 1.8 1997/05/19 01:17:10 cgd
** fix a comment re: the setting of the TxConfig register. Clean up
** interface counter maintenance (make it use standard idiom).
**
**--
*/
/*
* INCLUDE DEFINITIONS
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/syslog.h>
#include <sys/socket.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <net/if.h>
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_inarp.h>
#endif
#include <vm/vm.h>
#include <machine/kerndebug.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#include <arm32/isa/isadmavar.h>
#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include "if_csvar.h"
#ifdef SHARK
#include <arm32/shark/sequoia.h>
#endif
#ifdef OFW
#include <dev/ofw/openfirm.h>
#endif
/*
* MACRO DEFINITIONS
*
*/
#define CS_OUTPUT_LOOP_MAX 100 /* max times round notorious tx loop */
#define DMA_BUFFER_SIZE 16384 /* 16K or 64K */
#define DMA_STATUS_BITS 0x0007 /* bit masks for checking DMA status */
#define DMA_STATUS_OK 0x0004
#define CS_MEM_SIZE 4096 /* 4096 bytes of on chip memory */
#define ETHER_MTU 1518 /* ETHERMTU is defiend in if_ether.h as 1500
* ie. without the header.
*/
/*
* the kerndebug macros reserve use of the
* first and last byte, so we can only use the
* middle two bytes for flags.
*/
#define CSPROBE_DBG_INFO 0x00000100
#define CSATTACH_DBG_INFO 0x00000200
#define CSRESET_DBG_INFO 0x00000400
#define CSINIT_DBG_INFO 0x00000800
#define CSIOCTL_DBG_INFO 0x00001000
#define CSSTARTTX_DBG_INFO 0x00002000
#define CSTXEVENT_DBG_INFO 0x00004000
#define CSRXNORM_DBG_INFO 0x00008000
#define CSRXEARLY_DBG_INFO 0x00010000
#define CSRXDMA_DBG_INFO 0x00020000
#define CSMCAST_DBG_INFO 0x00040000
#define CSBUFF_DBG_INFO 0x00080000
#define CSUTIL_DBG_INFO 0x00800000
/*
* FUNCTION PROTOTYPES
*/
#ifdef __BROKEN_INDIRECT_CONFIG
int csProbe __P((struct device *, void *, void *));
#else
int csProbe __P((struct device *, struct cfdata *, void *));
#endif
void csAttach __P((struct device *, struct device *, void *));
ushort csReadPacketPage __P(( struct cs_softc *sc, ushort offset ));
void csWritePacketPage __P(( struct cs_softc *sc, ushort offset, ushort value ));
int csGetUnspecifiedParms __P(( struct cs_softc *sc ));
int csValidateParms __P(( struct cs_softc *sc ));
int csGetEthernetAddr __P(( struct cs_softc *sc ));
int csReadEEPROM __P(( struct cs_softc *sc, ushort offset, ushort *pValue ));
int csResetChip __P(( struct cs_softc *sc ));
void csShow __P(( struct cs_softc *sc, int Zap ));
int csInit __P(( struct cs_softc *sc ));
void csReset __P(( void * ));
int csIoctl __P(( struct ifnet *, ulong com, caddr_t pData ));
void csInitChip __P(( struct cs_softc *sc ));
int csIntr __P(( void *arg ));
void csBufferEvent __P(( struct cs_softc *sc, ushort BuffEvent ));
void csTransmitEvent __P(( struct cs_softc *sc, ushort txEvent ));
void csReceiveEvent __P(( struct cs_softc *sc, ushort rxEvent ));
void csEtherInput __P(( struct cs_softc *sc, struct mbuf *m ));
void csProcessReceive __P(( struct cs_softc *sc ));
void csProcessRxEarly __P(( struct cs_softc *sc ));
void csProcessRxDMA(struct cs_softc *sc);
void csStartOutput __P((struct ifnet *ifp ));
void csCopyTxFrame __P(( struct cs_softc *sc, struct mbuf *pMbufChain ));
void csSetLadrFilt( struct cs_softc *sc, struct ethercom *ec );
ushort csHashIndex(char *addr);
void csCounterEvent( struct cs_softc *sc, ushort cntEvent );
/*
* GLOBAL DECLARATIONS
*/
/*
* Xmit-early table.
*
* To get better performance, we tell the chip to start packet
* transmission before the whole packet is copied to the chip.
* However, this can fail under load. When it fails, we back off
* to a safer setting for a little while.
*
* txcmd is the value of txcmd used to indicate when to start transmission.
* better is the next 'better' state in the table.
* better_count is the number of output packets before transition to the
* better state.
* worse is the next 'worse' state in the table.
*
* Transition to the next worse state happens automatically when a
* transmittion underrun occurs.
*/
struct cs_xmit_early {
u_int16_t txcmd;
int better;
int better_count;
int worse;
} cs_xmit_early_table[3] = {
{ TX_CMD_START_381, 0, INT_MAX, 1, },
{ TX_CMD_START_1021, 0, 50000, 2, },
{ TX_CMD_START_ALL, 1, 5000, 2, },
};
/*
* this is static config information, the configuration file
* expects this struct to exist and contain the probe and attach
* functions, and softc size for allocation.
*/
struct cfattach cs_ca =
{
sizeof(struct cs_softc), csProbe, csAttach
};
extern struct cfdriver cs_cd;
int csdebug = 0x00000000; /* debug status, used with kerndebug macros */
#ifndef SHARK
/* SHARKS don't have the kernel in the lower megs of physical
* and hence this won't work. I don't know any other machines that
* this won't work for (I didn't look either) so I ifdef'd it like this.
*/
char cs_dma_buffer[DMA_BUFFER_SIZE] __attribute__ ((aligned (4096)));
#endif
#ifdef __BROKEN_INDIRECT_CONFIG
/*-------------------------------------------------------------------------*/
/*
** Function : csProbe.
**
** Description : This function will be called at system startup
** if the kernel is configured for
** cs8900 ethernet devices. The probe will
** check if a cs8900 ethernet device is plugged
** into the isa bus.
**
** Parameters : struct device *parent unused parent device
** void *match is a cs_softc structure
** void *aux is a pointer to isa_attach_args
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : 1 if the device is found 0 if not.
**
** Side Effects : None.
*/
int csProbe(struct device *parent, void *match, void *aux)
#else
int csProbe(struct device *parent, struct cfdata *cf, void *aux)
#endif
{
#ifdef __BROKEN_INDIRECT_CONFIG
struct cs_softc *sc = match;
#else
struct cs_softc junksc;
struct cs_softc *sc = &junksc;
#endif
struct isa_attach_args *ia = aux; /* this holds info statically
* configured in the config file
*/
ushort isaId;
sc->sc_iobase = ia->ia_iobase; /* store the IO base address for later use */
sc->sc_drq = ia->ia_drq; /* store the drq line */
sc->inMemoryMode = FALSE; /* must work in IO mode initially */
/* do bus handle stuff
*/
sc->sc_iot = ia->ia_iot;
sc->sc_memt = ia->ia_memt;
/* map the bus space here and put the arguments in the softc
* so that the csRead/WritePacketPage can operate.
*/
if ( bus_space_map(sc->sc_iot, ia->ia_iobase, CS8900_IOSIZE, 0,
&sc->sc_ioh) != 0 )
{
return 0; /* bus map didn't work, not much we can do */
}
/* Verify that we can read from the chip */
isaId = csReadPacketPage( sc, PKTPG_EISA_NUM );
/* Verify that the chip is a Crystal Semiconductor chip */
if ( isaId != EISA_NUM_CRYSTAL )
{
/* unmap the bus space and clear the softc
* structure.
*/
bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE);
sc->sc_iot = 0;
sc->sc_ioh = 0;
printf("\n%s - Chip is not a Crystal Semiconductor chip\n",
(sc->sc_dev).dv_xname);
return 0;
}
/* Verify that the chip is a CS8900 */
if ( (csReadPacketPage( sc, PKTPG_PRODUCT_ID ) & PROD_ID_MASK )
!= PROD_ID_CS8900 )
{
/* unmap the bus space and clear the softc
* structure.
*/
bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE);
sc->sc_iot = 0;
sc->sc_ioh = 0;
printf("\n%s - Chip is not a CS8900\n", (sc->sc_dev).dv_xname);
return 0;
}
#ifdef __BROKEN_INDIRECT_CONFIG
/* because of the BROKEN_INDIRECT...
* macro must unmap the bus space here.
*/
bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE);
#endif
ia->ia_iosize = CS8900_IOSIZE; /* this is not passed to us for some reason,
* so we just hardcode it here
*/
return 1; /* chip has been located can now attach it */
}
/*-------------------------------------------------------------------------*/
/*
** Function : csAttach.
**
** Description : This function will be called at system startup
** time if the kernel is configured for
** cs8900 ethernet devices and one has been found
** on the isa bus by the probe. The attach will
** will register the device with the system structures
** so that upper layer protocols can access the network.
**
** Parameters : struct device *parent unused parent device
** struct device *self is actually a softc struct
** void *aux is a pointer to isa_attach_args
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : None.
**
** Side Effects : The device is registered as an ether device on
** an isa bus.
*/
void csAttach(struct device *parent, struct device *self, void *aux)
{
struct cs_softc *sc = (struct cs_softc *)self;
struct isa_attach_args *ia = aux;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
#ifndef __BROKEN_INDIRECT_CONFIG
sc->sc_iobase = ia->ia_iobase;
sc->sc_drq = ia->ia_drq;
sc->inMemoryMode = FALSE;
sc->sc_iot = ia->ia_iot;
sc->sc_memt = ia->ia_memt;
if (bus_space_map(sc->sc_iot, ia->ia_iobase,
ia->ia_iosize, 0, &sc->sc_ioh) != 0)
{
panic("csAttach: bus space map failed");
}
#endif
printf(": %s Ethernet\n", sc->sc_dev.dv_xname);
/* the first thing to do is check that the mbuf cluster size is
* greater than the MTU for an ethernet frame. The code depends
* on this and to port this to a OS where this was not the case
* would not be straightforward.
*/
if (MCLBYTES < ETHER_MTU)
{
panic("%s : Attach failed -> machines mbuf cluster size is insufficient\n",
sc->sc_dev.dv_xname);
}
/* save relevant data passed in through the isa_attach_args structure */
sc->sc_int = ia->ia_irq;
/* this driver only supports mapped memory and DMA modes of
* operation. So if a memory base address is not given we can't
* go on.
*/
if (ia->ia_maddr != MADDRUNK)
{
sc->pPacketPagePhys = ia->ia_maddr;
}
else
{
panic("%s : Attach failed -> configuration info incomplete, no maddr\n",
sc->sc_dev.dv_xname);
}
/* Start out in IO mode */
sc->inMemoryMode = FALSE;
/* Start out not transmitting */
sc->txInProgress = FALSE;
/* Set up early transmit threshhold */
sc->sc_xe_ent = 0;
sc->sc_xe_togo = cs_xmit_early_table[sc->sc_xe_ent].better_count;
/* Initialize ifnet structure. */
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_start = csStartOutput;
ifp->if_ioctl = csIoctl;
ifp->if_watchdog = NULL; /* no watchdog at this stage */
ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS |
IFF_BROADCAST | IFF_MULTICAST;
#ifdef OFW
/* Dig MAC address out of the firmware. */
if (OF_getprop((int)ia->ia_aux, "mac-address", sc->sc_enaddr,
sizeof(sc->sc_enaddr)) < 0) {
int ofnet_handle;
ofnet_handle = OF_open("/isa/ethernet");
if (!ofnet_handle)
panic("OF_open: Unable to open /isa/ethernet");
if (OF_getprop((int)ia->ia_aux, "mac-address",
sc->sc_enaddr,
sizeof(sc->sc_enaddr)) < 0)
panic("csAttach: no mac-address");
/* closing the device causes the kernel to panic ... */
#if 0
OF_close(ofnet_handle);
#endif
}
sc->mediaType = MEDIA_10BASET;
#else
/* Get parameters, which were not specified, from the EEPROM */
if ( csGetUnspecifiedParms(sc) == CS_ERROR )
{
printf("%s : Couldn't get the unspecified parameters in the attach\n",\
sc->sc_dev.dv_xname);
return ;
}
/* Verify that parameters are valid */
if ( csValidateParms(sc) == CS_ERROR )
{
printf("%s : Couldn't get the validate parameters in the attach\n",\
sc->sc_dev.dv_xname );
return ;
}
/* Get and store the Ethernet address */
if ( csGetEthernetAddr(sc) == CS_ERROR )
{
printf("%s : couldn't get the ethernet address in the attach\n",\
sc->sc_dev.dv_xname );
return ;
}
#endif
printf("%s : address %s\n", sc->sc_dev.dv_xname,
ether_sprintf(sc->sc_enaddr));
/*
* First figure out which memory mode to use, by:
* 1. if we have a valid drq then use DMA only mode
* for receives. Chip only supports 5,6 & 7
* 2. else we have been given a memory address for IO
* mapping then use it.
*/
if ( (sc->sc_drq >= 5) && (sc->sc_drq <= 7) )
{
/* The sharks can't use the cs_dma_buffer because
* kernel is not placed in the lower megs of physical
* memory. Instead they call a function which returns
* an offset into the isaphysmem, which is a block
* of memory allocated at boot-time for use as
* DMA bounce buffers. The SHARKS allocate another
* 64Kbytes for a possible large buffer and allow
* access to it through isa_dmabuffer_get() defined
* in isadmavar.h
*/
#ifdef SHARK
/* If we get the buffer we never relinquish it,
* primarily because I expect the ethernet
* would be required always.
*/
sc->dmaBase = (char *) isa_dmabuffer_get();
#else
sc->dmaBase = cs_dma_buffer;
#endif
/* if we couldn't get a dma buffer then we can't
* go on with DMA.
*/
if ( sc->dmaBase != NULL )
{
KERN_DEBUG(csdebug, CSATTACH_DBG_INFO,
("csAttach : alloced memory address = 0x%x\n"\
,(int) sc->dmaBase));
sc->dmaMemSize = CS8900_DMA_BUFFER_SIZE;
/* now set the config flag and let the csResetChip and csInitChip
* routines do the rest.
*/
KERN_DEBUG(csdebug, CSATTACH_DBG_INFO,
("csAttach : setting DMA mode\n"));
sc->configFlags |= CFGFLG_DMA_MODE;
}
/* now go on and try mapped memory, or programmed IO. */
} /* if (drq) */
/* the default EEPROM configuration is to use programmed IO. If
* we want to use mapped memory we must manually set the
* chips registers.
*
* check here to see if we are configured to use mapped memory.
*/
if ( sc->pPacketPagePhys != MADDRUNK )
{
/* check that the configured IO memory size is 4K
* otherwise hardcode the driver to use programmed IO.
*/
if ( ia->ia_msize != CS_MEM_SIZE )
{
panic(": %s IO mem size is not CS_MEM_SIZE invalid configuration\n"
, (sc->sc_dev).dv_xname);
}
else
{
/* map the memory addresses */
if (bus_space_map(sc->sc_memt, sc->pPacketPagePhys,
CS_MEM_SIZE, 0, &sc->sc_memh) != 0)
{
panic("%s : csAttach : bus space map failed\n",
(sc->sc_dev).dv_xname);
}
else
{
/* I think we always want to use the external latch logic */
sc->configFlags = sc->configFlags | CFGFLG_MEM_MODE |
CFGFLG_USE_SA | CFGFLG_IOCHRDY;
}
}
}
/* Attach the interface. */
if_attach(ifp);
/* add to generic ethernet code */
ether_ifattach(ifp, sc->sc_enaddr);
/* attach the Berkely Packet Filter interface */
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
sc->sc_sh = shutdownhook_establish(csReset, sc);
if (sc->sc_sh == NULL)
{
panic("csAttach: can't establish shutdownhook");
}
/* Setup the interrupt handler */
sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_LEVEL,
IPL_NET, csIntr, sc);
/* Reset the chip */
if ( csResetChip(sc) == CS_ERROR )
{
panic("\n%s - Can not reset the ethernet chip\n",
(sc->sc_dev).dv_xname );
}
return ;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csReadPacketPage
**
** Description : This routine reads a word from the PacketPage at
** the specified offset.
**
** This routine should be maually in-lined as required
** for fast loops reading from the chip. ie. the
** loop should be inside the if (memory-mode) statement.
**
** Parameters : struct cs_softc *sc
** ushort offset is the offset in the PacketPage to
** read
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : The 16 bit PacketPage register.
**
** Side Effects : None.
**
*/
ushort csReadPacketPage( struct cs_softc *sc, ushort offset )
{
if ( sc->inMemoryMode )
{
return bus_space_read_2( sc->sc_memt, sc->sc_memh, offset );
}
else /* In IO mode */
{
bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_PTR, offset );
return bus_space_read_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_DATA );
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csWritePacketPage
**
** Description : This routine writes a value to the PacketPage at
** the specified offset.
**
** This routine should be maually in-lined as required
** for fast loops writing to the chip.
**
** Parameters : struct cs_softc *sc
** offset, PacketPage register offset
** value, value to write to the register
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csWritePacketPage( struct cs_softc *sc, ushort offset, ushort value )
{
if ( sc->inMemoryMode )
{
bus_space_write_2( sc->sc_memt, sc->sc_memh, offset, value );
}
else /* In IO mode */
{
bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_PTR, offset );
bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_DATA, value );
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csGetUnspecifiedParms
**
** Description : This routine gets parameters, that were not
** specifed to csAttach(), from the EEPROM and puts
** them in the cs_softc structure. If all the
** parameters were specified in csAttach(), then this
** routine does not attempt to read the EEPROM.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : CS_ERROR if it can't get the info from the EEPROM,
** else CS_OK.
**
** Side Effects : None.
**
*/
int csGetUnspecifiedParms( struct cs_softc *sc )
{
ushort selfStatus;
ushort isaConfig;
ushort memBase;
ushort adapterConfig;
ushort xmitCtl;
/* If all of these parameters were specified */
if ( sc->configFlags != 0 && sc->pPacketPagePhys != MADDRUNK
&& sc->sc_int != 0 && sc->mediaType != 0 )
{
KERN_DEBUG(csdebug, CSUTIL_DBG_INFO,
("csGetUnspecifiedParms : have got everything already\n"));
return CS_OK; /* Don't need to get anything from the EEPROM */
}
/* Verify that the EEPROM is present and OK */
selfStatus = csReadPacketPage( sc, PKTPG_SELF_ST );
if ( !((selfStatus & SELF_ST_EEP_PRES) && (selfStatus & SELF_ST_EEP_OK)) )
{
printf("\n%s - EEPROM is missing or bad\n", (sc->sc_dev).dv_xname);
return CS_ERROR;
}
/* Get ISA configuration from the EEPROM */
if ( csReadEEPROM(sc, EEPROM_ISA_CFG, &isaConfig) == CS_ERROR )
return CS_ERROR;
/* Get adapter configuration from the EEPROM */
if ( csReadEEPROM(sc, EEPROM_ADPTR_CFG, &adapterConfig) == CS_ERROR )
return CS_ERROR;
/* Get transmission control from the EEPROM */
if ( csReadEEPROM(sc, EEPROM_XMIT_CTL, &xmitCtl) == CS_ERROR )
return CS_ERROR;
/* If the configuration flags were not specified */
if ( sc->configFlags == 0 )
{
/* Copy the memory mode flag */
if ( isaConfig & ISA_CFG_MEM_MODE )
sc->configFlags |= CFGFLG_MEM_MODE;
/* Copy the USE_SA flag */
if ( isaConfig & ISA_CFG_USE_SA )
sc->configFlags |= CFGFLG_USE_SA;
/* Copy the IO Channel Ready flag */
if ( isaConfig & ISA_CFG_IOCHRDY )
sc->configFlags |= CFGFLG_IOCHRDY;
/* Copy the DC/DC Polarity flag */
if ( adapterConfig & ADPTR_CFG_DCDC_POL )
sc->configFlags |= CFGFLG_DCDC_POL;
/* Copy the Full Duplex flag */
if ( xmitCtl & XMIT_CTL_FDX )
sc->configFlags |= CFGFLG_FDX;
}
/* If the PacketPage pointer was not specified */
if ( sc->pPacketPagePhys == MADDRUNK )
{
/* If memory mode is enabled */
if ( sc->configFlags & CFGFLG_MEM_MODE )
{
/* Get the memory base address from EEPROM */
if ( csReadEEPROM(sc,EEPROM_MEM_BASE,&memBase) == CS_ERROR )
return CS_ERROR;
memBase &= MEM_BASE_MASK; /* Clear unused bits */
/* Setup the PacketPage pointer */
sc->pPacketPagePhys = ( ( (ulong) memBase) << 8 );
}
}
/* If the interrupt level was not specified */
if ( sc->sc_int == 0 )
{
/* Get the interrupt level from the ISA config */
sc->sc_int = isaConfig & ISA_CFG_IRQ_MASK;
if ( sc->sc_int == 3 )
sc->sc_int = 5;
else
sc->sc_int += 10;
}
/* If the media type was not specified */
if ( sc->mediaType == 0 )
{
switch( adapterConfig & ADPTR_CFG_MEDIA )
{
case ADPTR_CFG_AUI:
sc->mediaType = MEDIA_AUI;
break;
case ADPTR_CFG_10BASE2:
sc->mediaType = MEDIA_10BASE2;
break;
case ADPTR_CFG_10BASET:
default:
sc->mediaType = MEDIA_10BASET;
break;
}
}
return CS_OK;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csValidateParms
**
** Description : This routine verifies that the memory address,
** interrupt level and media type are valid. If any
** of these parameters are invalid, then an error
** message is printed and CS_ERROR is returned.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : returns CS_ERROR if any of the parameters are invalid
**
** Side Effects : None.
**
*/
int csValidateParms( struct cs_softc *sc )
{
int memAddr; /* this is an int so that we can
* do integer manipulation, not
* allowed on pointers.
*/
memAddr = sc->pPacketPagePhys;
if ( (memAddr & 0x000FFF) != 0 )
{
printf("\n%s - Memory address (0x%X) must start on a 4K boundary\n",
(sc->sc_dev).dv_xname, memAddr );
return CS_ERROR;
}
/* check if beyond 16M ISA space */
if ( memAddr > 0xFFF000 )
{
printf("\n%s - Memory address (0x%X) is too large\n",
(sc->sc_dev).dv_xname, memAddr );
return CS_ERROR;
}
if ( !(sc->sc_int==5 || sc->sc_int==10 || sc->sc_int==11 ||
sc->sc_int==12) )
{
printf("\n%s - Interrupt level (%d) is invalid\n",
(sc->sc_dev).dv_xname, sc->sc_int );
return CS_ERROR;
}
if ( !(sc->mediaType==MEDIA_AUI || sc->mediaType==MEDIA_10BASE2 ||
sc->mediaType==MEDIA_10BASET) )
{
printf("\n%s - Media type (%d) is invalid\n",
(sc->sc_dev).dv_xname, sc->mediaType );
return CS_ERROR;
}
return CS_OK;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csGetEthernetAddr
**
** Description : Read the Ethernet address from the EEPROM
** and save it in the arpcom structure.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : returns CS_ERROR if EEPROM can't be read.
**
** Side Effects : None.
**
*/
int csGetEthernetAddr( struct cs_softc *sc)
{
ushort selfStatus;
pia pIA;
/* Setup pointer of where to store the Ethernet address */
pIA = (pia) sc->sc_enaddr;
/* Verify that the EEPROM is present and OK */
selfStatus = csReadPacketPage( sc, PKTPG_SELF_ST );
if ( !((selfStatus & SELF_ST_EEP_PRES) && (selfStatus & SELF_ST_EEP_OK)))
{
printf("\n%s - EEPROM is missing or bad\n", (sc->sc_dev).dv_xname);
return CS_ERROR;
}
/* Get Ethernet address from the EEPROM */
/* XXX this will likely lose on a big-endian machine. -- cgd */
if ( csReadEEPROM(sc,EEPROM_IND_ADDR_H,&pIA->word[0]) == CS_ERROR )
return CS_ERROR;
if ( csReadEEPROM(sc,EEPROM_IND_ADDR_M,&pIA->word[1]) == CS_ERROR )
return CS_ERROR;
if ( csReadEEPROM(sc,EEPROM_IND_ADDR_L,&pIA->word[2]) == CS_ERROR )
return CS_ERROR;
return CS_OK;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csResetChip
**
** Description : This routine resets the CS8900 chip. It
** will wait until the chip is ready for use again.
**
** Parameters : struct cs_softc sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : Returns error if can't read from the chip.
**
** Side Effects : Memory mode will always be programmed IO after
** a reset.
**
*/
int csResetChip( struct cs_softc *sc )
{
int intState;
int x;
/* Disable interrupts at the CPU so reset command is atomic */
intState = splimp();
/* We are now resetting the chip
*
* A spurious interrupt is generated by the
* chip when it is reset. This variable
* informs the interrupt handler to ignore
* this interrupt.
*/
sc->resetting = TRUE;
/* Issue a reset command to the chip */
csWritePacketPage( sc, PKTPG_SELF_CTL, SELF_CTL_RESET );
/* Re-enable interrupts at the CPU */
splx( intState );
/* The chip is always in IO mode after a reset */
sc->inMemoryMode = FALSE;
/* If transmission was in progress, it is not now */
sc->txInProgress = FALSE;
/*
* there was a delay(125); here, but it seems uneccesary
* 125 usec is 1/8000 of a second, not 1/8 of a second.
* the data sheet advises 1/10 of a second here, but the
* SI_BUSY and INIT_DONE loops below should be sufficient.
*/
/* Transition SBHE to switch chip from 8-bit to 16-bit */
bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR );
bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR + 1 );
bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR );
bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR + 1 );
/* Wait until the EEPROM is not busy */
for ( x = 0; x < MAXLOOP; x++ )
{
if ( !(csReadPacketPage(sc, PKTPG_SELF_ST ) & SELF_ST_SI_BUSY) )
break;
}
if ( x == MAXLOOP )
return CS_ERROR;
/* Wait until initialization is done */
for ( x = 0; x < MAXLOOP; x++ )
{
if ( csReadPacketPage(sc, PKTPG_SELF_ST ) & SELF_ST_INIT_DONE )
break;
}
if ( x == MAXLOOP )
return CS_ERROR;
/* Reset is no longer in progress */
sc->resetting = FALSE;
return CS_OK;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csReadEEPROM
**
** Description : This routine reads a word from the EEPROM at the
** specified offset.
**
** Parameters : struct cs_softc *sc,
** ushort offset, into the EEPROM
** ushort *pValue, EEPROM data is placed here
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : CS_OK, else CS_ERROR if can't read from the EEPROM.
**
** Side Effects : None.
**
*/
int csReadEEPROM( struct cs_softc *sc, ushort offset, ushort *pValue )
{
int x;
/* Ensure that the EEPROM is not busy */
for ( x=0; x < MAXLOOP; x++ )
{
if ( !(csReadPacketPage(sc, PKTPG_SELF_ST) & SELF_ST_SI_BUSY) )
break;
}
if ( x == MAXLOOP )
{
printf("\n%s - Can not read from EEPROM\n", (sc->sc_dev).dv_xname);
return CS_ERROR;
}
/* Issue the command to read the offset within the EEPROM */
csWritePacketPage( sc, PKTPG_EEPROM_CMD, offset | EEPROM_CMD_READ );
/* Wait until the command is completed */
for ( x=0; x < MAXLOOP; x++ )
{
if ( !(csReadPacketPage(sc, PKTPG_SELF_ST) & SELF_ST_SI_BUSY) )
break;
}
if ( x == MAXLOOP )
{
printf("\n%s - Can not read from EEPROM\n", (sc->sc_dev).dv_xname);
return CS_ERROR;
}
/* Get the EEPROM data from the EEPROM Data register */
*pValue = csReadPacketPage( sc, PKTPG_EEPROM_DATA );
return CS_OK;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csInitChip
**
** Description : This routine uses the instance global variables
** in the cs_softc structure to initialize the CS8900
** chip.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : None.
**
** Side Effects : None.
**
*/
void csInitChip( struct cs_softc *sc )
{
ushort busCtl;
ushort selfCtl;
pia pIA;
ushort isaId;
/* Disable reception and transmission of frames */
csWritePacketPage( sc, PKTPG_LINE_CTL,
csReadPacketPage(sc,PKTPG_LINE_CTL) &
~LINE_CTL_RX_ON & ~LINE_CTL_TX_ON );
/* Disable interrupt at the chip */
csWritePacketPage( sc, PKTPG_BUS_CTL,
csReadPacketPage(sc,PKTPG_BUS_CTL) & ~BUS_CTL_INT_ENBL );
/* If IOCHRDY is enabled then clear the bit in the busCtl register */
busCtl = csReadPacketPage( sc, PKTPG_BUS_CTL );
if ( sc->configFlags & CFGFLG_IOCHRDY )
{
csWritePacketPage( sc, PKTPG_BUS_CTL, busCtl & ~BUS_CTL_IOCHRDY );
}
else
{
csWritePacketPage( sc, PKTPG_BUS_CTL, busCtl | BUS_CTL_IOCHRDY );
}
/* Set the Line Control register to match the media type */
if ( sc->mediaType == MEDIA_10BASET )
{
csWritePacketPage( sc, PKTPG_LINE_CTL, LINE_CTL_10BASET );
}
else
{
csWritePacketPage( sc, PKTPG_LINE_CTL, LINE_CTL_AUI_ONLY );
}
/* Set the BSTATUS/HC1 pin to be used as HC1 */
/* HC1 is used to enable the DC/DC converter */
selfCtl = SELF_CTL_HC1E;
/* If the media type is 10Base2 */
if ( sc->mediaType == MEDIA_10BASE2 )
{
/* Enable the DC/DC converter */
/* If the DC/DC converter has a low enable */
if ( (sc->configFlags & CFGFLG_DCDC_POL) == 0 )
/* Set the HCB1 bit, which causes the HC1 pin to go low */
selfCtl |= SELF_CTL_HCB1;
}
else /* Media type is 10BaseT or AUI */
{
/* Disable the DC/DC converter */
/* If the DC/DC converter has a high enable */
if ( (sc->configFlags & CFGFLG_DCDC_POL) != 0 )
{
/* Set the HCB1 bit, which causes the HC1 pin to go low */
selfCtl |= SELF_CTL_HCB1;
}
}
csWritePacketPage( sc, PKTPG_SELF_CTL, selfCtl );
/* If media type is 10BaseT */
if ( sc->mediaType == MEDIA_10BASET )
{
/* If full duplex mode then set the FDX bit in TestCtl register */
if ( sc->configFlags & CFGFLG_FDX )
{
KERN_DEBUG(csdebug, CSINIT_DBG_INFO,
("csInitChip : in full duplex mode\n"));
csWritePacketPage( sc, PKTPG_TEST_CTL, TEST_CTL_FDX );
}
}
/* RX_CTL set in csSetLadrFilt(), below */
/* enable all transmission interrupts */
csWritePacketPage( sc, PKTPG_TX_CFG, TX_CFG_ALL_IE );
/* Accept all receive interrupts */
csWritePacketPage( sc, PKTPG_RX_CFG, RX_CFG_ALL_IE );
/*
* Configure Operational Modes
*
* I have turned off the BUF_CFG_RX_MISS_IE, to speed things up, this is
* a better way to do it because the card has a counter which can be
* read to update the RX_MISS counter. This saves many interupts.
*
* I have turned on the tx and rx overflow interupts to counter using
* the receive miss interrupt. This is a better estimate of errors
* and requires lower system overhead.
*/
csWritePacketPage( sc, PKTPG_BUF_CFG, BUF_CFG_TX_UNDR_IE |
BUF_CFG_RX_DMA_IE );
if (sc->configFlags & CFGFLG_DMA_MODE)
{
/*
* First we program the DMA controller
* and ensure the memory buffer is valid.
* If it isn't then we just go on without DMA
*/
isa_dmastart(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, sc->dmaMemSize,
sc->sc_drq );
sc->dma_offset = sc->dmaBase;
/* interrupt when a DMA'd frame is received */
csWritePacketPage( sc, PKTPG_RX_CFG, RX_CFG_ALL_IE
| RX_CFG_RX_DMA_ONLY );
/* set the DMA burst bit so we don't tie up the bus
* for too long.
*/
if ( sc->dmaMemSize == 16384 )
{
KERN_DEBUG(csdebug, CSINIT_DBG_INFO,
("csInitChip : using 16K buffer\n"));
csWritePacketPage( sc, PKTPG_BUS_CTL,
( (csReadPacketPage(sc, PKTPG_BUS_CTL) \
& ~BUS_CTL_DMA_SIZE) | BUS_CTL_DMA_BURST));
}
else /* use 64K */
{
csWritePacketPage( sc, PKTPG_BUS_CTL,
csReadPacketPage(sc, PKTPG_BUS_CTL) \
| BUS_CTL_DMA_SIZE | BUS_CTL_DMA_BURST);
}
csWritePacketPage( sc, PKTPG_DMA_CHANNEL, sc->sc_drq - 5);
}
/* If memory mode is enabled */
if ( sc->configFlags & CFGFLG_MEM_MODE )
{
KERN_DEBUG(csdebug , CSINIT_DBG_INFO,
("csInitChip : Using Memory Mode\n"));
/* If external logic is present for address decoding */
if ( csReadPacketPage(sc,PKTPG_SELF_ST) & SELF_ST_EL_PRES )
{
/* Program the external logic to decode address bits SA20-SA23 */
csWritePacketPage( sc, PKTPG_EEPROM_CMD,
( (sc->pPacketPagePhys & 0xffffff) >> 20) |
EEPROM_CMD_ELSEL );
}
/* Setup chip for memory mode */
/* Write the packet page base physical address to the memory
* base register.
*/
csWritePacketPage( sc, PKTPG_MEM_BASE,
sc->pPacketPagePhys & 0xFFFF );
csWritePacketPage( sc, PKTPG_MEM_BASE + 2,
sc->pPacketPagePhys >> 16 );
busCtl = BUS_CTL_MEM_MODE;
/* tell the chip to read the addresses off the SA pins */
if ( sc->configFlags & CFGFLG_USE_SA )
{
busCtl |= BUS_CTL_USE_SA;
}
csWritePacketPage( sc, PKTPG_BUS_CTL,
csReadPacketPage(sc, PKTPG_BUS_CTL) | busCtl );
/* We are in memory mode now! */
sc->inMemoryMode = TRUE;
/* wait here (10ms) for the chip to swap over.
* this is the maximum time that this could take.
*/
delay(10000);
/* Verify that we can read from the chip */
isaId = csReadPacketPage( sc, PKTPG_EISA_NUM );
/* As a last minute sanity check before actually using mapped memory
* we verify that we can read the isa number from the chip
* in memory mode.
*/
if ( isaId != EISA_NUM_CRYSTAL )
{
panic("\n%s - Mapped memory mode failed.\n",
(sc->sc_dev).dv_xname);
/* nope didn't work abandon the idea and use pIO */
sc->inMemoryMode = FALSE;
}
else
{
/* we are in memory mode so if we aren't using DMA, then
* program the chip to interrupt early.
*/
if ( (sc->configFlags & CFGFLG_DMA_MODE) == 0 )
{
KERN_DEBUG(csdebug, CSINIT_DBG_INFO,
("csInitChip : about to turn on early interrupts\n"));
csWritePacketPage(sc, PKTPG_BUF_CFG, BUF_CFG_RX_DEST_IE |
BUF_CFG_RX_MISS_OVER_IE |
BUF_CFG_TX_COL_OVER_IE );
}
}
} /* if configFlags=memory mapped */
/* Put Ethernet address into the Individual Address register */
pIA = (pia)sc->sc_enaddr;
csWritePacketPage( sc, PKTPG_IND_ADDR, pIA->word[0] );
csWritePacketPage( sc, PKTPG_IND_ADDR+2, pIA->word[1] );
csWritePacketPage( sc, PKTPG_IND_ADDR+4, pIA->word[2] );
/* Set the interrupt level in the chip */
if ( sc->sc_int == 5 )
{
csWritePacketPage( sc, PKTPG_INT_NUM, 3 );
}
else
{
csWritePacketPage( sc, PKTPG_INT_NUM, (sc->sc_int)-10 );
}
/* write the multicast mask to the address filter register */
csSetLadrFilt(sc, &sc->sc_ethercom);
/* Enable reception and transmission of frames */
csWritePacketPage( sc, PKTPG_LINE_CTL,
csReadPacketPage(sc,PKTPG_LINE_CTL) |
LINE_CTL_RX_ON | LINE_CTL_TX_ON );
/* Enable interrupt at the chip */
csWritePacketPage( sc, PKTPG_BUS_CTL,
csReadPacketPage(sc,PKTPG_BUS_CTL) | BUS_CTL_INT_ENBL );
}
/*-------------------------------------------------------------------------*/
/*
** Function : csInit
**
** Description : This routine is called
** to initialize this network interface driver. This
** routine may be called several times for each
** operating system reboot to dynamically bring the
** network interface driver to an up and running state.
** This routine is called by the csIoctl() routine.
**
** This routine resets and then initializes the
** CS8900 chip.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : CS_ERROR if can't read from the chip.
**
** Side Effects : None.
**
*/
int csInit( struct cs_softc *sc )
{
int intState;
int error = CS_OK;
intState = splimp();
/* Mark the interface as down */
sc->sc_ethercom.ec_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
/* Enable debugging */
#ifdef CS_DEBUG
sc->sc_ethercom.ec_if.if_flags |= IFF_DEBUG;
#endif
/* Reset the chip */
if ( (error = csResetChip(sc)) == CS_OK )
{
/* Initialize the chip */
csInitChip( sc );
/* Mark the interface as up and running */
sc->sc_ethercom.ec_if.if_flags |= (IFF_UP | IFF_RUNNING);
sc->sc_ethercom.ec_if.if_flags &= ~IFF_OACTIVE;
sc->sc_ethercom.ec_if.if_timer = 0;
}
else
{
printf("csInit : couldn't reset the chip!\n");
}
splx(intState);
return error;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csSetLadrFilt
**
** Description : Reads the multicast addresses from the sc_enaddr
** structure and programs the cs8900's logical
** address filter to accept the frames.
**
** This function should only be called when the chip is
** being reset, it will overwrite whatever is in the
** logical address filter register.
**
** Parameters : struct ethercom
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value :
**
** Side Effects : The logical address filter register on the
** cs8900 is reprogrammed, any addresses which
** were set will be cleared unless they are still
** in the multicast list.
**
*/
void csSetLadrFilt( struct cs_softc *sc, struct ethercom *ec )
{
struct ifnet *ifp = &(ec->ec_if);
struct ether_multi *enm;
struct ether_multistep step;
ushort af[4];
ushort port, mask, index;
/*
* Set up multicast address filter by passing all multicast addresses
* through a crc generator, and then using the high order 6 bits as an
* index into the 64 bit logical address filter. The high order bit
* selects the word, while the rest of the bits select the bit within
* the word.
*
*/
if (ifp->if_flags & IFF_PROMISC)
{
/* accept all valid frames. */
csWritePacketPage( sc, PKTPG_RX_CTL,
RX_CTL_PROMISC_A | RX_CTL_RX_OK_A |
RX_CTL_IND_A | RX_CTL_BCAST_A | RX_CTL_MCAST_A );
ifp->if_flags |= IFF_ALLMULTI;
return;
}
/* accept frames if a. crc valid, b. individual address match
* c. broadcast address,and d. multicast addresses matched in the
* hash filter
*/
csWritePacketPage( sc, PKTPG_RX_CTL,
RX_CTL_RX_OK_A | RX_CTL_IND_A | RX_CTL_BCAST_A |
RX_CTL_MCAST_A );
/* start off with all multicast flag clear, set it if we need to later,
* otherwise we will leave it.
*/
ifp->if_flags &= ~IFF_ALLMULTI;
af[0] = af[1] = af[2] = af[3] = 0x0000;
ETHER_FIRST_MULTI(step, ec, enm);
/*
* Loop through all the multicast addresses unless we get a range
* of addresses, in which case we will just accept all packets.
* Justification for this is given in the next comment.
*/
while ( (enm != NULL) && ( (ifp->if_flags | IFF_ALLMULTI) == 0) )
{
if (bcmp(enm->enm_addrlo, enm->enm_addrhi, sizeof enm->enm_addrlo))
{
/*
* We must listen to a range of multicast addresses.
* For now, just accept all multicasts, rather than
* trying to set only those filter bits needed to match
* the range. (At this time, the only use of address
* ranges is for IP multicast routing, for which the
* range is big enough to require all bits set.)
*/
ifp->if_flags |= IFF_ALLMULTI;
af[0] = af[1] = af[2] = af[3] = 0xffff;
}
else
{
/*
* we have got an individual address so just set that
* bit.
*/
index = csHashIndex(enm->enm_addrlo);
/* Set the bit the Logical address filter. */
port = (ushort) ( 2 * ((index / 16)) );
mask = (ushort) ( 1 << (15 - (index % 16)) );
af[port] |= mask;
ETHER_NEXT_MULTI(step, enm);
}
}
/* now program the chip with the addresses */
csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[0]);
csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[1]);
csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[2]);
csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[3]);
return;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csHashIndex.
**
** Description : This function calculates Hash index for address.
** This code is chip specific, I pulled it from the
** SCO Unix driver I think. I could not find this
** algorithm anywhere in the manuals.
**
** Parameters : char *addr Pointer to address.
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : The corresponding bit number in the logical
** address filter corresponding to addr.
**
** Side Effects : None.
*/
ushort csHashIndex(char *addr)
{
uint POLY = 0x04c11db6;
uint crc_value = 0xffffffff;
ushort hash_code = 0;
int i;
uint current_bit;
char current_byte = *addr;
uint cur_crc_high;
for ( i = 0; i < 6; i++)
{
current_byte = *addr;
addr++;
for(current_bit = 8; current_bit; current_bit--)
{
cur_crc_high = crc_value >> 31;
crc_value <<= 1;
if(cur_crc_high ^ (current_byte & 0x01))
{
crc_value ^= POLY;
crc_value |= 0x00000001;
}
current_byte >>= 1;
}
}
/*
** The hash code is the 6 least significant bits of the CRC
** in the reverse order: CRC[0] = hash[5],CRC[1] = hash[4],etc.
*/
for(i=0; i < 6; i++)
{
hash_code = (ushort)( (hash_code << 1) |
(ushort)((crc_value >> i) & 0x00000001));
}
return hash_code;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csReset
**
** Description : This routine is a major entry point and is called
** by the protocol stack to shut down this network
** interface driver. This routine may be called
** several times for each operating system reboot
** to dynamically bring the network interface driver
** to a non-running state.
**
** This routine resets the CS8900 chip.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void
**
** Side Effects : None.
**
*/
void csReset( void * arg)
{
struct cs_softc *sc = arg;
/* Mark the interface as down */
sc->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING;
/* Reset the chip */
csResetChip( sc );
}
/*-------------------------------------------------------------------------*/
/*
** Function : csIoctl
**
** Description : This routine is a major entry point and is called
** by the protocol stack to modify characteristics
** of this network interface driver. There are many
** network interface ioctl commands, this
** driver only supports:
** Set Interface Address
** Set Interface Flags.
** Add/Delete Multicast addresses
**
** Parameters : struct ifnet
** command
** pData
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : 0, if successful, else a standard error code
** is returned.
**
** Side Effects : None.
**
*/
int csIoctl( struct ifnet *pIf, ulong com, caddr_t pData )
{
int state;
int result;
struct cs_softc *sc = pIf->if_softc;
struct ifaddr *ifa = (struct ifaddr *) pData;
struct ifreq *ifr = (struct ifreq *)pData;
state = splimp();
result = 0; /* only set if something goes wrong */
switch( com )
{
case SIOCSIFADDR:
/* only support INET at this stage */
if ( ifa->ifa_addr->sa_family == AF_INET )
{
csInit(sc);
/* Set interface address (IP address) */
arp_ifinit(&sc->sc_ethercom.ec_if, ifa);
}
else
{
result = EPERM;
}
break;
case SIOCSIFFLAGS:
/* Set interface flags */
KERN_DEBUG(csdebug, CSIOCTL_DBG_INFO,
("csIoctl: Set interface flags\n"));
if ( pIf->if_flags & IFF_UP )
{
/* Mark the interface as up and running */
sc->sc_ethercom.ec_if.if_flags |= (IFF_UP | IFF_RUNNING);
/* Initialize the interface */
csInit( pIf->if_softc );
}
else /* The interface is down */
{
/* Mark the interface as down */
sc->sc_ethercom.ec_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
/* Reset the chip */
csResetChip( sc );
}
break;
case SIOCADDMULTI:
result = ether_addmulti(ifr, &sc->sc_ethercom);
if (result == ENETRESET)
{
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
csInit(sc);
result = 0;
}
break;
case SIOCDELMULTI:
result = ether_delmulti(ifr, &sc->sc_ethercom);
if (result == ENETRESET)
{
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
csInit(sc);
result = 0;
}
break;
default:
result = EINVAL; /* Invalid argument */
break;
}
splx( state );
return result;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csIntr
**
** Description : This routine is the interrupt service routine.
** This routine is called by assembly language wrapper
** code whenever the CS8900 chip generates an
** interrupt. The wrapper code issues an EOI command
** to the 8259 PIC.
**
** This routine processes the events on the Interrupt
** Status Queue. The events are read one at a time
** from the ISQ and the appropriate event handlers are
** called. The ISQ is read until it is empty. If
** the chip's interrupt request line is active, then
** reading a zero from the ISQ will deactivate the
** interrupt request line.
**
** Parameters : arg is a pointer to the softc struct
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : 0 if the interupt was not handled (chaining)
** 1 if the interupt was handled.
** -1 is also valid, but we don't return it,
** because I don't know what it means.
**
** Side Effects : None.
**
*/
int csIntr( void *arg )
{
struct cs_softc *sc = arg;
ushort Event;
/* Ignore any interrupts that happen while the chip is being reset */
if ( sc->resetting )
{
printf("%s : interupt handler: already resetting\n",
(sc->sc_dev).dv_xname);
/* this was probably still caused by the chip but don't worry */
return 1;
}
/* Read an event from the Interrupt Status Queue */
Event = bus_space_read_2( sc->sc_memt, sc->sc_memh, PKTPG_ISQ );
/* Process all the events in the Interrupt Status Queue */
while ( Event != 0 )
{
/* Dispatch to an event handler based on the register number */
switch ( Event & REG_NUM_MASK )
{
case REG_NUM_RX_EVENT:
csReceiveEvent( sc, Event );
break;
case REG_NUM_TX_EVENT:
csTransmitEvent(sc, Event);
break;
case REG_NUM_BUF_EVENT:
csBufferEvent( sc, Event );
break;
case REG_NUM_TX_COL:
case REG_NUM_RX_MISS:
csCounterEvent( sc, Event );
break;
default:
printf("%s : Unknown interrupt event 0x%x\n",
(sc->sc_dev).dv_xname, Event );
break;
}
/* Read another event from the Interrupt Status Queue */
Event = bus_space_read_2( sc->sc_memt, sc->sc_memh, PKTPG_ISQ );
}
/* have handled the interupt */
return 1;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csCounterEvent
**
** Description : Handles the transmit collision and receive miss
** counter overflow events. These event occur
** when the counters reach 0x200. This should
** leave ample time to read the counter before
** overflow of the 11 bit counter.
**
** Parameters : struct cs_softc *sc
** cntEvent : the interrupt status queue register.
**
** Implicit Inputs : none.
**
** Implicit Outputs : none.
**
** Return Value : void.
**
** Side Effects : the ifnet's counters have been updated.
**
*/
void csCounterEvent( struct cs_softc *sc, ushort cntEvent )
{
struct ifnet *pIf;
ushort errorCount;
pIf = &sc->sc_ethercom.ec_if;
switch ( cntEvent & REG_NUM_MASK )
{
case REG_NUM_TX_COL :
/* the count should be read before
* an overflow occurs.
*/
errorCount = csReadPacketPage(sc, PKTPG_TX_COL);
/* the tramsit event routine always
* checks the number of collisions for any packet
* so we don't increment any counters here, as they
* should already have been considered.
*/
break;
case REG_NUM_RX_MISS :
/* the count should be read before
* an overflow occurs.
*/
errorCount = csReadPacketPage(sc, PKTPG_RX_MISS);
/* Increment the input error count,
* the first 6bits are the register id.
*/
pIf->if_ierrors += ( (errorCount & 0xffC0) >> 6 );
break;
default :
/* do nothing */
break;
}
return ;
}
/*-------------------------------------------------------------------------*/
/*
** Function : csBufferEvent
**
** Description : The routine is called whenever an event occurs
** regarding the transmit and receive buffers within
** the CS8900 chip. This should be called when
** early detection of received packets is enabled.
**
** Software generated interrupts are checked but
** should never be generated.
**
** Parameters : struct cs_softc *sc
** bufEvent
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csBufferEvent( struct cs_softc *sc, ushort bufEvent )
{
struct ifnet *pIf;
pIf = &sc->sc_ethercom.ec_if;
KERN_DEBUG(csdebug, CSBUFF_DBG_INFO,
("csBufferEvent : event = 0x%x\n",bufEvent));
/* multiple events can be in the buffer event register at
* one time so a standard switch statement will not suffice, here
* every event must be checked.
*/
/* if 128 bits have been rxed by the time we get here, the
* dest event will be cleared and 128 event will be set.
*/
if ( (bufEvent & (BUF_EVENT_RX_DEST | BUF_EVENT_RX_128)) != 0 )
{
csProcessRxEarly(sc);
}
if ( bufEvent & BUF_EVENT_RX_DMA )
{
/* process the receive data */
csProcessRxDMA(sc);
}
if (bufEvent & BUF_EVENT_TX_UNDR) {
#if 0 /* this is an expected condition (occasionally) */
printf("%s: transmit underrun (%d -> %d)\n", sc->sc_dev.dv_xname,
sc->sc_xe_ent, cs_xmit_early_table[sc->sc_xe_ent].worse);
#endif
sc->sc_xe_ent = cs_xmit_early_table[sc->sc_xe_ent].worse;
sc->sc_xe_togo = cs_xmit_early_table[sc->sc_xe_ent].better_count;
/* had an underrun, transmit is finished */
sc->txInProgress = FALSE;
}
if ( bufEvent & BUF_EVENT_SW_INT )
{
printf( "%s : Software initiated interrupt\n",
(sc->sc_dev).dv_xname );
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csTransmitEvent
**
** Description : This routine is called whenever the transmission
** of a packet has completed successfully or
** unsuccessfully. If the transmission was not
** successful, then the output error count is
** incremented. If collisions occured while
** sending the packet, then the number of collisions
** is added to the collision counter. If there are
** more packets on the transmit queue, then the next
** packet is started by calling
** csStartOutput().
**
** Parameters : struct cs_softc *sc
** txEvent
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csTransmitEvent( struct cs_softc *sc, ushort txEvent )
{
struct ifnet *pIf = &sc->sc_ethercom.ec_if;
KERN_DEBUG(csdebug, CSTXEVENT_DBG_INFO,
("csTransmitEvent : event = 0x%x\n", txEvent));
/* If there were any errors transmitting this frame */
if ( txEvent & (TX_EVENT_LOSS_CRS | TX_EVENT_SQE_ERR | TX_EVENT_OUT_WIN |
TX_EVENT_JABBER | TX_EVENT_16_COLL) )
{
/* Increment the output error count */
pIf->if_oerrors++;
/* If debugging is enabled then log error messages */
if ( pIf->if_flags & IFF_DEBUG )
{
if ( txEvent & TX_EVENT_LOSS_CRS )
{
printf( "%s : Loss of carrier\n", (sc->sc_dev).dv_xname );
}
if ( txEvent & TX_EVENT_SQE_ERR )
{
printf( "%s : SQE error\n", (sc->sc_dev).dv_xname );
}
if ( txEvent & TX_EVENT_OUT_WIN )
{
printf( "%s : Out-of-window collision\n",
(sc->sc_dev).dv_xname );
}
if ( txEvent & TX_EVENT_JABBER )
{
printf("%s : Jabber\n", (sc->sc_dev).dv_xname );
}
if ( txEvent & TX_EVENT_16_COLL )
{
printf( "%s : 16 collisions\n", (sc->sc_dev).dv_xname );
}
}
}
else
{
ledNetActive();
}
/* Add the number of collisions for this frame */
if ( txEvent & TX_EVENT_16_COLL )
{
pIf->if_collisions += 16;
}
else
{
pIf->if_collisions += ((txEvent & TX_EVENT_COLL_MASK) >> 11);
}
pIf->if_opackets++;
/* Transmission is no longer in progress */
sc->txInProgress = FALSE;
/* If there is more to transmit */
if ( pIf->if_snd.ifq_head != NULL )
{
/* Start the next transmission */
csStartOutput(pIf);
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csReceiveEvent
**
** Description : This routine is called whenever a packet is
** received at the chip. If the packet is received
** with errors, then the input error count is
** incremented. If the packet is received OK, then
** the data is copied to an internal receive
** buffer and the csProcessReceive() routine is
** called to process the received packet.
**
** Parameters : struct cs_softc *sc
** rxEvent
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csReceiveEvent( struct cs_softc *sc, ushort rxEvent )
{
struct ifnet *pIf = &sc->sc_ethercom.ec_if;
KERN_DEBUG(csdebug, CSRXNORM_DBG_INFO,
("csProcessReceive : packet received event = 0x%x\n",rxEvent));
/* If the frame was not received OK */
if ( !(rxEvent & RX_EVENT_RX_OK) )
{
/* Increment the input error count */
pIf->if_ierrors++;
/* If debugging is enabled then log error messages */
if ( pIf->if_flags & IFF_DEBUG )
{
/* If an error bit is set */
if ( rxEvent != REG_NUM_RX_EVENT )
{
if ( rxEvent & RX_EVENT_RUNT )
{
printf( "%s : Runt\n", (sc->sc_dev).dv_xname );
}
if ( rxEvent & RX_EVENT_X_DATA )
{
printf( "%s : Extra data\n", (sc->sc_dev).dv_xname );
}
if ( rxEvent & RX_EVENT_CRC_ERR )
{
if ( rxEvent & RX_EVENT_DRIBBLE )
{
printf( "%s : Alignment error\n",
(sc->sc_dev).dv_xname );
}
else
{
printf( "%s : CRC Error\n", (sc->sc_dev).dv_xname );
}
}
else
{
if ( rxEvent & RX_EVENT_DRIBBLE )
{
printf( "%s : Dribble bits\n", (sc->sc_dev).dv_xname );
}
}
/* Must read the length of all received frames */
csReadPacketPage( sc, PKTPG_RX_LENGTH );
/* Skip the received frame */
csWritePacketPage( sc, PKTPG_RX_CFG,
csReadPacketPage(sc,PKTPG_RX_CFG) |
RX_CFG_SKIP );
}
else
{
printf( "%s : Implied skip\n", (sc->sc_dev).dv_xname );
}
}
}
else
{
/* process the received frame and pass it up to the upper layers. */
csProcessReceive(sc);
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csEtherInput
**
** Description : This routine hands a received packet to the higher-
** level networking code, taking care to pass it
** to BPF as appropriate and to discard it when
** necessary.
**
** Parameters : struct cs_softc *sc network interface softc
** struct mbuf *m incoming packet
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : Mbuf chain passed to higher-level network code,
** or deallocated if it shouldn't be passed up.
**
*/
void
csEtherInput(sc, m)
struct cs_softc *sc;
struct mbuf *m;
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct ether_header *eh;
ifp->if_ipackets++;
/* the first thing in the mbuf is the ethernet header.
* we need to pass this header to the upper layers as a structure,
* so cast the start of the mbuf, and adjust the mbuf to point to
* the end of the ethernet header, ie the ethernet packet data.
*/
eh = mtod(m, struct ether_header *);
#if NBPFILTER > 0
/*
* Check if there's a BPF listener on this interface.
* If so, hand off the raw packet to BPF.
*
*/
if (ifp->if_bpf) {
bpf_mtap(ifp->if_bpf, m);
/*
* Note that the interface cannot be in promiscuous mode if
* there are no BPF listeners. And if we are in promiscuous
* mode, we have to check if this packet is really ours.
*/
if ((ifp->if_flags & IFF_PROMISC) &&
(eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
bcmp(eh->ether_dhost, sc->sc_enaddr,
sizeof(eh->ether_dhost)) != 0) {
m_freem(m);
return;
}
}
#endif
/* Pass the packet up, with the ether header sort-of removed.
* ie. adjust the data pointer to point to the data.
*/
m_adj(m, sizeof(struct ether_header));
ether_input(ifp, eh, m);
}
/*-------------------------------------------------------------------------*/
/*
** Function : csProcessReceive
**
** Description : This routine processes a received packet. The
** received packet is read from the chip directly
** into a clustered mbuf. These being 1 page
** on i386 netBSD, and thus guarenteed to fit
** an ethernet frame.
**
** Parameters : struct cs_softc *sc
** pRxBuf
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csProcessReceive( struct cs_softc *sc )
{
struct ifnet *pIf;
struct mbuf *m;
int totlen;
int len;
volatile ushort *pBuff, *pBuffLimit;
int pad;
unsigned int frameOffset;
ledNetActive();
pIf = &sc->sc_ethercom.ec_if;
/* Initialize the frame offset*/
frameOffset = PKTPG_RX_LENGTH;
/* Get the length of the received frame */
totlen = bus_space_read_2( sc->sc_memt, sc->sc_memh,
frameOffset );
frameOffset += 2;
KERN_DEBUG(csdebug, CSRXNORM_DBG_INFO,
("csProcessReceive : length = 0x%x\n",totlen));
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
{
printf("%s : csProcessReceive : Couldn't allocate mbuf\n",
(sc->sc_dev).dv_xname);
pIf->if_ierrors++;
/* couldn't allocate an mbuf so things are not good,
* may as well drop the packet I think.
*
* have already read the length so we should be
* right to skip the packet.
*/
csWritePacketPage( sc, PKTPG_RX_CFG,
csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP );
return ;
}
m->m_pkthdr.rcvif = pIf;
m->m_pkthdr.len = totlen;
/* save processing by always using a mbuf cluster,
* guarenteed to fit packet, on i386 netBSD anyway.
*/
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
{
len = MCLBYTES;
}
else
{
/* couldn't allocate an mbuf cluster */
printf("%s : csProcessReceive : couldn't allocate a cluster\n",
(sc->sc_dev).dv_xname);
m_freem(m);
/* skip the received frame */
csWritePacketPage( sc, PKTPG_RX_CFG,
csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP );
return ;
}
/* align ip header on word boundary for ipintr */
pad = ALIGN(sizeof(struct ether_header))
- sizeof(struct ether_header);
m->m_data += pad;
m->m_len = len = min(totlen, len);
pBuff = mtod(m, ushort *);
pBuffLimit = pBuff + (len + 1 )/2; /* don't want to go over */
/* now read the data from the chip */
while ( pBuff < pBuffLimit )
{
*pBuff++ = bus_space_read_2( sc->sc_memt, sc->sc_memh,
frameOffset );
frameOffset += 2;
}
csEtherInput(sc, m);
}
/*-------------------------------------------------------------------------*/
/*
** Function : csProcessRxDMA
**
** Description : This routine is called by the ISR to accept a
** frame from the adapter. (Called on a Rx ISQ
** event). It copies frames which have been
** DMA transfered to memory.
**
** Parameters : softc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : None.
**
** Side Effects :
*/
void csProcessRxDMA(struct cs_softc *sc)
{
struct ifnet *pIf;
ushort num_dma_frames;
ushort pkt_length;
ushort status;
uint to_copy;
char *dma_mem_ptr;
struct mbuf *m;
u_char *pBuff;
int pad;
/* initialise the pointers */
pIf = &sc->sc_ethercom.ec_if;
/* Read the number of frames DMAed. */
num_dma_frames = csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT);
num_dma_frames &= (ushort) (0x0fff);
/* Loop till number of DMA frames ready to read is zero.
* After reading the frame out of memory we must check
* if any have been received while we were processing
*/
while ( num_dma_frames != 0 )
{
dma_mem_ptr = sc->dma_offset;
/* process all of the dma frames in memory
*
* This loop relies on the dma_mem_ptr variable being set
* to the next frames start address.
*/
for(; num_dma_frames > 0; num_dma_frames--)
{
/* Get the length and status of the packet.
* Only the status is guarenteed to be at
* dma_mem_ptr, ie need to check for wraparound
* before reading the length
*/
status = *((unsigned short *)dma_mem_ptr)++;
if (dma_mem_ptr > (sc->dmaBase + sc->dmaMemSize))
{
dma_mem_ptr = sc->dmaBase;
}
pkt_length = *((unsigned short *)dma_mem_ptr)++;
/* Do some sanity checks on the length and status. */
if( (pkt_length > ETHER_MTU) || ((status & DMA_STATUS_BITS) !=
DMA_STATUS_OK) )
{
/* the SCO driver kills the adapter in this situation */
/* should increment the error count
* and reset the dma operation.
*/
printf("%s : csProcessRxDMA : DMA buffer out of sync about to reset\n",
(sc->sc_dev).dv_xname);
KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO,
("csProcessRxDMA : error length = %d, \
status = 0x%x\n", pkt_length, status));
KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO,
("csProcessRxDMA : the offset is 0x%x\n", \
(int) sc->dma_offset));
pIf->if_ierrors++;
/* skip the rest of the DMA buffer */
isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, sc->dmaMemSize,
sc->sc_drq);
/* now reset the chip and reinitialise */
csInit(sc);
return ;
}
/* Check the status of the received packet. */
if ( status & RX_EVENT_RX_OK )
{
/* get a new mbuf */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
{
printf("%s : csProcessRxDMA : Couldn't allocate mbuf\n",\
(sc->sc_dev).dv_xname);
pIf->if_ierrors++;
/* couldn't allocate an mbuf so things are not good,
* may as well drop all the packets I think.
*/
csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT );
/* now reset DMA operation */
isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase,
sc->dmaMemSize, sc->sc_drq);
/* now reset the chip and reinitialise */
csInit(sc);
return ;
}
/* save processing by always using a mbuf cluster,
* guarenteed to fit packet
*/
MCLGET(m, M_DONTWAIT);
if ( (m->m_flags & M_EXT) == 0)
{
/* couldn't allocate an mbuf cluster */
printf("%s : csProcessRxDMA : couldn't allocate \
a cluster\n", (sc->sc_dev).dv_xname);
m_freem(m);
/* skip the frame */
csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT );
isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase,
sc->dmaMemSize, sc->sc_drq);
/* now reset the chip and reinitialise */
csInit(sc);
return ;
}
m->m_pkthdr.rcvif = pIf;
m->m_pkthdr.len = pkt_length;
m->m_len = pkt_length;
/* align ip header on word boundary for ipintr */
pad = ALIGN(sizeof(struct ether_header))
- sizeof(struct ether_header);
m->m_data += pad;
/* set up the buffer pointer to point
* to the data area
*/
pBuff = mtod(m, char *);
/*
* Read the frame into free_pktbuf
* The buffer is circular buffer, either
* 16K or 64K in length.
*
* need to check where the end of the buffer
* is and go back to the start.
*/
if ( (dma_mem_ptr + pkt_length )
< sc->dmaBase + sc->dmaMemSize)
{
/* No wrap around. Copy the frame header */
bcopy(dma_mem_ptr, pBuff, pkt_length);
dma_mem_ptr += pkt_length;
}
else
{
KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO,
("csProcessRxDMA : wraparound\n"));
to_copy = (uint) ( (sc->dmaBase + sc->dmaMemSize)
- dma_mem_ptr);
/* Copy the first half of the frame. */
bcopy(dma_mem_ptr, pBuff, to_copy);
pBuff += to_copy;
/*
* Rest of the frame is to be read from the
* first byte of the DMA memory.
*/
/* Get the number of bytes leftout in the frame. */
to_copy = pkt_length - to_copy;
dma_mem_ptr = sc->dmaBase;
/* Copy rest of the frame. */
bcopy(dma_mem_ptr, pBuff, to_copy);
dma_mem_ptr += to_copy;
}
KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO,
("csProcessRxDMA : about to pass packet up protocol stack\n"));
KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO,
("csProcessRxDMA : start addr = 0x%x\n",
(unsigned int) mtod(m, char *)) );
csEtherInput(sc, m);
} /* (status & RX_OK) */
else
{
/* the frame was not received OK */
/* Increment the input error count */
pIf->if_ierrors++;
/* If debugging is enabled then log error messages */
if ( pIf->if_flags & IFF_DEBUG )
{
/* If an error bit is set */
if ( status != REG_NUM_RX_EVENT )
{
if ( status & RX_EVENT_RUNT )
{
printf( "%s : Runt\n", (sc->sc_dev).dv_xname );
}
if ( status & RX_EVENT_X_DATA )
{
printf( "%s : Extra data\n",
(sc->sc_dev).dv_xname );
}
if ( status & RX_EVENT_CRC_ERR )
{
if ( status & RX_EVENT_DRIBBLE )
{
printf( "%s : Alignment error\n", \
(sc->sc_dev).dv_xname);
}
else
{
printf("%s : CRC Error\n", \
(sc->sc_dev).dv_xname );
}
}
else
{
if ( status & RX_EVENT_DRIBBLE )
{
printf("%s : Dribble bits\n", \
(sc->sc_dev).dv_xname );
}
}
}
}
}
/* now update the current frame pointer.
* the dma_mem_ptr should point to the next packet to
* be received, without the alignment considerations.
*
* The cs8900 pads all frames to start at the next 32bit
* aligned addres. hence we need to pad our offset pointer.
*/
dma_mem_ptr += 3;
dma_mem_ptr = (char *) ((long) dma_mem_ptr & 0xfffffffc);
if ( dma_mem_ptr < (sc->dmaBase + sc->dmaMemSize) )
{
sc->dma_offset = dma_mem_ptr;
}
else
{
dma_mem_ptr = sc->dma_offset = sc->dmaBase;
}
} /* for all frames */
/* Read the number of frames DMAed again. */
num_dma_frames = csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT );
num_dma_frames &= (ushort) (0x0fff);
} /* while there are frames left */
}
/*-------------------------------------------------------------------------*/
/*
** Function : csProcessRxEarly
**
** Description : This routine is called after a buffer event
** RxDest or Rx128 has occurred. The frame is read
** from the chip as it arrives. The packet is then
** skipped, so that the next packet can be read.
**
** For a more detailed description of this operation
** refer to section "5.2.9 Receive Frame Byte
** Counter" in the chip spec.
**
** This code presently supports both mapped memory
** mode and programmed I/O mode. Programmed I/O is
** not as fast as mapped memory and the programmed
** I/O code slows the mapped memory implementation
** down. To improve performance remove the programmed
** I/O support.
**
** Parameters : struct cs_softc *sc
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : The frame is read from the chip and passed to the
** upper layer protocols if it is ok.
**
*/
void csProcessRxEarly( struct cs_softc *sc )
{
struct ifnet *pIf;
struct mbuf *m;
ushort frameCount, oldFrameCount;
ushort rxEvent;
ushort *pBuff;
int pad;
unsigned int frameOffset;
pIf = &sc->sc_ethercom.ec_if;
/* Initialize the frame offset */
frameOffset = PKTPG_RX_FRAME;
frameCount = 0;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
{
printf("%s : csProcessRxEarly : Couldn't allocate mbuf\n",
(sc->sc_dev).dv_xname);
pIf->if_ierrors++;
/* couldn't allocate an mbuf so things are not good, may as well drop
* the packet I think.
*
* have already read the length so we should be
* right to skip the packet.
*/
csWritePacketPage( sc, PKTPG_RX_CFG,
csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP );
return ;
}
m->m_pkthdr.rcvif = pIf;
/* save processing by always using a mbuf cluster,
* guarenteed to fit packet
*/
MCLGET(m, M_DONTWAIT);
if ( (m->m_flags & M_EXT) == 0)
{
/* couldn't allocate an mbuf cluster */
printf("%s : csProcessRxEarly : couldn't allocate a cluster\n",
(sc->sc_dev).dv_xname);
m_freem(m);
/* skip the frame */
csWritePacketPage( sc, PKTPG_RX_CFG,
csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP );
return ;
}
/* align ip header on word boundary for ipintr */
pad = ALIGN(sizeof(struct ether_header))
- sizeof(struct ether_header);
m->m_data += pad;
/* set up the buffer pointer to point to the data area */
pBuff = mtod(m, ushort *);
/* now read the frame byte counter until we have
* finished reading the frame
*/
oldFrameCount = 0;
frameCount = bus_space_read_2( sc->sc_memt, sc->sc_memh,
PKTPG_FRAME_BYTE_COUNT );
while ( (frameCount != 0) && ( frameCount < MCLBYTES ) )
{
for (; oldFrameCount < frameCount; oldFrameCount += 2)
{
*pBuff++ = bus_space_read_2( sc->sc_memt, sc->sc_memh,
frameOffset );
frameOffset += 2;
}
/* read the new count from the chip */
frameCount = bus_space_read_2( sc->sc_memt, sc->sc_memh,
PKTPG_FRAME_BYTE_COUNT );
}
/* update the mbuf counts */
m->m_len = oldFrameCount;
m->m_pkthdr.len = oldFrameCount;
/* now check the Rx Event register */
rxEvent = csReadPacketPage(sc,PKTPG_RX_EVENT);
if ( (rxEvent & RX_EVENT_RX_OK ) != 0 )
{
/* do an implied skip, it seems to be more reliable
* than a forced skip.
*/
rxEvent = bus_space_read_2( sc->sc_memt, sc->sc_memh,
PKTPG_RX_STATUS );
rxEvent = bus_space_read_2( sc->sc_memt, sc->sc_memh,
PKTPG_RX_LENGTH );
/* now read the RX_EVENT register to
* perform an implied skip.
*/
rxEvent = csReadPacketPage(sc,PKTPG_RX_EVENT);
csEtherInput(sc, m);
}
else
{
m_freem(m);
pIf->if_ierrors++;
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csStartOutput
**
** Description : This routine is called to start the transmission
** of the packet at the head of the transmit queue.
** This routine is called by the ether_output()
** routine when a new packet is added to the transmit
** queue and this routine is called by
** csTransmitEvent() when a previous packet
** transmission is completed.
**
** This should always be called at splimp.
**
** Parameters : pIf : pointer to devices ifnet
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csStartOutput( struct ifnet *pIf )
{
struct cs_softc *sc;
struct mbuf *pMbuf;
struct mbuf *pMbufChain;
struct ifqueue *pTxQueue;
ushort BusStatus;
ushort Length;
int txLoop = 0;
int dropout = 0;
sc = pIf->if_softc;
pTxQueue = &sc->sc_ethercom.ec_if.if_snd;
/* check that the interface is up and running */
if ((pIf->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
{
return ;
}
/* Don't interrupt a transmission in progress */
if ( sc->txInProgress )
{
return;
}
/* this loop will only run through once if transmission is successful */
/* While there are packets to transmit and a transmit is not in progress */
while ( (pTxQueue->ifq_head != NULL) && !(sc->txInProgress)
&& !(dropout) )
{
IF_DEQUEUE( pTxQueue, pMbufChain );
#if NBPFILTER > 0
/*
* If BPF is listening on this interface, let it see the packet
* before we commit it to the wire.
*/
if (pIf->if_bpf)
bpf_mtap(pIf->if_bpf, pMbufChain);
#endif
/* Find the total length of the data to transmit */
Length = 0;
for ( pMbuf=pMbufChain; pMbuf!=NULL; pMbuf=pMbuf->m_next )
Length += pMbuf->m_len;
do {
/* Request that the transmit be started after all data has
* been copied
*
* In IO mode must write to the IO port not the packet page
* address
*
* If this is changed to start transmission after a small
* amount of data has been copied you tend to get packet
* missed errors i think because the ISA bus is too slow. Or
* possibly the copy routine is not streamlined enough.
*/
csWritePacketPage( sc, PKTPG_TX_CMD,
cs_xmit_early_table[sc->sc_xe_ent].txcmd );
csWritePacketPage( sc, PKTPG_TX_LENGTH, Length );
/* Adjust early-transmit machinery.
*/
if (--sc->sc_xe_togo == 0) {
sc->sc_xe_ent = cs_xmit_early_table[sc->sc_xe_ent].better;
sc->sc_xe_togo =
cs_xmit_early_table[sc->sc_xe_ent].better_count;
}
/* Read the BusStatus register which indicates success of
* the request
*/
BusStatus = csReadPacketPage( sc, PKTPG_BUS_ST );
/* If there was an error in the transmit bid
* free the mbuf and go on. This is presuming that
* mbuf is corrupt.
*/
if ( BusStatus & BUS_ST_TX_BID_ERR )
{
printf( "%s : Transmit bid error (too big)",
(sc->sc_dev).dv_xname );
/* Discard the bad mbuf chain */
m_freem( pMbufChain );
sc->sc_ethercom.ec_if.if_oerrors++;
/* Loop up to transmit the next chain */
txLoop = 0;
}
else
{
if ( BusStatus & BUS_ST_RDY4TXNOW )
{
KERN_DEBUG(csdebug, CSSTARTTX_DBG_INFO,
("csStartOutput : about to send frame\n"));
/* The chip is ready for transmission now */
/* Copy the frame to the chip to start
* transmission
*/
csCopyTxFrame( sc, pMbufChain );
/* Free the mbuf chain */
m_freem( pMbufChain );
/* Transmission is now in progress */
sc->txInProgress = TRUE;
txLoop = 0;
}
else
{
/* if we get here we want to try again with
* the same mbuf, until the chip lets us
* transmit.
*/
txLoop++;
if ( txLoop > CS_OUTPUT_LOOP_MAX )
{
/* Free the mbuf chain */
m_freem( pMbufChain );
/* Transmission is not in progress */
sc->txInProgress = FALSE;
/* Increment the output error count */
pIf->if_oerrors++;
/* exit the routine and drop the packet. */
txLoop = 0;
dropout = 1;
}
KERN_DEBUG(csdebug, CSSTARTTX_DBG_INFO,
( "%s : Not ready for transmission now",
(sc->sc_dev).dv_xname ));
}
}
} while (txLoop);
}
}
/*-------------------------------------------------------------------------*/
/*
** Function : csCopyTxFrame
**
** Description : This routine copies the packet from a chain of
** mbufs to the chip. When all the data has been
** copied, then the chip automatically begins
** transmitting the data.
**
** The reason why this "simple" copy routine is so
** long and complicated is because all reads and writes
** to the chip must be done as 16-bit words. If an mbuf
** has an odd number of bytes, then the last byte must
** be saved and combined with the first byte of the
** next mbuf.
**
** Parameters : struct cs_softc *sc : device softc structure
** pMbufChain : mbuf to transmit.
**
** Implicit Inputs : None.
**
** Implicit Outputs : None.
**
** Return Value : void.
**
** Side Effects : None.
**
*/
void csCopyTxFrame( struct cs_softc *sc, struct mbuf *m0 )
{
bus_space_tag_t memt = sc->sc_memt;
bus_space_handle_t memh = sc->sc_memh;
struct mbuf *m;
int len, leftover, frameoff;
u_int16_t dbuf;
u_int8_t *p, *lim;
/* Initialize frame pointer and data port address */
frameoff = PKTPG_TX_FRAME;
/* start out with no leftover data */
leftover = 0;
dbuf = 0;
/* Process the chain of mbufs */
for (m = m0; m != NULL; m = m->m_next) {
/*
* Process all of the data in a single mbuf.
*/
p = mtod(m, u_int8_t *);
len = m->m_len;
#ifdef DIAGNOSTIC
lim = p + len;
#endif
while (len > 0) {
if (leftover) {
/*
* Data left over (from mbuf or realignment).
* Buffer the next byte, and write it and
* the leftover data out.
*/
dbuf |= *p++ << 8;
len--;
bus_space_write_2(memt, memh, frameoff,
dbuf);
frameoff += 2;
leftover = 0;
} else if ((long)p & 1) {
/*
* Misaligned data. Buffer the next byte.
*/
dbuf = *p++;
len--;
leftover = 1;
} else {
/*
* Aligned data. This is the case we like.
*
* Write-region out as much as we can, then
* buffer the remaining byte (if any).
*/
leftover = len & 1;
len &= ~1;
bus_space_write_region_2(memt, memh, frameoff,
(u_int16_t *)p, len >> 1);
p += len;
frameoff += len;
if (leftover)
dbuf = *p++;
len = 0;
}
}
if (len < 0)
panic("csCopyTxFrame: negative len");
#ifdef DIAGNOSTIC
if (p != lim)
panic("csCopyTxFrame: p != lim");
#endif
}
if (leftover)
bus_space_write_2(memt, memh, frameoff, dbuf);
}