1
0
Files
2022-09-29 17:59:04 +03:00

1042 lines
24 KiB
C

#if IP20 || IP22 || IP26 || IP28
#ident "lib/libsk/net/if_ec2.c: $Revision: 1.90 $"
/*
* if_ec2.c - driver for IP20/IP22/IP26/IP28 onboard ethernet controller
*/
#include <arcs/errno.h>
#include <arcs/hinv.h> /* NBPP */
#include <sys/cpu.h>
#include <sys/param.h>
#include <sys/sbd.h>
#include <sys/immu.h>
#include <net/in.h>
#include <net/socket.h>
#include <net/arp.h>
#include <net/ei.h>
#include <net/mbuf.h>
#include <net/seeq.h>
#include <saio.h>
#include <saioctl.h>
#include <libsk.h>
#include <libsc.h>
#if IP22 || IP26 || IP28
#define reset_enet_carrier()
#endif /* IP22 || IP26 || IP28 */
#if IP20
#define ENET_READ(reg) (reg)
#endif /* IP20 */
#if IP22 || IP26 || IP28
#define ENET_READ(reg) (hio->piocfg, ((__psunsigned_t)reg) & ~0xf)
#endif /* IP22 || IP26 || IP28 */
#if _MIPS_SIM == _ABI64
#define tclean_dcache(x,y) if ((__psunsigned_t)(x) >= K0BASE_NONCOH) clear_cache(x,y)
#else
#define tclean_dcache(x,y) if ((__psunsigned_t)(x) < K1BASE) clear_cache(x,y)
#endif
#define cei(x) ((struct ether_info *)(x->DevPtr))
#define SGI_ETHER_ADDR 0x08 /* 1st byte of SGI enet addresses */
#ifdef VERBOSE
extern int Verbose;
#define VPRINTF if (Verbose) printf
#else
#define VPRINTF
#endif
#ifdef DEBUG
extern int Debug;
#define DPRINTF2(arg) if (Debug>1) printf arg
#define DPRINTF(arg) if (Debug) printf arg
#else
#define DPRINTF2(arg)
#define DPRINTF(arg)
#endif
/*
* Each interface is represented by a network interface structure, eh_info,
* which the routing code uses to locate the interface.
*/
struct eh_info {
struct arpcom ei_ac; /* common ethernet structures */
rd_desc *ei_ract; /* next active receive buffer */
rd_desc *ei_rtail; /* end of available receive buffers */
int ei_rnum; /* number of unused buffers */
xd_desc *ei_tact; /* transmit chain */
xd_desc *ei_ttail; /* end of transmit chain */
int ei_tnum;
} eh_info, *ei;
#define ei_if ei_ac.ac_if /* network-visible interface */
#define ei_addr ei_ac.ac_enaddr /* current ethernet address */
#define EDLC_NEW_FEATURES (SEQ_CTL_XCOLL | SEQ_CTL_ACOLL | SEQ_CTL_SQE | \
SEQ_CTL_SHORT | SEQ_CTL_NOCARR)
#define NUM_TMD (NSO_TABLE * MAXMBUFS)
#define NUM_RMD (NSO_TABLE * MAXMBUFS)
/* the descriptor chains must not be cached */
char _align0[128]; /* cache alignment */
xd_desc eh_xd[NUM_TMD+2]; /* transmit/receive descriptor arrays, +1 */
rd_desc eh_rd[NUM_RMD+2]; /* for alignment, +1 for sentinal */
char _align1[128]; /* cache alignment */
EHIO hio = (EHIO)PHYS_TO_K1(0x1fb80000); /* hpc registers map into mem here */
extern int cachewrback;
static int carr_detect;
static int new_edlc;
#if IP22 || IP26 || IP28
static u_int xid;
#endif
int _eclose(void);
static int eioctl(IOBLOCK *);
static int eoutput(struct ifnet *, struct mbuf *, struct sockaddr *);
static void epoll(IOBLOCK *);
static int eput(struct mbuf *);
static void ereset(void);
static int einit(void);
static void seeq_init(u_int);
static void eput_finish(void);
static void eread(void);
static void e_frwrd_pkt(struct mbuf *);
/*
* _einit -- cleanup globals
*/
int
_einit(void)
{
ei = &eh_info;
bzero(ei, sizeof(eh_info));
ereset();
/* set up ethernet PIO and DMA timing */
#if IP22 || IP26 || IP28
#define DMA_CFG_25MHZ 0x1e015
#define DMA_CFG_33MHZ 0x1e026
#define PIO_CFG_25MHZ 0x51
#define PIO_CFG_33MHZ 0x161
if (ip22_gio_is_33MHZ()) {
*(volatile u_int *)PHYS_TO_K1(HPC3_ETHER_PIO_CFG_ADDR) =
PIO_CFG_33MHZ;
*(volatile u_int *)PHYS_TO_K1(HPC3_ETHER_DMA_CFG_ADDR) =
DMA_CFG_33MHZ;
DPRINTF(("GIO clock rate is 33 MHz\n"));
} else {
*(volatile u_int *)PHYS_TO_K1(HPC3_ETHER_PIO_CFG_ADDR) =
PIO_CFG_25MHZ;
*(volatile u_int *)PHYS_TO_K1(HPC3_ETHER_DMA_CFG_ADDR) =
DMA_CFG_25MHZ;
DPRINTF(("GIO clock rate is 25 MHz\n"));
}
#endif /* IP22 || IP26 || IP28 */
new_edlc = (hio->seq_coll_xmit[0] & 0xff) == 0;
if (new_edlc) { /* new EDLC */
hio->seq_csx = SEQ_XC_REGBANK2;
hio->seq_ctl = EDLC_NEW_FEATURES;
hio->seq_csx = 0x00;
DPRINTF(("Using the new EDLC\n"));
}
#if IP20
carr_detect = 1;
#endif /* IP20 */
#if IP22 || IP26 || IP28
carr_detect = 2;
#endif /* IP22 || IP26 || IP28 */
return 0;
}
/*
* eprobe -- returns -1 on invalid unit number
*/
int
_eprobe(int unit)
{
return (unit == 0 ? 1 : -1);
}
static COMPONENT ectmpl = {
ControllerClass, /* Class */
NetworkController, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
4, /* IdentifierLength */
"ec0" /* Identifier */
};
static COMPONENT ecptmpl = {
PeripheralClass, /* Class */
NetworkPeripheral, /* Type */
Input|Output, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
0, /* IdentiferLength */
NULL /* Identifier */
};
/*
* ec_install -- manages ARCS configuration for ec0
*/
void
ec_install(COMPONENT *p)
{
p = AddChild(p,&ectmpl,0);
if (p == (COMPONENT *)NULL) cpu_acpanic("ec0");
p = AddChild(p,&ecptmpl,(void *)0);
if (p == (COMPONENT *)NULL) cpu_acpanic("ec0");
RegisterDriverStrategy(p,_if_strat);
}
/*
* _eopen -- setup seeq and initialize network driver data structs
*/
int
_eopen(IOBLOCK *iob)
{
if (ei->ei_if.if_flags & (IFF_UP|IFF_RUNNING|IFF_BROADCAST))
panic("ec0: trying to open the driver twice\n");
cei(iob)->ei_registry = -1; /* mark not bound yet */
cpu_get_eaddr(ei->ei_addr);
if (ei->ei_addr[0] != SGI_ETHER_ADDR) {
printf("ec0: bad ethernet address %s\n",
(char *)ether_sprintf(ei->ei_addr));
return(iob->ErrorNumber = EINVAL);
}
if (einit() < 0)
return(iob->ErrorNumber = EIO);
ei->ei_if.if_unit = 0;
ei->ei_if.if_mtu = ETHERMTU;
ei->ei_if.if_output = eoutput;
ei->ei_if.if_ioctl = eioctl;
ei->ei_if.if_poll = epoll;
cei(iob)->ei_acp = &ei->ei_ac; /* iob ptr to arpcom */
ei->ei_if.if_flags = IFF_UP|IFF_RUNNING|IFF_BROADCAST;
return(ESUCCESS);
}
static void
ereset(void)
{
hio->rcvstat = 0; /* shut off dma before doing the reset */
hio->trstat = 0;
hio->ctl = HPC_MODNORM | HPC_ERST;
delay(15);
hio->ctl = HPC_MODNORM;
}
/*
* eioctl -- device specific ioctls
*/
static int
eioctl(IOBLOCK *iob)
{
switch ((__psint_t)(iob->IoctlCmd)) {
case NIOCRESET:
(void) ereset();
break;
default:
printf("ec0: invalid ioctl (cmd %d)\n",
iob->IoctlCmd);
return iob->ErrorNumber = EINVAL;
}
return(ESUCCESS);
}
static int
einit(void)
{
rd_desc *rd_prev, *rd;
xd_desc *xd;
struct mbuf *m;
int i;
/*
* allocate new mbufs for the seeq to receive into
*
*/
#if IP20
rd = ei->ei_ract = (rd_desc *)PHYS_TO_K1(KDM_TO_PHYS(((u_char *)eh_rd +
(NBPP - poff(eh_rd)) % sizeof(rd_desc))));
#endif /* IP20 */
#if IP22 || IP26 || IP28
/* quadword alignment */
rd = ei->ei_ract =
(rd_desc *)PHYS_TO_K1(KDM_TO_PHYS(((__psunsigned_t)eh_rd + 0xf) & ~0xf));
#endif /* IP22 || IP26 || IP28 */
bzero((void *)rd, sizeof(rd_desc) * (NUM_RMD + 1));
rd_prev = rd + NUM_RMD; /* extra desc is sentinal */
for (i = 0; i < NUM_RMD; i++) {
if ((m = _m_get(M_DONTWAIT, MT_DATA)) == 0) {
rd = ei->ei_ract;
while (rd && rd->r_mbuf) {
_m_freem(rd->r_mbuf);
rd = (rd_desc *)PHYS_TO_K1(rd->r_nrdesc);
}
printf("ec0: Not enough mbufs to initialize receiver.\n");
return -1;
}
/* initialize new receive descriptor
*/
rd->r_rbcnt = MAX_RPKT;
rd->r_rown = 1;
rd->r_rbufptr = KDM_TO_MCPHYS( mtod(m, char *) );
#ifdef DEBUG
if (Debug > 1) {
u_int *dp = ((u_int *)PHYS_TO_K1(rd->r_rbufptr));
*dp++ = 0;
*dp++ = 0;
*dp++ = 0;
*dp = 0;
}
#endif
m->m_off = 2;
rd->r_mbuf = m;
rd->r_eor = 0;
#if IP22 || IP26 || IP28
rd->r_eorp = 1;
rd->r_xie = 0;
#endif /* IP22 || IP26 || IP28 */
rd_prev->r_nrdesc = KDM_TO_MCPHYS(rd);
rd_prev = rd++;
tclean_dcache(m, sizeof(struct mbuf));
}
/* XXX DO WE REALLY NEED A SETINEL ? XXX */
rd->r_mbuf = 0;
rd->r_rbufptr = 0xEBAD;
rd->r_rown = 1;
rd_prev->r_eor = 1;
rd_prev->r_nrdesc = KDM_TO_MCPHYS(rd);
ei->ei_rtail = rd_prev;
ei->ei_rnum = NUM_RMD;
/*
* Initialize transmit descriptors.
*/
/* align xmit desc */
#if IP20
xd = ei->ei_tact = (xd_desc *)PHYS_TO_K1(KDM_TO_PHYS(((u_char *)eh_xd +
(NBPP - poff(eh_xd)) % sizeof(xd_desc))));
#endif /* IP20 */
#if IP22 || IP26 || IP28
/* quadword alignment */
xd = ei->ei_tact =
(xd_desc *)PHYS_TO_K1(KDM_TO_PHYS(((__psunsigned_t)eh_xd + 0xf) & ~0xf));
#endif /* IP22 || IP26 || IP28 */
bzero((void *)xd, sizeof(xd_desc) * (NUM_TMD + 1));
ei->ei_ttail = xd + NUM_TMD;
ei->ei_ttail->x_nxdesc = KDM_TO_MCPHYS(xd);
for (i = 0; i < NUM_TMD; i++) {
xd->x_nxdesc = KDM_TO_MCPHYS(xd + 1);
xd++;
}
ei->ei_tnum = 0;
/*
* setup seeq address and mode
*/
seeq_init( SEQ_RC_RSB );
hio->nrbdp = hio->crbdp = KDM_TO_MCPHYS(ei->ei_ract);
hio->nxbdp = hio->cpfxbdp = hio->ppfxbdp = KDM_TO_MCPHYS(ei->ei_tact);
#if IP20
hio->rcvstat = HPC_STRCVDMA;
#endif /* IP20 */
#if IP22 || IP26 || IP28
#ifdef _MIPSEB
hio->rcvstat = HPC_STRCVDMA;
#else
hio->rcvstat = HPC_STRCVDMA | HPC_RCV_ENDIAN_LITTLE;
#endif /* _MIPSEB */
DPRINTF2(("Starting receive DMA in einit()\n"));
#endif /* IP22 || IP26 || IP28 */
return 0;
}
/*
* initialize seeq chip
*/
static void
seeq_init(u_int rmode)
{
int i;
ereset();
for (i = 0; i < 6; i++)
hio->seq_eaddr[i] = ei->ei_addr[i];
hio->seq_csx = SEQ_XC_INTGOOD | SEQ_XC_INT16TRY | SEQ_XC_INTCOLL |
SEQ_XC_INTUFLOW | SEQ_XC_REGBANK2;
hio->seq_csr = rmode | SEQ_RC_INTGOOD | SEQ_RC_INTEND |
SEQ_RC_INTSHORT | SEQ_RC_INTDRBL | SEQ_RC_INTCRC;
#if IP20
hio->intdelay = 0;
#endif /* IP20 */
}
/*
* eoutput -- raw access to transmit a packet
*/
/*ARGSUSED*/
static int
eoutput(struct ifnet *ifp, struct mbuf *m0, struct sockaddr *dst)
{
ETHADDR edst;
struct in_addr idst;
struct ether_header *eh;
int type;
#ifdef notdef
DPRINTF(("sending to %d 0x%x %d, cc=%d\n", dst->sa_family, ((struct sockaddr_in *)dst)->sin_addr, nuxi_s((((struct sockaddr_in *)dst)->sin_port), m0->m_len)));
#endif
switch (dst->sa_family) {
case AF_INET:
idst = ((struct sockaddr_in *)dst)->sin_addr;
_arpresolve(&ei->ei_ac, &idst, (u_char *)&edst);
type = ETHERTYPE_IP;
break;
case AF_UNSPEC:
eh = (struct ether_header *)dst->sa_data;
bcopy(eh->ether_dhost, &edst, sizeof(edst));
type = eh->ether_type;
break;
default:
/* EAFNOSUPPORT */
_m_freem(m0);
printf("ec0: no support for address family %d\n",
(int)dst->sa_family);
return(-1);
}
/*
* Add local net header. If no space in mbuf, drop it (shouldn't
* happen!)
*/
if (m0->m_off < sizeof(struct ether_header)
|| m0->m_off + m0->m_len > MLEN) {
DPRINTF(("ec0: transmit -- no space for hdr\n"));
_m_freem(m0);
return(-1);
}
m0->m_off -= sizeof(struct ether_header);
m0->m_len += sizeof(struct ether_header);
eh = mtod(m0, struct ether_header *);
eh->ether_type = htons((u_short)type);
bcopy(&edst, eh->ether_dhost, sizeof(edst));
bcopy(ei->ei_addr, eh->ether_shost, sizeof(ETHADDR));
#ifdef _OLDHPC
if (needs_dma_swap)
swapl(m0->m_dat, (((m0->m_off + m0->m_len) + 3) & ~3) >> 2);
#endif /* _OLDHPC */
/*
* Queue message on interface
*/
return( eput(m0) );
}
static int
eput(struct mbuf *m)
{
xd_desc *xd_build;
caddr_t cp;
/*
* must have a descriptor to use
*/
if (ei->ei_tnum >= NUM_TMD) {
DPRINTF(("ec0: dropped transmit packet\n"));
_m_freem(m);
return -1;
}
xd_build = (xd_desc *)PHYS_TO_K1(ei->ei_ttail->x_nxdesc);
ei->ei_tnum++;
cp = mtod(m, caddr_t);
xd_build->x_xbufptr = KDM_TO_MCPHYS(cp);
xd_build->x_xbcnt = _max(MIN_TPKT, m->m_len);
xd_build->x_xie = 0;
xd_build->x_eoxp = 1;
xd_build->x_eox = 1;
#if IP22 || IP26 || IP28
xd_build->x_txd = 0;
xd_build->x_xid = xid++;
#endif /* IP22 || IP26 || IP28 */
xd_build->x_mbuf = m;
if (cachewrback)
tclean_dcache(cp, xd_build->x_xbcnt);
/*
* starting xmit is sort of a hack because there is a small
* window in which STTRDMA can be high but not notice that
* x_eox has become zero, which will cause the packet to not
* go out until dma is restarted later in eput_finish
*/
ei->ei_ttail->x_eox = 0;
ei->ei_ttail = xd_build;
if (!(hio->trstat & HPC_STTRDMA)) {
#if IP22 || IP26 || IP28
xd_desc *xd;
xd_desc *xd0;
xd_desc *xd1;
#endif
if (carr_detect == 1)
reset_enet_carrier();
else if (carr_detect == 2) {
hio->seq_ctl = EDLC_NEW_FEATURES &
~(SEQ_CTL_NOCARR | SEQ_CTL_XCOLL);
hio->seq_ctl = EDLC_NEW_FEATURES;
}
/* need to kickstart the hpc */
#if IP20
/* XXX NEED TO CHECK FOR RETRANSMISSION XXX */
hio->ppfxbdp = hio->cpfxbdp = ENET_READ(hio->nxbdp);
hio->trstat = HPC_STTRDMA;
#endif /* IP20 */
#if IP22 || IP26 || IP28
xd0 = (xd_desc *)PHYS_TO_K1(ENET_READ(hio->ppfxbdp));
xd1 = (xd_desc *)PHYS_TO_K1(ENET_READ(hio->cpfxbdp));
if (xd0->x_txd && xd1->x_txd)
xd = (xd_desc *)ENET_READ(hio->nxbdp);
else if (!xd0->x_txd && !xd1->x_txd) {
if (((xd1->x_xid - xd0->x_xid) & XID_MASK) != XID_MASK)
xd = xd0;
else
xd = xd1;
} else if (!xd0->x_txd)
xd = xd0;
else if (!xd1->x_txd)
xd = xd1;
hio->ppfxbdp = hio->cpfxbdp = hio->nxbdp = KDM_TO_MCPHYS(xd);
#ifdef _MIPSEB
hio->trstat = HPC_STTRDMA;
#else
hio->trstat = HPC_STTRDMA | HPC_TR_ENDIAN_LITTLE;
#endif /* _MIPSEB */
DPRINTF2(("Starting transmit DMA in eput()\n"));
#endif /* IP22 || IP26 || IP28 */
}
return 0;
}
/*
* eput_finish -- lower half of eput. Recover mbufs, check for
* errors. Called while in scandevs()
*/
static void
eput_finish(void)
{
xd_desc *xd_chain;
xd_desc *xd0, *xd1;
u_int xstat;
xd_chain = ei->ei_tact;
xd0 = (xd_desc *)ENET_READ(hio->ppfxbdp);
xd1 = (xd_desc *)ENET_READ(hio->cpfxbdp);
while ((xd_desc *)KDM_TO_PHYS(xd_chain) != xd0
&& (xd_desc *)KDM_TO_PHYS(xd_chain) != xd1) {
_m_freem(xd_chain->x_mbuf);
xd_chain->x_eox = 1;
xd_chain->x_xbufptr = 0;
xd_chain = (xd_desc *)PHYS_TO_K1(xd_chain->x_nxdesc);
ei->ei_tnum--;
}
xstat = hio->trstat;
#if IP20
if (!(xstat & HPC_STTRDMA)) {
xd0 = (xd_desc *)(ENET_READ(hio->nxbdp));
while ((xd_desc *)KDM_TO_PHYS(xd_chain) != xd0) {
_m_freem(xd_chain->x_mbuf);
xd_chain->x_eox = 1;
xd_chain->x_xbufptr = 0;
xd_chain = (xd_desc *)PHYS_TO_K1(xd_chain->x_nxdesc);
ei->ei_tnum--;
}
if (xstat & SEQ_XS_UFLOW) {
DPRINTF(("ec0: transmit underflow\n"));
} else if (xstat & SEQ_XS_16TRY) {
DPRINTF(("ec0: 16 xmit collisions\n"));
} else if (ei->ei_tnum) {
DPRINTF(("ec0: late hpc dma start\n"));
}
if (ei->ei_tnum) {
/* need to kickstart the hpc */
hio->ppfxbdp = hio->cpfxbdp = ENET_READ(hio->nxbdp);
hio->trstat = HPC_STTRDMA;
} else if (carr_detect && !enet_carrier_on())
printf("ec0: ethernet cable problem\n");
}
#endif /* IP20 */
#if IP22 || IP26 || IP28
if (!(xstat & HPC_STTRDMA)) {
u_int xd;
xd0 = (xd_desc *)PHYS_TO_K1(ENET_READ(hio->ppfxbdp));
xd1 = (xd_desc *)PHYS_TO_K1(ENET_READ(hio->cpfxbdp));
if (xd0->x_txd && xd1->x_txd)
xd = ENET_READ(hio->nxbdp);
else if (!xd0->x_txd && !xd1->x_txd) {
if (((xd1->x_xid - xd0->x_xid) & XID_MASK) != XID_MASK)
xd = KDM_TO_MCPHYS(xd0);
else
xd = KDM_TO_MCPHYS(xd1);
} else if (!xd0->x_txd)
xd = KDM_TO_MCPHYS(xd0);
else if (!xd1->x_txd)
xd = KDM_TO_MCPHYS(xd1);
while (KDM_TO_PHYS(xd_chain) != xd) {
_m_freem(xd_chain->x_mbuf);
xd_chain->x_eox = 1;
xd_chain->x_xbufptr = 0;
xd_chain = (xd_desc *)PHYS_TO_K1(xd_chain->x_nxdesc);
ei->ei_tnum--;
}
if (xstat & SEQ_XS_UFLOW) {
DPRINTF(("ec0: transmit underflow\n"));
} else if (xstat & SEQ_XS_16TRY) {
DPRINTF(("ec0: 16 xmit collision\n"));
} else if (xstat & SEQ_XS_LATE_COLL) {
DPRINTF(("ec0: late xmit collision\n"));
} else if (ei->ei_tnum) {
DPRINTF(("ec0: late hpc dma start\n"));
}
if (ei->ei_tnum) {
/* need to kickstart the hpc */
hio->ppfxbdp = hio->cpfxbdp = hio->nxbdp = xd;
#ifdef _MIPSEB
hio->trstat = HPC_STTRDMA;
#else
hio->trstat = HPC_STTRDMA | HPC_TR_ENDIAN_LITTLE;
#endif /* _MIPSEB */
DPRINTF2(("Starting transmit DMA in eput_finish()\n"));
} else if (hio->seq_flags & SEQ_XS_NO_CARRIER) {
if (((hio->seq_coll_xmit[0] & 0xff) == 0)
&& ((hio->seq_coll_xmit[1] & 0x7) == 0))
printf("ec0: ethernet cable problem\n");
}
}
#endif /* IP22 || IP26 || IP28 */
ei->ei_tact = xd_chain;
}
static void
eread(void)
{
int bcnt;
rd_desc *rd_chain;
rd_desc *rd_tail, *rd_crbdp;
u_char rstat, *status;
u_int ircvstat;
struct mbuf *m;
bcnt = ei->ei_rnum;
rd_chain = ei->ei_ract; /* from beginning of chain */
while (!rd_chain->r_rown) {
struct ether_header *eh;
int rem, rlen;
if (Verbose > 1) printf (".");
m = rd_chain->r_mbuf;
eh = mtod(m, struct ether_header *);
#ifdef IP28 /* R10000_DMA_READ_WAR */
rlen = MAX_RPKT - rd_chain->r_rbcnt - HPC_RSPACE;
__dcache_inval(eh,rlen);
#endif
#ifdef DEBUG
if (Debug > 1) {
char *htoe();
printf("packet in: type %x", ntohs(eh->ether_type));
printf(" tgt %s", htoe(eh->ether_dhost));
printf(" src %s\n", htoe(eh->ether_shost));
}
#endif
#ifdef _OLDHPC
/*
* the cache needs to be flushed before byte swapping is
* done, but byte swapping needs to be done before this
* ddm stuff needs to be done. This flushes and swaps
* all of the data in the mbuf whether the packet is
* valid or not, and whether the packet is smaller than
* the mbuf data.
*/
if (needs_dma_swap)
swapl(m->m_dat, MLEN >> 2);
#endif
/*
* the DDM memory controller chip has a bug which
* allows the remaining byte count to occasionally
* be written into the bufptr field. This is a
* heuristic fix.
*/
rem = rd_chain->r_rbcnt;
rlen = MAX_RPKT - rem - HPC_RSPACE;
if (rlen <= 0) {
DPRINTF(("eread(): rec'd >MAX_RPKT packet: %d bytes\n",
rem));
_m_freem(m);
goto nextpkt;
}
rstat = *((caddr_t)eh + rlen);
if (!(rstat & SEQ_RS_GOOD)) {/* map error state */
#if IP22 || IP26 || IP28
if ((rstat & SEQ_RS_LATE_RXDC)
&& !(rstat & SEQ_RS_STATUS_5_0)) {
DPRINTF(("ec0: late receive discard.\n"));
}
if ((rstat & SEQ_RS_TIMEOUT)
&& (rstat & SEQ_RS_STATUS_5_0 == SEQ_RS_END)) {
DPRINTF(("ec0: receive timeout.\n"));
}
#endif /* IP22 || IP26 || IP28 */
if (rstat & SEQ_RS_SHORT) {
DPRINTF(("ec0: packet too small (%d).\n",rlen));
} else if (rstat & SEQ_RS_DRBL) {
DPRINTF(("ec0: packet framing error\n"));
} else if (rstat & SEQ_RS_CRC) {
DPRINTF(("ec0: CRC error\n"));
} else if (rstat & SEQ_RS_OFLOW) {
DPRINTF(("ec0: FIFO overflow error\n"));
}
_m_freem(m);
goto nextpkt;
}
m->m_len = rlen;
e_frwrd_pkt(m);
nextpkt: bcnt--;
rd_chain->r_rown = 1;
if (rd_chain->r_eor) /* stay sane */
((rd_desc *)PHYS_TO_K1(rd_chain->r_nrdesc))->r_rown = 1;
rd_chain = (rd_desc *)PHYS_TO_K1(rd_chain->r_nrdesc);
}
#ifdef DEBUG
if (Debug > 1) { /* check first 16 bytes to see if written */
u_int *dp = (u_int *)PHYS_TO_K1(rd_chain->r_rbufptr);
if (*dp++ || *dp++ || *dp++ || *dp)
DPRINTF(("eread(): buffer written by seeq without "
"ownership bit cleared\n"));
}
#endif
/*
* allocate new mbufs for the seeq to receive into
*/
rd_tail = ei->ei_rtail;
while (bcnt < NUM_RMD) {
rd_desc *rd;
if ((m = _m_get(M_DONTWAIT, MT_DATA)) == 0) {
printf("ec0: eread out of mbufs\n");
break;
}
rd = (rd_desc *)PHYS_TO_K1(rd_tail->r_nrdesc);
bcnt++;
rd->r_rbcnt = MAX_RPKT;
rd->r_rbufptr = KDM_TO_MCPHYS(mtod(m, char *));
#ifdef DEBUG
if (Debug > 1) {
u_int *dp = (u_int *)PHYS_TO_K1(rd->r_rbufptr);
*dp++ = 0;
*dp++ = 0;
*dp++ = 0;
*dp = 0;
}
#endif
rd->r_mbuf = m;
m->m_off = 2; /* hpc offsets rcv packets 2 bytes */
((rd_desc *)PHYS_TO_K1(rd->r_nrdesc))->r_rown = 1;
rd->r_eor = 1;
#if IP22 || IP26 || IP28
rd->r_eorp = 1;
rd->r_xie = 0;
#endif /* IP22 || IP26 || IP28 */
rd_tail->r_eor = 0;
rd_tail = rd;
tclean_dcache(m, sizeof(struct mbuf));
}
ei->ei_rtail = rd_tail;
ei->ei_rnum = bcnt;
ei->ei_ract = rd_chain; /* from beginning of chain */
ircvstat = hio->rcvstat;
if (!(ircvstat & HPC_STRCVDMA)) { /* stopped. why? */
rstat = ircvstat >> RCVSTAT_SHIFT;
rd_crbdp = (rd_desc *)PHYS_TO_K1(ENET_READ(hio->crbdp));
/*
* on the 2 error conditions for which we could be stopped,
* patch up the mbuf so that it will get tossed when the
* chain is received
*/
#if IP20
if (hio->ctl & HPC_RBO) {
hio->ctl = HPC_MODNORM | HPC_RBO;
#endif /* IP20 */
#if IP22 || IP26 || IP28
if (rstat & HPC_RBO) {
#endif /* IP22 || IP26 || IP28 */
DPRINTF(("ec0: packet too big"));
rd_crbdp->r_rbcnt = MAX_RPKT-HPC_RSPACE;
if (rd_crbdp->r_nrdesc != (__uint32_t)KDM_TO_MCPHYS(rd_chain))
rd_crbdp->r_rown = 0;
m = rd_crbdp->r_mbuf;
status = mtod(m, u_char *);
*status = 0; /* clear SEQ_RS_GOOD */
}
if (rd_crbdp != ei->ei_rtail) { /* restart if buffers avail */
hio->nrbdp = rd_crbdp->r_nrdesc;
hio->rcvstat = HPC_STRCVDMA;
}
}
}
/*
* e_frwrd_pkt -- forward packet to appropriate protocol handler
*/
static void
e_frwrd_pkt(struct mbuf *m)
{
struct ether_header *eh;
int len1, len2, off;
u_short type;
char *cp, *resid;
/*
* Get input data length.
* Get pointer to ethernet header (in input buffer).
* Deal with trailer protocol: if type is PUP trailer
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
*/
eh = mtod(m, struct ether_header *);
cp = (char *)eh;
m->m_off += sizeof(struct ether_header);
m->m_len -= sizeof(struct ether_header);
cp += sizeof(struct ether_header);
len1 = m->m_len;
#ifdef _MIPSEL
type = nuxi_s((u_short)eh->ether_type);
#else /*_MIPSEB*/
type = eh->ether_type;
#endif /*_MIPSEL*/
if (type >= ETHERTYPE_TRAIL
&& type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
off = (type - ETHERTYPE_TRAIL) * 512;
if (off >= ETHERMTU) {
DPRINTF(("e_frwrd_pkt(): discarding, off(%d) >= ETHERMTU\n",
off));
_m_freem(m);
return; /* sanity */
}
resid = cp + off;
#ifdef _MIPSEL
type = nuxi_s(*(u_short *)resid);
#else
type = *(u_short *)resid;
#endif /* _MIPSEL */
resid += sizeof(u_short);
#ifdef _MIPSEL
len2 = nuxi_s(*(u_short *)resid);
#else
len2 = *(u_short *)resid;
#endif /* _MIPSEL */
resid += sizeof(u_short);
if (off + len2 > len1) {
DPRINTF(("e_frwrd_pkt(): discarding, off(%d) + "
"len2(%d) > len1(%d)\n", off, len2, len1));
_m_freem(m);
return; /* sanity */
}
len2 -= 2*sizeof(u_short);
len1 = off;
} else
off = 0;
if( len1 == 0 ) {
DPRINTF(("e_frwrd_pkt(): discarding, len1 == 0\n"));
_m_freem(m);
return;
}
if (off) { /* trailer protocol */
struct mbuf *n;
if ((n = _m_get(M_DONTWAIT, MT_DATA)) == 0) {
#ifdef DEBUG
DPRINTF(("e_frwrd_pkt(): discarding, no mbuf\n"));
#endif
_m_freem(m);
return;
}
n->m_off = MMAXOFF;
n->m_len = len1 + len2;
bcopy(resid, mtod(n, char *), len2);
bcopy(cp, mtod(n, char *)+len2, len1);
_m_freem(m);
m = n;
}
switch (type) {
case ETHERTYPE_IP:
_ip_input(&ei->ei_if, m);
break;
case ETHERTYPE_ARP:
_arpinput(&ei->ei_ac, m);
break;
default:
DPRINTF(("e_frwrd_pkt(): discarding, unknown type %x\n", type));
_m_freem(m);
}
}
int
_eclose(void)
{
rd_desc *rd;
xd_desc *xd;
int bcnt;
while (hio->trstat & HPC_STTRDMA)
/* empty */;
ereset();
bcnt = ei->ei_rnum;
rd = ei->ei_ract;
while (bcnt--) {
_m_freem(rd->r_mbuf);
rd = (rd_desc *)PHYS_TO_K1(rd->r_nrdesc);
}
bcnt = ei->ei_tnum;
xd = ei->ei_tact;
while (bcnt--) {
_m_freem(xd->x_mbuf);
xd = (xd_desc *)PHYS_TO_K1(xd->x_nxdesc);
}
ei->ei_rnum = 0;
ei->ei_tnum = 0;
ei->ei_if.if_flags &= ~(IFF_UP|IFF_RUNNING|IFF_BROADCAST);
return 0;
}
/*ARGSUSED*/
static void
epoll(IOBLOCK *io)
{
eread();
if (ei->ei_tnum)
eput_finish();
}
#endif /* IP20 || IP22 || IP26 || IP28 */