#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_ie.c,v 1.74 2024/02/09 22:08:33 andvar Exp $");
#include "opt_inet.h"
#include "opt_ns.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/bpf.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 <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/pmap.h>
#define XSWAP(y) ( (((y) & 0xff00) >> 8) | (((y) & 0xff) << 8) )
#define SWAP(x) ((u_short)(XSWAP((u_short)(x))))
#include "i82586.h"
#include "if_iereg.h"
#include "if_ievar.h"
#define IED_RINT 0x01
#define IED_TINT 0x02
#define IED_RNR 0x04
#define IED_CNA 0x08
#define IED_READFRAME 0x10
#define IED_ENQ 0x20
#define IED_XMIT 0x40
#define IED_ALL 0x7f
#ifdef IEDEBUG
#define inline
void print_rbd(volatile struct ie_recv_buf_desc *);
int in_ierint = 0;
int in_ietint = 0;
int ie_debug_flags = 0;
#endif
int ie_run_tdr = 0;
static void iewatchdog(struct ifnet *);
static int ieinit(struct ie_softc *);
static int ieioctl(struct ifnet *, u_long, void *);
static void iestart(struct ifnet *);
static void iereset(struct ie_softc *);
static int ie_setupram(struct ie_softc *);
static int cmd_and_wait(struct ie_softc *, int, void *, int);
static void ie_drop_packet_buffer(struct ie_softc *);
static void ie_readframe(struct ie_softc *, int);
static inline void ie_setup_config(struct ie_config_cmd *, int, int);
static void ierint(struct ie_softc *);
static void iestop(struct ie_softc *);
static void ietint(struct ie_softc *);
static void iexmit(struct ie_softc *);
static int mc_setup(struct ie_softc *, void *);
static void mc_reset(struct ie_softc *);
static void run_tdr(struct ie_softc *, struct ie_tdr_cmd *);
static void iememinit(struct ie_softc *);
static inline uint8_t *Align(char *);
static inline u_int Swap32(u_int);
static inline u_int vtop24(struct ie_softc *, void *);
static inline uint16_t vtop16sw(struct ie_softc *, void *);
static inline void ie_ack(struct ie_softc *, u_int);
static inline u_short ether_cmp(u_char *, uint8_t *);
static inline int ie_buflen(struct ie_softc *, int);
static inline int ie_packet_len(struct ie_softc *);
static inline struct mbuf * ieget(struct ie_softc *);
static inline u_int
vtop24(struct ie_softc *sc, void *ptr)
{
u_int pa;
pa = (vaddr_t)ptr - (vaddr_t)sc->sc_iobase;
#ifdef IEDEBUG
if (pa & ~0xffFFff)
panic("ie:vtop24");
#endif
return pa;
}
static inline u_short
vtop16sw(struct ie_softc *sc, void *ptr)
{
u_int pa;
pa = (vaddr_t)ptr - (vaddr_t)sc->sc_maddr;
#ifdef IEDEBUG
if (pa & ~0xFFff)
panic("ie:vtop16");
#endif
return SWAP(pa);
}
static inline u_int
Swap32(u_int x)
{
u_int y;
y = x & 0xFF;
y <<= 8; x >>= 8;
y |= x & 0xFF;
y <<= 8; x >>= 8;
y |= x & 0xFF;
y <<= 8; x >>= 8;
y |= x & 0xFF;
return y;
}
static inline uint8_t *
Align(char *ptr)
{
u_long l = (u_long)ptr;
l = (l + 3) & ~3L;
return (uint8_t *)l;
}
static inline void
ie_ack(struct ie_softc *sc, u_int mask)
{
volatile struct ie_sys_ctl_block *scb = sc->scb;
cmd_and_wait(sc, scb->ie_status & mask, 0, 0);
}
void
ie_attach(struct ie_softc *sc)
{
struct ifnet *ifp = &sc->sc_if;
printf(": macaddr %s\n", ether_sprintf(sc->sc_addr));
sc->ntxbuf = NTXBUF;
switch (sc->sc_msize) {
case 16384:
sc->nframes = 8 * 4;
sc->nrxbuf = 8 * 6;
break;
case 32768:
sc->nframes = 16 * 4;
sc->nrxbuf = 16 * 6;
break;
case 65536:
sc->nframes = 32 * 4;
sc->nrxbuf = 32 * 6;
break;
default:
sc->nframes = 0;
}
if (sc->nframes > MXFRAMES)
sc->nframes = MXFRAMES;
if (sc->nrxbuf > MXRXBUF)
sc->nrxbuf = MXRXBUF;
#ifdef IEDEBUG
aprint_debug_dev(sc->sc_dev,
"%dK memory, %d tx frames, %d rx frames, %d rx bufs\n",
(sc->sc_msize >> 10), sc->ntxbuf, sc->nframes, sc->nrxbuf);
#endif
if ((sc->nframes <= 0) || (sc->nrxbuf <= 0))
panic("%s: weird memory size", __func__);
if (ie_setupram(sc) == 0) {
aprint_error(": RAM CONFIG FAILED!\n");
return;
}
strcpy(ifp->if_xname, device_xname(sc->sc_dev));
ifp->if_softc = sc;
ifp->if_start = iestart;
ifp->if_ioctl = ieioctl;
ifp->if_watchdog = iewatchdog;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
if_attach(ifp);
if_deferred_start_init(ifp, NULL);
ether_ifattach(ifp, sc->sc_addr);
}
static int
ie_setupram(struct ie_softc *sc)
{
volatile struct ie_sys_conf_ptr *scp;
volatile struct ie_int_sys_conf_ptr *iscp;
volatile struct ie_sys_ctl_block *scb;
int off;
off = sc->buf_area_sz;
off &= ~3;
scp = sc->scp;
(sc->sc_memset)(__UNVOLATILE(scp), 0, sizeof(*scp));
off -= sizeof(*iscp);
iscp = (volatile void *)(sc->buf_area + off);
(sc->sc_memset)(__UNVOLATILE(iscp), 0, sizeof(*iscp));
sc->iscp = iscp;
off -= sizeof(*scb);
scb = (volatile void *)(sc->buf_area + off);
(sc->sc_memset)(__UNVOLATILE(scb), 0, sizeof(*scb));
sc->scb = scb;
sc->buf_area_sz = off;
scp->ie_bus_use = 0;
scp->ie_iscp_ptr = Swap32(vtop24(sc, __UNVOLATILE(iscp)));
iscp->ie_busy = 1;
iscp->ie_scb_offset = vtop16sw(sc, __UNVOLATILE(scb));
iscp->ie_base = Swap32(vtop24(sc, sc->sc_maddr));
scb->ie_command_list = SWAP(0xffff);
scb->ie_recv_list = SWAP(0xffff);
(sc->reset_586)(sc);
(sc->chan_attn)(sc);
delay(100);
if (iscp->ie_busy) {
return 0;
}
ie_ack(sc, IE_ST_WHENCE);
return 1;
}
static 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);
}
int
ie_intr(void *arg)
{
struct ie_softc *sc = arg;
uint16_t status;
int loopcnt;
if (sc->hard_type == IE_VME) {
volatile struct ievme *iev =
(volatile struct ievme *)sc->sc_reg;
if (iev->status & IEVME_PERR) {
printf("%s: parity error (ctrl 0x%x @ 0x%02x%04x)\n",
device_xname(sc->sc_dev), iev->pectrl,
iev->pectrl & IEVME_HADDR, iev->peaddr);
iev->pectrl = iev->pectrl | IEVME_PARACK;
}
}
status = sc->scb->ie_status;
if ((status & IE_ST_WHENCE) == 0)
return 0;
loopcnt = sc->nframes;
loop:
ie_ack(sc, IE_ST_WHENCE & status);
if (status & (IE_ST_RECV | IE_ST_RNR)) {
#ifdef IEDEBUG
in_ierint++;
if (sc->sc_debug & IED_RINT)
printf("%s: rint\n", device_xname(sc->sc_dev));
#endif
ierint(sc);
#ifdef IEDEBUG
in_ierint--;
#endif
}
if (status & IE_ST_DONE) {
#ifdef IEDEBUG
in_ietint++;
if (sc->sc_debug & IED_TINT)
printf("%s: tint\n", device_xname(sc->sc_dev));
#endif
ietint(sc);
#ifdef IEDEBUG
in_ietint--;
#endif
}
if (status & IE_ST_RNR) {
#ifdef IEDEBUG
printf("%s: receiver not ready\n", device_xname(sc->sc_dev));
#endif
if_statinc(&sc->sc_if, if_ierrors);
iereset(sc);
}
#ifdef IEDEBUG
if ((status & IE_ST_ALLDONE) && (sc->sc_debug & IED_CNA))
printf("%s: cna\n", device_xname(sc->sc_dev));
#endif
status = sc->scb->ie_status;
if (status & IE_ST_WHENCE) {
if (--loopcnt > 0)
goto loop;
log(LOG_ERR, "%s: interrupt stuck?\n",
device_xname(sc->sc_dev));
iereset(sc);
}
return 1;
}
void
ierint(struct ie_softc *sc)
{
volatile struct ie_sys_ctl_block *scb = sc->scb;
int i, status;
static int timesthru = 1024;
i = sc->rfhead;
for (;;) {
status = sc->rframes[i]->ie_fd_status;
if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) {
if (!--timesthru) {
if_statadd(&sc->sc_if, if_ierrors,
SWAP(scb->ie_err_crc) +
SWAP(scb->ie_err_align) +
SWAP(scb->ie_err_resource) +
SWAP(scb->ie_err_overrun));
scb->ie_err_crc = 0;
scb->ie_err_align = 0;
scb->ie_err_resource = 0;
scb->ie_err_overrun = 0;
timesthru = 1024;
}
ie_readframe(sc, i);
} else {
if ((status & IE_FD_RNR) != 0 &&
(scb->ie_status & IE_RU_READY) == 0) {
sc->rframes[0]->ie_fd_buf_desc = vtop16sw(sc,
__UNVOLATILE(sc->rbuffs[0]));
scb->ie_recv_list = vtop16sw(sc,
__UNVOLATILE(sc->rframes[0]));
cmd_and_wait(sc, IE_RU_START, 0, 0);
}
break;
}
i = (i + 1) % sc->nframes;
}
}
void
ietint(struct ie_softc *sc)
{
struct ifnet *ifp;
int status;
ifp = &sc->sc_if;
ifp->if_timer = 0;
status = sc->xmit_cmds[sc->xctail]->ie_xmit_status;
if (!(status & IE_STAT_COMPL) || (status & IE_STAT_BUSY))
printf("%s: command still busy!\n", __func__);
if (status & IE_STAT_OK) {
if_statadd2(ifp, if_opackets, 1,
if_collisions, SWAP(status & IE_XS_MAXCOLL));
} else {
if_statinc(ifp, if_oerrors);
if (status & IE_STAT_ABORT)
printf("%s: send aborted\n", device_xname(sc->sc_dev));
if (status & IE_XS_LATECOLL)
printf("%s: late collision\n",
device_xname(sc->sc_dev));
if (status & IE_XS_NOCARRIER)
printf("%s: no carrier\n", device_xname(sc->sc_dev));
if (status & IE_XS_LOSTCTS)
printf("%s: lost CTS\n", device_xname(sc->sc_dev));
if (status & IE_XS_UNDERRUN)
printf("%s: DMA underrun\n", device_xname(sc->sc_dev));
if (status & IE_XS_EXCMAX) {
if_statadd(ifp, if_collisions, 16);
}
}
if (sc->want_mcsetup) {
mc_setup(sc, (void *)sc->xmit_cbuffs[sc->xctail]);
sc->want_mcsetup = 0;
}
sc->xmit_busy--;
sc->xctail = (sc->xctail + 1) % NTXBUF;
if (sc->xmit_busy > 0)
iexmit(sc);
if_schedule_deferred_start(ifp);
}
static inline uint16_t
ether_cmp(uint8_t *one, uint8_t *two)
{
uint16_t *a = (uint16_t *)one;
uint16_t *b = (uint16_t *)two;
uint16_t diff;
diff = *a++ - *b++;
diff |= *a++ - *b++;
diff |= *a++ - *b++;
return diff;
}
#define ether_equal !ether_cmp
static inline int
ie_buflen(struct ie_softc *sc, int head)
{
int len;
len = SWAP(sc->rbuffs[head]->ie_rbd_actual);
len &= (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1));
return len;
}
static inline int
ie_packet_len(struct ie_softc *sc)
{
int i;
int head = sc->rbhead;
int acc = 0;
do {
if ((sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)
== 0) {
#ifdef IEDEBUG
print_rbd(sc->rbuffs[sc->rbhead]);
#endif
log(LOG_ERR,
"%s: receive descriptors out of sync at %d\n",
device_xname(sc->sc_dev), sc->rbhead);
iereset(sc);
return -1;
}
i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST;
acc += ie_buflen(sc, head);
head = (head + 1) % sc->nrxbuf;
} while (i == 0);
return acc;
}
static void
iexmit(struct ie_softc *sc)
{
struct ifnet *ifp;
ifp = &sc->sc_if;
#ifdef IEDEBUG
if (sc->sc_debug & IED_XMIT)
printf("%s: xmit buffer %d\n", device_xname(sc->sc_dev),
sc->xctail);
#endif
sc->xmit_buffs[sc->xctail]->ie_xmit_flags |= IE_XMIT_LAST;
sc->xmit_buffs[sc->xctail]->ie_xmit_next = SWAP(0xffff);
sc->xmit_buffs[sc->xctail]->ie_xmit_buf =
Swap32(vtop24(sc, sc->xmit_cbuffs[sc->xctail]));
sc->xmit_cmds[sc->xctail]->com.ie_cmd_link = SWAP(0xffff);
sc->xmit_cmds[sc->xctail]->com.ie_cmd_cmd =
IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST;
sc->xmit_cmds[sc->xctail]->ie_xmit_status = SWAP(0);
sc->xmit_cmds[sc->xctail]->ie_xmit_desc =
vtop16sw(sc, __UNVOLATILE(sc->xmit_buffs[sc->xctail]));
sc->scb->ie_command_list =
vtop16sw(sc, __UNVOLATILE(sc->xmit_cmds[sc->xctail]));
cmd_and_wait(sc, IE_CU_START, 0, 0);
ifp->if_timer = 5;
}
static inline struct mbuf *
ieget(struct ie_softc *sc)
{
struct mbuf *top, **mp, *m;
int len, totlen, resid;
int thisrboff, thismboff;
int head;
struct ether_header eh;
totlen = ie_packet_len(sc);
if (totlen <= 0)
return 0;
head = sc->rbhead;
(sc->sc_memcpy)((void *)&eh, (void *)sc->cbuffs[head],
sizeof(struct ether_header));
resid = totlen;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
return 0;
m_set_rcvif(m, &sc->sc_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) {
char *newdata = (char *)
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 = uimin(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;
int thismblen = m->m_len - thismboff;
len = uimin(thisrblen, thismblen);
(sc->sc_memcpy)(mtod(m, char *) + thismboff,
(void *)(sc->cbuffs[head] + thisrboff),
(u_int)len);
resid -= len;
if (len == thismblen) {
m = m->m_next;
thismboff = 0;
} else
thismboff += len;
if (len == thisrblen) {
head = (head + 1) % sc->nrxbuf;
thisrboff = 0;
} else
thisrboff += len;
}
return top;
}
static void
ie_readframe(struct ie_softc *sc, int num)
{
int status;
struct mbuf *m = 0;
status = sc->rframes[num]->ie_fd_status;
sc->rframes[num]->ie_fd_status = SWAP(0);
sc->rframes[num]->ie_fd_last |= IE_FD_LAST;
sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST;
sc->rftail = (sc->rftail + 1) % sc->nframes;
sc->rfhead = (sc->rfhead + 1) % sc->nframes;
if (status & IE_FD_OK) {
m = ieget(sc);
ie_drop_packet_buffer(sc);
}
if (m == 0) {
if_statinc(&sc->sc_if, if_ierrors);
return;
}
#ifdef IEDEBUG
if (sc->sc_debug & IED_READFRAME) {
struct ether_header *eh = mtod(m, struct ether_header *);
printf("%s: frame from ether %s type 0x%x\n",
device_xname(sc->sc_dev),
ether_sprintf(eh->ether_shost), (u_int)eh->ether_type);
}
#endif
if_percpuq_enqueue((&sc->sc_if)->if_percpuq, m);
}
static void
ie_drop_packet_buffer(struct ie_softc *sc)
{
int i;
do {
if ((sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)
== 0) {
#ifdef IEDEBUG
print_rbd(sc->rbuffs[sc->rbhead]);
#endif
log(LOG_ERR,
"%s: receive descriptors out of sync at %d\n",
device_xname(sc->sc_dev), sc->rbhead);
iereset(sc);
return;
}
i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST;
sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST;
sc->rbuffs[sc->rbhead]->ie_rbd_actual = SWAP(0);
sc->rbhead = (sc->rbhead + 1) % sc->nrxbuf;
sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST;
sc->rbtail = (sc->rbtail + 1) % sc->nrxbuf;
} while (i == 0);
}
static void
iestart(struct ifnet *ifp)
{
struct ie_softc *sc = ifp->if_softc;
struct mbuf *m0, *m;
uint8_t *buffer;
uint16_t len;
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING)
return;
while (sc->xmit_busy < sc->ntxbuf) {
IF_DEQUEUE(&ifp->if_snd, m0);
if (m0 == 0)
break;
if ((m0->m_flags & M_PKTHDR) == 0)
panic("%s: no header mbuf", __func__);
bpf_mtap(ifp, m0, BPF_D_OUT);
#ifdef IEDEBUG
if (sc->sc_debug & IED_ENQ)
printf("%s: fill buffer %d\n", device_xname(sc->sc_dev),
sc->xchead);
#endif
buffer = sc->xmit_cbuffs[sc->xchead];
for (m = m0; m != 0; m = m->m_next) {
(sc->sc_memcpy)(buffer, mtod(m, void *), m->m_len);
buffer += m->m_len;
}
if (m0->m_pkthdr.len < ETHER_MIN_LEN - ETHER_CRC_LEN) {
sc->sc_memset(buffer, 0,
ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len);
len = ETHER_MIN_LEN - ETHER_CRC_LEN;
} else
len = m0->m_pkthdr.len;
m_freem(m0);
sc->xmit_buffs[sc->xchead]->ie_xmit_flags = SWAP(len);
if (sc->xmit_busy == 0)
iexmit(sc);
sc->xchead = (sc->xchead + 1) % sc->ntxbuf;
sc->xmit_busy++;
}
}
static void
iereset(struct ie_softc *sc)
{
int s;
s = splnet();
iestop(sc);
if (cmd_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0))
printf("%s: abort commands timed out\n",
device_xname(sc->sc_dev));
if (cmd_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0))
printf("%s: disable commands timed out\n",
device_xname(sc->sc_dev));
ieinit(sc);
splx(s);
}
static int
cmd_and_wait(struct ie_softc *sc, int cmd, void *pcmd, int mask)
{
volatile struct ie_cmd_common *cc = pcmd;
volatile struct ie_sys_ctl_block *scb = sc->scb;
int tmo;
scb->ie_command = (uint16_t)cmd;
(sc->chan_attn)(sc);
tmo = 10;
while (scb->ie_command && --tmo)
delay(10);
if (scb->ie_command) {
#ifdef IEDEBUG
printf("%s: cmd_and_wait, CU stuck (1)\n",
device_xname(sc->sc_dev));
#endif
return -1;
}
if (IE_ACTION_COMMAND(cmd) && pcmd) {
tmo = 36900;
while (((cc->ie_cmd_status & mask) == 0) && --tmo)
delay(10);
if ((cc->ie_cmd_status & mask) == 0) {
#ifdef IEDEBUG
printf("%s: cmd_and_wait, CU stuck (2)\n",
device_xname(sc->sc_dev));
#endif
return -1;
}
}
return 0;
}
static void
run_tdr(struct ie_softc *sc, struct ie_tdr_cmd *cmd)
{
int result;
cmd->com.ie_cmd_status = SWAP(0);
cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST;
cmd->com.ie_cmd_link = SWAP(0xffff);
sc->scb->ie_command_list = vtop16sw(sc, cmd);
cmd->ie_tdr_time = SWAP(0);
if (cmd_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) ||
(cmd->com.ie_cmd_status & IE_STAT_OK) == 0)
result = 0x10000;
else
result = cmd->ie_tdr_time;
ie_ack(sc, IE_ST_WHENCE);
if (result & IE_TDR_SUCCESS)
return;
if (result & 0x10000) {
printf("%s: TDR command failed\n", device_xname(sc->sc_dev));
} else if (result & IE_TDR_XCVR) {
printf("%s: transceiver problem\n", device_xname(sc->sc_dev));
} else if (result & IE_TDR_OPEN) {
printf("%s: TDR detected an open %d clocks away\n",
device_xname(sc->sc_dev), SWAP(result & IE_TDR_TIME));
} else if (result & IE_TDR_SHORT) {
printf("%s: TDR detected a short %d clocks away\n",
device_xname(sc->sc_dev), SWAP(result & IE_TDR_TIME));
} else {
printf("%s: TDR returned unknown status 0x%x\n",
device_xname(sc->sc_dev), result);
}
}
static void
iememinit(struct ie_softc *sc)
{
uint8_t *ptr;
int i;
uint16_t nxt;
ptr = sc->buf_area;
(sc->sc_memset)(ptr, 0, sc->buf_area_sz);
for (i = 0; i < NTXBUF; i++) {
sc->xmit_cbuffs[i] = ptr;
ptr += IE_TBUF_SIZE;
}
for (i = 0; i < sc->nrxbuf; i++) {
sc->cbuffs[i] = ptr;
ptr += IE_RBUF_SIZE;
}
ptr += 16;
for (i = 0; i < NTXBUF; i++) {
sc->xmit_buffs[i] = (volatile void *)ptr;
ptr = Align(ptr + sizeof(*sc->xmit_buffs[i]));
sc->xmit_buffs[i]->ie_xmit_buf =
Swap32(vtop24(sc, sc->xmit_cbuffs[i]));
sc->xmit_buffs[i]->ie_xmit_next = SWAP(0xffff);
}
for (i = 0; i < sc->nrxbuf; i++) {
sc->rbuffs[i] = (volatile void *)ptr;
ptr = Align(ptr + sizeof(*sc->rbuffs[i]));
sc->rbuffs[i]->ie_rbd_buffer =
Swap32(vtop24(sc, sc->cbuffs[i]));
sc->rbuffs[i]->ie_rbd_length = SWAP(IE_RBUF_SIZE);
}
i = sc->nrxbuf - 1;
sc->rbuffs[i]->ie_rbd_length |= IE_RBD_LAST;
nxt = vtop16sw(sc, __UNVOLATILE(sc->rbuffs[0]));
do {
sc->rbuffs[i]->ie_rbd_next = nxt;
nxt = vtop16sw(sc, __UNVOLATILE(sc->rbuffs[i]));
} while (--i >= 0);
for (i = 0; i < NTXBUF; i++) {
sc->xmit_cmds[i] = (volatile void *)ptr;
ptr = Align(ptr + sizeof(*sc->xmit_cmds[i]));
sc->xmit_cmds[i]->com.ie_cmd_link = SWAP(0xffff);
}
for (i = 0; i < sc->nframes; i++) {
sc->rframes[i] = (volatile void *)ptr;
ptr = Align(ptr + sizeof(*sc->rframes[i]));
}
i = sc->nframes - 1;
sc->rframes[i]->ie_fd_last |= IE_FD_LAST;
nxt = vtop16sw(sc, __UNVOLATILE(sc->rframes[0]));
do {
sc->rframes[i]->ie_fd_next = nxt;
nxt = vtop16sw(sc, __UNVOLATILE(sc->rframes[i]));
} while (--i >= 0);
sc->xchead = sc->xctail = 0;
sc->xmit_busy = 0;
sc->rfhead = 0;
sc->rftail = sc->nframes - 1;
sc->rbhead = 0;
sc->rbtail = sc->nrxbuf - 1;
sc->scb->ie_recv_list =
vtop16sw(sc, __UNVOLATILE(sc->rframes[0]));
sc->rframes[0]->ie_fd_buf_desc =
vtop16sw(sc, __UNVOLATILE(sc->rbuffs[0]));
i = (ptr - sc->buf_area);
#ifdef IEDEBUG
printf("IE_DEBUG: used %d of %d bytes\n", i, sc->buf_area_sz);
#endif
if (i > sc->buf_area_sz)
panic("ie: iememinit, out of space");
}
static int
mc_setup(struct ie_softc *sc, void *ptr)
{
struct ie_mcast_cmd *cmd = ptr;
cmd->com.ie_cmd_status = SWAP(0);
cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST;
cmd->com.ie_cmd_link = SWAP(0xffff);
(sc->sc_memcpy)((void *)cmd->ie_mcast_addrs,
(void *)sc->mcast_addrs,
sc->mcast_count * sizeof *sc->mcast_addrs);
cmd->ie_mcast_bytes =
SWAP(sc->mcast_count * ETHER_ADDR_LEN);
sc->scb->ie_command_list = vtop16sw(sc, cmd);
if (cmd_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) ||
(cmd->com.ie_cmd_status & IE_STAT_OK) == 0) {
printf("%s: multicast address setup command failed\n",
device_xname(sc->sc_dev));
return 0;
}
return 1;
}
static inline void
ie_setup_config(struct ie_config_cmd *cmd, int promiscuous, int manchester)
{
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 = promiscuous | manchester << 2;
cmd->ie_crs_cdt = 0;
cmd->ie_min_len = 64;
cmd->ie_junk = 0xff;
}
static int
ieinit(struct ie_softc *sc)
{
volatile struct ie_sys_ctl_block *scb = sc->scb;
void *ptr;
struct ifnet *ifp;
ifp = &sc->sc_if;
ptr = sc->buf_area;
{
struct ie_config_cmd *cmd = ptr;
scb->ie_command_list = vtop16sw(sc, cmd);
cmd->com.ie_cmd_status = SWAP(0);
cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST;
cmd->com.ie_cmd_link = SWAP(0xffff);
ie_setup_config(cmd, (sc->promisc != 0), 0);
if (cmd_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) ||
(cmd->com.ie_cmd_status & IE_STAT_OK) == 0) {
printf("%s: configure command failed\n",
device_xname(sc->sc_dev));
return 0;
}
}
{
struct ie_iasetup_cmd *cmd = ptr;
scb->ie_command_list = vtop16sw(sc, cmd);
cmd->com.ie_cmd_status = SWAP(0);
cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST;
cmd->com.ie_cmd_link = SWAP(0xffff);
(sc->sc_memcpy)((void *)&cmd->ie_address,
CLLADDR(ifp->if_sadl), sizeof(cmd->ie_address));
if (cmd_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) ||
(cmd->com.ie_cmd_status & IE_STAT_OK) == 0) {
printf("%s: individual address setup command failed\n",
device_xname(sc->sc_dev));
return 0;
}
}
if (ie_run_tdr)
run_tdr(sc, ptr);
ie_ack(sc, IE_ST_WHENCE);
iememinit(sc);
ifp->if_flags |= IFF_RUNNING;
sc->scb->ie_recv_list =
vtop16sw(sc, __UNVOLATILE(sc->rframes[0]));
cmd_and_wait(sc, IE_RU_START, 0, 0);
ie_ack(sc, IE_ST_WHENCE);
if (sc->run_586)
(sc->run_586)(sc);
return 0;
}
static void
iestop(struct ie_softc *sc)
{
cmd_and_wait(sc, IE_RU_DISABLE, 0, 0);
}
static int
ieioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct ie_softc *sc = ifp->if_softc;
struct ifaddr *ifa = (struct ifaddr *)data;
int s, 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;
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
sc->promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
case IFF_RUNNING:
iestop(sc);
ifp->if_flags &= ~IFF_RUNNING;
break;
case IFF_UP:
ieinit(sc);
break;
default:
iestop(sc);
ieinit(sc);
break;
}
#ifdef IEDEBUG
if (ifp->if_flags & IFF_DEBUG)
sc->sc_debug = IED_ALL;
else
sc->sc_debug = ie_debug_flags;
#endif
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
mc_reset(sc);
error = 0;
}
break;
default:
error = ether_ioctl(ifp, cmd, data);
break;
}
splx(s);
return error;
}
static void
mc_reset(struct ie_softc *sc)
{
struct ethercom *ec = &sc->sc_ethercom;
struct ether_multi *enm;
struct ether_multistep step;
struct ifnet *ifp;
ifp = &sc->sc_if;
sc->mcast_count = 0;
ETHER_FIRST_MULTI(step, ec, enm);
while (enm) {
if (sc->mcast_count >= MAXMCAST ||
ether_cmp(enm->enm_addrlo, enm->enm_addrhi) != 0) {
ifp->if_flags |= IFF_ALLMULTI;
ieioctl(ifp, SIOCSIFFLAGS, NULL);
goto setflag;
}
memcpy(&sc->mcast_addrs[sc->mcast_count], enm->enm_addrlo,
ETHER_ADDR_LEN);
sc->mcast_count++;
ETHER_NEXT_MULTI(step, enm);
}
setflag:
ETHER_UNLOCK(ec);
sc->want_mcsetup = 1;
}
#ifdef IEDEBUG
void
print_rbd(volatile struct ie_recv_buf_desc *rbd)
{
printf("RBD at %08lx:\nactual %04x, next %04x, buffer %08x\n"
"length %04x, mbz %04x\n", (u_long)rbd, rbd->ie_rbd_actual,
rbd->ie_rbd_next, rbd->ie_rbd_buffer, rbd->ie_rbd_length,
rbd->mbz);
}
#endif