#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_ie.c,v 1.57 2025/08/18 20:59:55 andvar Exp $");
#define IGNORE_ETHER1_IDROM_CHECKSUM
#include "opt_inet.h"
#include "opt_ns.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <machine/io.h>
#include <machine/intr.h>
#include <acorn32/podulebus/podulebus.h>
#include <dev/podulebus/podules.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_inarp.h>
#endif
#include "if_iereg.h"
#include <net/bpf.h>
#define PODULE_IRQ_PENDING (1)
#define NFRAMES (16)
#define NRXBUF (48)
#define IE_RXBUF_SIZE (256)
#define NTXBUF (2)
#define IE_TXBUF_SIZE (1522)
#define PWriteShort(a,b) WriteWord(a,(b)<<16|(b))
#define xoffsetof(type, member) (offsetof(type, member) << 1)
struct ie_softc {
device_t sc_dev;
int sc_podule_number;
podule_t *sc_podule;
irqhandler_t sc_ih;
int sc_flags;
#define IE_BROKEN 1
int sc_iobase;
int sc_fastbase;
int sc_rom;
int sc_ram;
int sc_control;
struct ethercom sc_ethercom;
u_short promisc;
int sc_irqmode;
u_long rframes[NFRAMES];
u_long rbuffs[NRXBUF];
u_long cbuffs[NRXBUF];
int rfhead, rftail, rbhead, rbtail;
u_long xmit_cmds[NTXBUF];
u_long xmit_buffs[NTXBUF];
u_long xmit_cbuffs[NTXBUF];
int xmit_count;
int xmit_free;
int xchead;
int xctail;
};
static void host2ie( struct ie_softc *sc, void *src, u_long dest, int size );
static void ie2host( struct ie_softc *sc, u_long src, void *dest, int size );
static void iezero( struct ie_softc *sc, u_long p, int size );
void iereset( struct ie_softc *sc );
void iewatchdog( struct ifnet *ifp );
int ieioctl( struct ifnet *ifp, u_long cmd, void *data );
void iestart( struct ifnet *ifp );
int iestop( struct ie_softc *sc );
int ieinit( struct ie_softc *sc );
int ieintr( void *arg );
void ietint( struct ie_softc *sc );
static int command_and_wait( struct ie_softc *sc, u_short cmd,
struct ie_sys_ctl_block *pscb,
void *pcmd, int ocmd, int scmd, int mask );
int ieprobe(device_t, cfdata_t, void *);
void ieattach(device_t, device_t, void *);
static inline void ie_cli(struct ie_softc *);
static inline void ieattn(struct ie_softc *);
static inline void setpage(struct ie_softc *, u_long);
static void ie_ack(struct ie_softc *, u_short);
void PWriteShorts(char *, char *, int);
void ReadShorts(char *, char *, int);
static void run_tdr(struct ie_softc *);
u_long setup_rfa(struct ie_softc *, u_long);
static inline int ie_buflen(struct ie_softc *, int);
static inline int ie_packet_len(struct ie_softc *);
struct mbuf *ieget(struct ie_softc *, int *);
void ie_drop_packet_buffer(struct ie_softc *);
void ie_read_frame(struct ie_softc *, int num);
void ierint(struct ie_softc *);
void iexmit(struct ie_softc *);
static void start_receiver(struct ie_softc *);
CFATTACH_DECL_NEW(ie, sizeof(struct ie_softc),
ieprobe, ieattach, NULL, NULL);
static inline void
ie_cli(struct ie_softc *sc)
{
WriteByte(sc->sc_fastbase + (IE_CONTROL<<2), IE_CONT_CLI);
}
static inline void
ieattn(struct ie_softc *sc)
{
WriteByte ( sc->sc_control + (IE_CONTROL<<2), IE_CONT_ATTN );
}
static inline void
setpage(struct ie_softc *sc, u_long off)
{
WriteByte ( sc->sc_control + (IE_PAGE<<2), IE_COFF2PAGE(off) );
}
static void
ie_ack(struct ie_softc *sc, u_short mask)
{
u_short stat;
int i;
setpage(sc, IE_IBASE + IE_SCB_OFF );
stat = ReadShort ( sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_status)) );
PWriteShort ( sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_command)),
stat & mask );
ieattn(sc);
for ( i=4000; --i>=0; ) {
if ( !ReadShort(sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_command))) )
break;
delay(100);
}
if ( i<=0 )
printf ( "ie: command timed out\n" );
ie_cli(sc);
}
#ifndef IGNORE_ETHER1_IDROM_CHECKSUM
static u_long
crc32(u_char *p, int l)
{
u_long crc=-1;
int i, b;
while ( --l >= 0 ) {
b = *p++;
for ( i=8; --i >= 0; b>>=1 )
if ((b&1)^(crc>>31))
crc=(crc<<1)^0x4c11db7;
else
crc<<=1;
}
return crc;
}
#endif
int
ieprobe(device_t parent, cfdata_t cf, void *aux)
{
struct podule_attach_args *pa = aux;
return (pa->pa_product == PODULE_ETHER1);
}
void
ieattach(device_t parent, device_t self, void *aux)
{
struct ie_softc *sc = device_private(self);
struct podule_attach_args *pa = aux;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
int i;
char idrom[32];
uint8_t hwaddr[ETHER_ADDR_LEN];
if (pa->pa_podule_number == -1)
panic("Podule has disappeared !");
sc->sc_dev = self;
sc->sc_podule_number = pa->pa_podule_number;
sc->sc_podule = pa->pa_podule;
podules[sc->sc_podule_number].attached = 1;
sc->sc_iobase = sc->sc_podule->sync_base;
sc->sc_fastbase = sc->sc_podule->fast_base;
sc->sc_rom = sc->sc_podule->sync_base;
sc->sc_control = sc->sc_podule->fast_base;
sc->sc_ram = sc->sc_podule->fast_base + IE_MEMOFF;
setpage(sc, IE_SCB_OFF);
for ( i=0; i<16; i++ )
idrom[i] = ReadByte ( sc->sc_rom + (i<<2) );
if ( ReadByte ( sc->sc_rom + (3<<2) ) != 0x03 ) {
printf(": Ether1 ROM probably broken. ECID corrupt\n");
sc->sc_flags |= IE_BROKEN;
return;
}
WriteByte ( sc->sc_fastbase + (IE_CONTROL<<2), IE_CONT_RESET );
delay(1000);
WriteByte ( sc->sc_fastbase + (IE_CONTROL<<2), 0 );
delay(10000);
ie_cli (sc);
{
struct ie_sys_conf_ptr scp;
bzero (&scp, sizeof(scp) );
scp.ie_iscp_ptr = (void *)IE_ISCP_ADDR;
host2ie(sc, &scp, IE_SCP_ADDR, sizeof (scp) );
}
{
struct ie_int_sys_conf_ptr iscp;
bzero ( &iscp, sizeof(iscp) );
iscp.ie_busy = 1;
iscp.ie_base = (void *)IE_IBASE;
iscp.ie_scb_offset = IE_SCB_OFF;
host2ie(sc, &iscp, IE_ISCP_ADDR, sizeof(iscp) );
}
iezero ( sc, IE_IBASE + IE_SCB_OFF, sizeof(struct ie_sys_ctl_block) );
ieattn(sc);
setpage ( sc, IE_ISCP_ADDR );
for ( i=10000; --i>=0; ) {
if ( !ReadShort( sc->sc_ram + IE_COFF2POFF(IE_ISCP_ADDR) +
( xoffsetof(struct ie_int_sys_conf_ptr, ie_busy)) ) )
break;
delay (10);
}
if ( i<=0 ) {
printf ( ": ether1 chipset didn't respond\n" );
sc->sc_flags |= IE_BROKEN;
return;
}
for ( i=1000; --i>=0 ; ) {
if ( ReadByte(sc->sc_rom + 0) & PODULE_IRQ_PENDING )
break;
delay (10);
}
if ( i<=0 ) {
printf ( ": interrupt from chipset didn't reach host\n" );
sc->sc_flags |= IE_BROKEN;
return;
}
ie_ack(sc,IE_ST_WHENCE);
ie_cli (sc);
for ( i=16; i<32; i++ )
idrom[i] = ReadByte ( sc->sc_rom + (i<<2) );
#ifndef IGNORE_ETHER1_IDROM_CHECKSUM
if ( crc32(idrom,28) != *(u_long *)(idrom+28) )
{
printf ( "ie: ether1 idrom failed checksum %08x!=%08x\n",
crc32(idrom,28), *(u_long *)(idrom+28));
for ( i=0; i<32; i+=8 ) {
printf ( "IDROM: %02x %02x %02x %02x %02x %02x %02x %02x\n",
idrom[0+i], idrom[1+i], idrom[2+i], idrom[3+i],
idrom[4+i], idrom[5+i], idrom[6+i], idrom[7+i] );
}
printf ( "ie: I'll ignore this fact for now!\n" );
}
#endif
for ( i=0; i<ETHER_ADDR_LEN; i++ )
hwaddr[i] = idrom[9+i];
memcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_start = iestart;
ifp->if_ioctl = ieioctl;
ifp->if_watchdog = iewatchdog;
ifp->if_flags = IFF_BROADCAST;
if_attach (ifp);
if_deferred_start_init(ifp, NULL);
ether_ifattach(ifp, hwaddr);
printf ( ": %s using %dk card ram",
ether_sprintf(hwaddr),
((NRXBUF*IE_RXBUF_SIZE)+(NTXBUF*IE_TXBUF_SIZE))/1024 );
sc->sc_ih.ih_func = ieintr;
sc->sc_ih.ih_arg = sc;
sc->sc_ih.ih_level = IPL_NET;
sc->sc_ih.ih_name = "net: ie";
sc->sc_ih.ih_maskaddr = sc->sc_podule->irq_addr;
sc->sc_ih.ih_maskbits = sc->sc_podule->irq_mask;
if (irq_claim(sc->sc_podule->interrupt, &sc->sc_ih)) {
sc->sc_irqmode = 0;
printf(" POLLED");
panic("%s: Cannot install IRQ handler", device_xname(sc->sc_dev));
} else {
sc->sc_irqmode = 1;
printf(" IRQ");
}
printf("\n");
}
void
PWriteShorts(char *src, char *dest, int cnt)
{
for (cnt /= 2; --cnt >= 0; ) {
PWriteShort(dest, *(u_short *)src);
src+=2;
dest+=4;
}
}
void
ReadShorts(char *src, char *dest, int cnt)
{
for (cnt /= 2; --cnt >= 0; ) {
*(u_short *)dest = ReadShort(src);
src+=4;
dest+=2;
}
}
static void
host2ie(struct ie_softc *sc, void *src, u_long dest, int size)
{
int cnt;
char *sptr = src;
#ifdef DIAGNOSTIC
if (size & 1)
panic("host2ie");
#endif
while (size > 0) {
cnt = IE_PAGESIZE - dest % IE_PAGESIZE;
if (cnt > size)
cnt = size;
setpage(sc, dest);
PWriteShorts(sptr, (char *)sc->sc_ram + IE_COFF2POFF(dest), cnt);
sptr+=cnt;
dest+=cnt;
size-=cnt;
}
}
static void
ie2host(struct ie_softc *sc, u_long src, void *dest, int size)
{
int cnt;
char *dptr = dest;
#ifdef DIAGNOSTIC
if (size & 1)
panic ( "ie2host" );
#endif
while (size > 0) {
cnt = IE_PAGESIZE - src % IE_PAGESIZE;
if (cnt > size)
cnt = size;
setpage(sc, src);
ReadShorts((char *)sc->sc_ram + IE_COFF2POFF(src), dptr, cnt);
src+=cnt;
dptr+=cnt;
size-=cnt;
}
}
static void
iezero(struct ie_softc *sc, u_long p, int size)
{
int cnt;
while (size > 0) {
cnt = IE_PAGESIZE - p % IE_PAGESIZE;
if (cnt > size)
cnt=size;
setpage(sc, p);
memset((char *)sc->sc_ram + IE_COFF2POFF(p), 0, 2*cnt);
p += cnt;
size -= cnt;
}
}
int
ieioctl(struct ifnet *ifp, unsigned long cmd, void *data)
{
struct ie_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
int s;
int error=0;
s=splnet();
switch ( cmd )
{
case SIOCINITIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family ) {
#ifdef INET
case AF_INET:
ieinit(sc);
arp_ifinit(ifp, ifa );
break;
#endif
default:
ieinit(sc);
break;
}
break;
#define IZSET(a,b) ((a->if_flags&b)!=0)
#define IZCLR(a,b) ((a->if_flags&b)==0)
#define DOSET(a,b) (a->if_flags|=b)
#define DOCLR(a,b) (a->if_flags&=~b)
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
sc->promisc = ifp->if_flags & ( IFF_PROMISC | IFF_ALLMULTI );
if ( IZCLR(ifp,IFF_UP) && IZSET(ifp,IFF_RUNNING) )
{
iestop(sc);
DOCLR(ifp,IFF_RUNNING);
}
else if ( IZSET(ifp,IFF_UP) && IZCLR(ifp,IFF_RUNNING) )
{
ieinit(sc);
}
else
{
iestop(sc);
ieinit(sc);
}
default:
error = ether_ioctl(ifp, cmd, data);
break;
}
(void)splx(s);
return error;
}
void
iereset(struct ie_softc *sc)
{
struct ie_sys_ctl_block scb;
int s = splnet();
iestop(sc);
ie2host(sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb);
if (command_and_wait(sc, IE_RU_ABORT|IE_CU_ABORT, 0, 0, 0, 0, 0))
printf("ie0: abort commands timed out\n");
if (command_and_wait(sc, IE_RU_DISABLE|IE_CU_STOP, 0, 0, 0, 0, 0))
printf("ie0: abort commands timed out\n");
ieinit(sc);
(void)splx(s);
}
void
iewatchdog(struct ifnet *ifp)
{
struct ie_softc *sc = ifp->if_softc;
log(LOG_ERR, "%s: device timeout\n", device_xname(sc->sc_dev));
if_statinc(ifp, if_oerrors);
iereset(sc);
}
static void
run_tdr(struct ie_softc *sc)
{
struct ie_sys_ctl_block scb;
u_long ptr = IE_IBASE + IE_SCB_OFF + sizeof scb;
struct ie_tdr_cmd cmd;
int result;
bzero ( &scb, sizeof(scb) );
bzero ( &cmd, sizeof(cmd) );
cmd.com.ie_cmd_status = 0;
cmd.com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
cmd.com.ie_cmd_link = 0xffff;
cmd.ie_tdr_time = 0;
scb.ie_command_list = (u_short)ptr;
result=0;
if ( command_and_wait(sc, IE_CU_START, &scb, &cmd, ptr, sizeof cmd,
IE_STAT_COMPL) )
{
result = 0x10000;
}
else if ( !(cmd.com.ie_cmd_status & IE_STAT_OK) )
{
result = 0x10000;
}
if ( result==0 )
result = cmd.ie_tdr_time;
ie_ack ( sc, IE_ST_WHENCE );
if (result & IE_TDR_SUCCESS )
return;
if ( result & 0x10000 )
{
printf ( "ie: TDR command failed\n" );
}
else if ( result & IE_TDR_XCVR )
{
printf ( "ie: transceiver problem. Is it plugged in?\n" );
}
else if ( result & IE_TDR_OPEN )
{
if ((result & IE_TDR_TIME)>0)
printf ( "ie: TDR detected an open %d clocks away.\n",
result & IE_TDR_TIME );
}
else if ( result & IE_TDR_SHORT )
{
if ((result & IE_TDR_TIME)>0)
printf ( "ie: TDR detected a short %d clock away.\n",
result & IE_TDR_TIME );
}
else
{
printf ( "ie: TDR returned unknown status %x\n", result );
}
}
u_long
setup_rfa(struct ie_softc *sc, u_long ptr)
{
int i;
{
struct ie_recv_frame_desc rfd;
memset( &rfd, 0, sizeof rfd );
for ( i=0; i<NFRAMES; i++ )
{
sc->rframes[i] = ptr;
rfd.ie_fd_next = ptr + sizeof rfd;
host2ie(sc, (char *)&rfd, ptr, sizeof rfd);
ptr += sizeof rfd;
}
rfd.ie_fd_next = sc->rframes[0];
rfd.ie_fd_last |= IE_FD_LAST;
host2ie(sc, (char *)&rfd, sc->rframes[NFRAMES-1], sizeof rfd );
ie2host(sc, sc->rframes[0], (char *)&rfd, sizeof rfd );
rfd.ie_fd_buf_desc = (u_short) ptr;
host2ie(sc, (char *)&rfd, sc->rframes[0], sizeof rfd );
}
{
struct ie_recv_buf_desc rbd;
memset(&rbd, 0, sizeof rbd);
for ( i=0; i<NRXBUF; i++ )
{
sc->rbuffs[i] = ptr;
rbd.ie_rbd_length = IE_RXBUF_SIZE;
rbd.ie_rbd_buffer = (void *)(ptr + sizeof rbd);
rbd.ie_rbd_next = (u_short)(ptr + sizeof rbd + IE_RXBUF_SIZE);
host2ie(sc, &rbd, ptr, sizeof rbd);
ptr+=sizeof rbd;
sc->cbuffs[i] = ptr;
ptr+=IE_RXBUF_SIZE;
}
rbd.ie_rbd_next = sc->rbuffs[0];
rbd.ie_rbd_length |= IE_RBD_LAST;
host2ie(sc, &rbd, sc->rbuffs[NRXBUF-1], sizeof rbd);
}
sc->rfhead = 0;
sc->rftail = NFRAMES-1;
sc->rbhead = 0;
sc->rbtail = NRXBUF-1;
{
struct ie_sys_ctl_block scb;
bzero ( &scb, sizeof scb );
scb.ie_recv_list = (u_short)sc->rframes[0];
host2ie(sc, (char *)&scb, (IE_IBASE + IE_SCB_OFF), sizeof scb );
}
return ptr;
}
static void
start_receiver(struct ie_softc *sc)
{
struct ie_sys_ctl_block scb;
ie2host ( sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb );
scb.ie_recv_list = (u_short)sc->rframes[0];
command_and_wait(sc, IE_RU_START, &scb, 0, 0, 0, 0);
ie_ack(sc, IE_ST_WHENCE );
}
int
ieinit(struct ie_softc *sc)
{
struct ifnet *ifp;
struct ie_sys_ctl_block scb;
struct ie_config_cmd cmd;
struct ie_iasetup_cmd iasetup_cmd;
u_long ptr = IE_IBASE + IE_SCB_OFF + sizeof scb;
int n;
ifp = &sc->sc_ethercom.ec_if;
bzero ( &scb, sizeof(scb) );
cmd.com.ie_cmd_status = 0;
cmd.com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
cmd.com.ie_cmd_link = 0xffff;
cmd.ie_config_count = 0x0c;
cmd.ie_fifo = 8;
cmd.ie_save_bad = 0x40;
cmd.ie_addr_len = 0x2e;
cmd.ie_priority = 0;
cmd.ie_ifs = 0x60;
cmd.ie_slot_low = 0;
cmd.ie_slot_high = 0xf2;
cmd.ie_promisc = 0;
cmd.ie_crs_cdt = 0;
cmd.ie_min_len = 64;
cmd.ie_junk = 0xff;
scb.ie_command_list = (u_short)ptr;
if ( command_and_wait(sc, IE_CU_START, &scb, &cmd, ptr, sizeof cmd,
IE_STAT_COMPL) )
{
printf ( "%s: command failed: timeout\n", device_xname(sc->sc_dev));
return 0;
}
if ( !(cmd.com.ie_cmd_status & IE_STAT_OK) )
{
printf ( "%s: command failed: !IE_STAT_OK\n", device_xname(sc->sc_dev));
return 0;
}
iasetup_cmd.com.ie_cmd_status = 0;
iasetup_cmd.com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
iasetup_cmd.com.ie_cmd_link = 0xffff;
bcopy ( CLLADDR(ifp->if_sadl), (void *) &iasetup_cmd.ie_address,
sizeof (iasetup_cmd.ie_address) );
if ( command_and_wait(sc, IE_CU_START, &scb, &iasetup_cmd, ptr, sizeof cmd,
IE_STAT_COMPL) )
{
printf ( "%s: iasetup failed : timeout\n", device_xname(sc->sc_dev));
return 0;
}
if ( !(cmd.com.ie_cmd_status & IE_STAT_OK) )
{
printf ( "%s: iasetup failed : !IE_STAT_OK\n", device_xname(sc->sc_dev));
return 0;
}
ie_ack ( sc, IE_ST_WHENCE );
run_tdr ( sc );
ie_ack ( sc, IE_ST_WHENCE );
ptr = setup_rfa(sc, ptr);
ifp->if_flags |= IFF_RUNNING;
for ( n=0; n<NTXBUF; n++ ) {
sc->xmit_cmds[n] = ptr;
iezero(sc, ptr, sizeof(struct ie_xmit_cmd) );
ptr += sizeof(struct ie_xmit_cmd);
sc->xmit_buffs[n] = ptr;
iezero(sc, ptr, sizeof(struct ie_xmit_buf));
ptr += sizeof(struct ie_xmit_buf);
}
for ( n=0; n<NTXBUF; n++ ) {
sc->xmit_cbuffs[n] = ptr;
ptr += IE_TXBUF_SIZE;
}
sc->xmit_free = NTXBUF;
sc->xchead = sc->xctail = 0;
{
struct ie_xmit_cmd xmcmd;
bzero ( &xmcmd, sizeof xmcmd );
xmcmd.ie_xmit_status = IE_STAT_COMPL;
host2ie(sc, &xmcmd, sc->xmit_cmds[0], sizeof xmcmd);
}
start_receiver (sc);
return 0;
}
int
iestop(struct ie_softc *sc)
{
struct ie_sys_ctl_block scb;
int s = splnet();
ie2host ( sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb );
if ( command_and_wait(sc, IE_RU_DISABLE, &scb, 0, 0, 0, 0) )
printf ( "ie0: abort commands timed out\n" );
(void)splx(s);
return(0);
}
static int
command_and_wait(struct ie_softc *sc, u_short cmd, struct ie_sys_ctl_block *pscb, void *pcmd, int ocmd, int scmd, int mask)
{
int i=0;
if ( pcmd )
host2ie(sc, pcmd, ocmd, scmd);
if ( pscb ) {
pscb->ie_command = cmd;
host2ie(sc, pscb, IE_IBASE + IE_SCB_OFF, sizeof *pscb);
}
else
{
setpage ( sc, IE_IBASE + IE_SCB_OFF );
PWriteShort ( sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_command)), cmd );
}
ieattn(sc);
if ( IE_ACTION_COMMAND(cmd) && pcmd )
{
setpage(sc,ocmd);
for ( i=4000; --i>=0; ) {
if ( ReadShort(sc->sc_ram + IE_COFF2POFF(ocmd) +
(xoffsetof(struct ie_config_cmd, ie_config_status))) & mask)
break;
delay(100);
}
}
else
{
for ( i=4000; --i>=0; ) {
if ( !ReadShort(sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_command))) )
break;
delay(100);
}
}
if ( pscb )
ie2host(sc, IE_IBASE + IE_SCB_OFF, pscb, sizeof *pscb );
if ( pcmd )
ie2host(sc, ocmd, pcmd, scmd);
return i < 0;
}
#define READ_MEMBER(sc,type,member,ptr,dest) \
setpage(sc, ptr); \
dest = ReadShort(sc->sc_ram + IE_COFF2POFF(ptr) + \
(xoffsetof(type, member)) );
#define WRITE_MEMBER(sc,type,member,ptr,dest) \
setpage(sc, ptr); \
PWriteShort(sc->sc_ram + IE_COFF2POFF(ptr) + \
(xoffsetof(type, member)), dest );
static inline int
ie_buflen(struct ie_softc *sc, int head)
{
int actual;
READ_MEMBER(sc,struct ie_recv_buf_desc, ie_rbd_actual,
sc->rbuffs[head], actual );
return(actual & (IE_RXBUF_SIZE | (IE_RXBUF_SIZE-1))) ;
}
static inline int
ie_packet_len(struct ie_softc *sc)
{
int i;
int actual;
int head = sc->rbhead;
int acc=0;
do {
READ_MEMBER(sc,struct ie_recv_buf_desc, ie_rbd_actual,
sc->rbuffs[sc->rbhead], actual );
if (!(actual&IE_RBD_USED))
{
return (-1);
}
READ_MEMBER(sc,struct ie_recv_buf_desc, ie_rbd_actual,
sc->rbuffs[head], i );
i = i & IE_RBD_LAST;
acc += ie_buflen(sc, head);
head = (head+1) % NRXBUF;
} while (!i);
return acc;
}
struct mbuf *
ieget(struct ie_softc *sc, int *to_bpf )
{
struct mbuf *top, **mp, *m;
int head;
int resid, totlen, thisrboff, thismboff;
int len;
struct ether_header eh;
totlen = ie_packet_len(sc);
if ( totlen > ETHER_MAX_LEN )
{
printf ( "ie: Gosh that packet was s-o-o-o big.\n" );
return 0;
}
if ( totlen<=0 )
return 0;
head = sc->rbhead;
ie2host ( sc, sc->cbuffs[head], (void *)&eh, sizeof eh );
resid = totlen;
MGETHDR ( m, M_DONTWAIT, MT_DATA );
if ( m==0 )
return 0;
m_set_rcvif(m, &sc->sc_ethercom.ec_if);
m->m_pkthdr.len = totlen;
len = MHLEN;
top = 0;
mp = ⊤
while (totlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
m_freem(top);
return 0;
}
len = MLEN;
}
if (totlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (m->m_flags & M_EXT)
len = MCLBYTES;
}
if (mp == &top) {
void *newdata = (void *)
ALIGN(m->m_data + sizeof(struct ether_header)) -
sizeof(struct ether_header);
len -= newdata - m->m_data;
m->m_data = newdata;
}
m->m_len = len = min(totlen, len);
totlen -= len;
*mp = m;
mp = &m->m_next;
}
m = top;
thismboff = 0;
memcpy(mtod(m, void *), &eh, sizeof(struct ether_header));
thismboff = sizeof(struct ether_header);
thisrboff = sizeof(struct ether_header);
resid -= sizeof(struct ether_header);
while (resid > 0) {
int thisrblen = ie_buflen(sc, head) - thisrboff,
thismblen = m->m_len - thismboff;
len = min(thisrblen, thismblen);
if ( len&1 )
{
ie2host(sc, sc->cbuffs[head]+thisrboff,
mtod(m, void *) + thismboff, (u_int)len+1);
}
else
{
ie2host(sc, sc->cbuffs[head]+thisrboff,
mtod(m, void *) + thismboff, (u_int)len);
}
resid -= len;
if (len == thismblen) {
m = m->m_next;
thismboff = 0;
} else
thismboff += len;
if (len == thisrblen) {
head = (head + 1) % NRXBUF;
thisrboff = 0;
} else
thisrboff += len;
}
return top;
}
void
ie_drop_packet_buffer(struct ie_softc *sc)
{
int i, actual, last;
do {
READ_MEMBER(sc,struct ie_recv_buf_desc, ie_rbd_actual,
sc->rbuffs[sc->rbhead], actual );
if (!(actual&IE_RBD_USED))
{
iereset(sc);
return;
}
i = actual & IE_RBD_LAST;
READ_MEMBER(sc,struct ie_recv_buf_desc,ie_rbd_length,
sc->rbuffs[sc->rbhead], last );
last |= IE_RBD_LAST;
WRITE_MEMBER(sc,struct ie_recv_buf_desc,ie_rbd_length,
sc->rbuffs[sc->rbhead], last );
WRITE_MEMBER(sc,struct ie_recv_buf_desc,ie_rbd_actual,
sc->rbuffs[sc->rbhead], 0 );
sc->rbhead = ( sc->rbhead + 1 ) % NRXBUF;
READ_MEMBER(sc,struct ie_recv_buf_desc,ie_rbd_length,
sc->rbuffs[sc->rbtail], last );
last &= ~IE_RBD_LAST;
WRITE_MEMBER(sc,struct ie_recv_buf_desc,ie_rbd_length,
sc->rbuffs[sc->rbtail], last );
sc->rbtail = ( sc->rbtail + 1 ) % NRXBUF;
} while (!i);
}
void
ie_read_frame(struct ie_softc *sc, int num)
{
int status;
struct ie_recv_frame_desc rfd;
struct mbuf *m=0;
struct ifnet *ifp;
int last;
ifp = &sc->sc_ethercom.ec_if;
ie2host(sc, sc->rframes[num], &rfd, sizeof rfd );
status = rfd.ie_fd_status;
WRITE_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_status,
sc->rframes[num], 0 );
READ_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_last,
sc->rframes[num], last );
last |= IE_FD_LAST;
WRITE_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_last,
sc->rframes[num], last );
READ_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_last,
sc->rframes[sc->rftail], last );
last &= ~IE_FD_LAST;
WRITE_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_last,
sc->rframes[sc->rftail], last );
sc->rftail = ( sc->rftail + 1 ) % NFRAMES;
sc->rfhead = ( sc->rfhead + 1 ) % NFRAMES;
if ( status & IE_FD_OK ) {
m = ieget(sc, 0);
ie_drop_packet_buffer(sc);
}
if ( m==0 ) {
if_statinc(ifp, if_ierrors);
return;
}
if_percpuq_enqueue(ifp->if_percpuq, m);
}
void
ierint(struct ie_softc *sc)
{
int i;
int times_thru = 1024;
struct ie_sys_ctl_block scb;
int status;
int safety_catch = 0;
i = sc->rfhead;
for (;;) {
if ( (safety_catch++)>100 )
{
printf ( "ie: ierint safety catch tripped\n" );
iereset(sc);
return;
}
READ_MEMBER(sc,struct ie_recv_frame_desc,ie_fd_status,
sc->rframes[i],status);
if ((status&IE_FD_COMPLETE)&&(status&IE_FD_OK)) {
if ( !--times_thru ) {
printf ( "IERINT: Uh oh. Nuts, look at this bit!!!\n" );
ie2host ( sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb );
sc->sc_ethercom.ec_if.if_ierrors += scb.ie_err_crc +
scb.ie_err_align +
scb.ie_err_resource +
scb.ie_err_overrun;
scb.ie_err_crc = scb.ie_err_align = 0;
scb.ie_err_resource = scb.ie_err_overrun = 0;
host2ie(sc, &scb, IE_SCP_ADDR, sizeof (scb) );
}
ie_read_frame(sc, i);
} else {
ie2host ( sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb );
if ( ((status&IE_FD_RNR)!=0) && ((scb.ie_status&IE_RU_READY)==0) )
{
WRITE_MEMBER(sc,struct ie_recv_frame_desc, ie_fd_buf_desc,
sc->rframes[0], sc->rbuffs[0] );
scb.ie_recv_list = sc->rframes[0];
host2ie(sc, (char *)&scb, IE_IBASE + IE_SCB_OFF, sizeof (scb) );
command_and_wait(sc, IE_RU_START, &scb, 0, 0, 0, 0);
}
break;
}
i = (i + 1) % NFRAMES;
}
}
static int in_intr = 0;
int
ieintr(void *arg)
{
struct ie_softc *sc = arg;
u_short status;
int safety_catch = 0;
static int safety_net = 0;
if (in_intr == 1)
panic ( "ie: INTERRUPT REENTERED\n" );
ie_cli (sc);
setpage(sc, IE_IBASE + IE_SCB_OFF );
status = ReadShort ( sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_status)) );
status = status & IE_ST_WHENCE;
if (status == 0) {
in_intr = 0;
return(0);
}
loop:
ie_ack(sc, status);
if (status & (IE_ST_FR | IE_ST_RNR))
ierint(sc);
if (status & IE_ST_CX)
ietint(sc);
if (status & IE_ST_RNR) {
printf ( "ie: receiver not ready\n" );
sc->sc_ethercom.ec_if.if_ierrors++;
iereset(sc);
}
setpage(sc, IE_IBASE + IE_SCB_OFF );
status = ReadShort ( sc->sc_ram + IE_COFF2POFF(IE_IBASE+IE_SCB_OFF) +
(xoffsetof(struct ie_sys_ctl_block, ie_status)) );
status = status & IE_ST_WHENCE;
ie_cli(sc);
if (status == 0) {
in_intr = 0;
return(0);
}
if ( safety_catch++ > 10 )
{
printf ( "ie: Interrupt couldn't be cleared\n" );
delay ( 1000 );
ie_cli(sc);
if ( safety_net++ > 50 )
{
}
in_intr = 0;
return(0);
}
goto loop;
}
void
iexmit(struct ie_softc *sc)
{
struct ie_sys_ctl_block scb;
struct ie_xmit_cmd xc;
struct ie_xmit_buf xb;
ie2host(sc, sc->xmit_buffs[sc->xctail], (char *)&xb, sizeof xb );
xb.ie_xmit_flags |= IE_XMIT_LAST;
xb.ie_xmit_next = 0xffff;
xb.ie_xmit_buf = (void *)sc->xmit_cbuffs[sc->xctail];
host2ie(sc, &xb, sc->xmit_buffs[sc->xctail], sizeof xb );
bzero ( &xc, sizeof xc );
xc.com.ie_cmd_link = 0xffff;
xc.com.ie_cmd_cmd = IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST;
xc.ie_xmit_status = 0x0000;
xc.ie_xmit_desc = sc->xmit_buffs[sc->xctail];
host2ie(sc, (char *)&xc, sc->xmit_cmds[sc->xctail], sizeof xc );
ie2host ( sc, IE_IBASE + IE_SCB_OFF, &scb, sizeof scb );
scb.ie_command_list = sc->xmit_cmds[sc->xctail];
host2ie(sc, (char *)&scb, (IE_IBASE + IE_SCB_OFF), sizeof scb );
command_and_wait(sc, IE_CU_START, &scb, &xc, sc->xmit_cmds[sc->xctail]
, sizeof xc, IE_STAT_COMPL);
sc->sc_ethercom.ec_if.if_timer = 5;
}
void
iestart(struct ifnet *ifp)
{
struct ie_softc *sc = ifp->if_softc;
struct mbuf *m0, *m;
u_char *buffer;
u_short len;
char txbuf[IE_TXBUF_SIZE];
int safety_catch = 0;
while (sc->xmit_free != 0) {
if ( (safety_catch++)>100 )
{
printf ( "ie: iestart safety catch tripped\n" );
iereset(sc);
return;
}
IF_DEQUEUE(&ifp->if_snd, m);
if (!m)
break;
len = 0;
buffer = txbuf;
for (m0 = m; m && (len + m->m_len) < IE_TXBUF_SIZE;
m = m->m_next) {
memcpy(buffer, mtod(m, void *), m->m_len);
buffer += m->m_len;
len += m->m_len;
}
bpf_mtap(ifp, m0, BPF_D_OUT);
m_freem(m0);
if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) {
memset(buffer, 0, ETHER_MIN_LEN - ETHER_CRC_LEN - len);
len = ETHER_MIN_LEN - ETHER_CRC_LEN;
buffer += ETHER_MIN_LEN - ETHER_CRC_LEN;
}
if (len&1)
host2ie(sc, txbuf, sc->xmit_cbuffs[sc->xchead], len+1 );
else
host2ie(sc, txbuf, sc->xmit_cbuffs[sc->xchead], len );
WRITE_MEMBER(sc,struct ie_xmit_buf, ie_xmit_flags,
sc->xmit_buffs[sc->xchead], len)
if (sc->xmit_free == NTXBUF)
iexmit(sc);
sc->xchead = (sc->xchead + 1) % NTXBUF;
sc->xmit_free--;
}
}
void
ietint(struct ie_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
int status;
ifp->if_timer=0;
READ_MEMBER(sc,struct ie_xmit_cmd, ie_xmit_status,
sc->xmit_cmds[sc->xctail], status );
if (!(status&IE_STAT_COMPL) || (status & IE_STAT_BUSY) )
printf ( "ietint: command still busy!\n" );
if ( status & IE_STAT_OK ) {
if_statinc(ifp, if_opackets);
if_statadd(ifp, if_collisions, status & IE_XS_MAXCOLL);
} else {
if_statinc(ifp, if_oerrors);
if ( status & IE_STAT_ABORT )
printf ( "ie: send aborted\n" );
if ( status & IE_XS_LATECOLL )
printf ( "ie: late collision\n" );
if ( status & IE_XS_NOCARRIER )
printf ( "ie: no carrier\n" );
if ( status & IE_XS_LOSTCTS )
printf ( "ie: lost CTS\n" );
if ( status & IE_XS_UNDERRUN )
printf ( "ie: DMA underrun\n" );
if ( status & IE_XS_EXCMAX )
printf ( "ie: too many collisions\n" );
if_statadd(ifp, if_collisions, 16);
}
sc->xmit_free++;
sc->xctail = (sc->xctail + 1 ) % NTXBUF;
if ( sc->xmit_free<NTXBUF )
iexmit(sc);
if_schedule_deferred_start(ifp);
}