NetBSD/sys/arch/x68k/dev/if_se.c
1998-07-05 00:51:04 +00:00

1637 lines
38 KiB
C

/* $NetBSD: if_se.c,v 1.5 1998/07/05 00:51:16 jonathan Exp $ */
/*
* Device driver for National Semiconductor DS8390 based ethernet adapters.
*
* Based on original ISA bus driver by David Greenman, 29-April-1993
*
* Copyright (C) 1993, David Greenman. This software may be used, modified,
* copied, distributed, and sold, in both source and binary form provided
* that the above copyright and these terms are retained. Under no
* circumstances is the author responsible for the proper functioning
* of this software, nor does the author assume any responsibility
* for damages incurred with its use.
*
* Adapted for MacBSD by Brad Parker <brad@fcr.com>
*
* Currently supports:
* Cayman Systems GatorCard
*/
#include "opt_inet.h"
#include "se.h"
#if NSE > 0
/* bpfilter included here in case it is needed in future net includes */
#include "bpfilter.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/buf.h> /* XXX */
#include <sys/dkstat.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/netisr.h>
#include <sys/malloc.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif
#include <x68k/dev/if_sereg.h>
#include <x68k/dev/device.h>
#include <x68k/dev/scsireg.h>
#include <x68k/dev/scsivar.h>
/*
* se_softc: per line info and status
*/
struct se_softc {
struct x68k_device *sc_xd;
struct scsi_queue sc_dq;
int sc_flags;
short sc_type; /* ansi scsi type */
short sc_punit; /* physical unit (scsi lun) */
u_char sc_cmd;
char sc_datalen[32]; /* additional data length on some commands */
struct arpcom sc_arpcom; /* ethernet common */
char *type_str; /* pointer to type string */
u_char vendor; /* interface vendor */
u_char type; /* interface type code */
caddr_t smem_start; /* shared memory start address */
caddr_t smem_end; /* shared memory end address */
u_long smem_size; /* total shared memory size */
caddr_t smem_ring; /* start of RX ring-buffer (in smem) */
caddr_t bpf; /* BPF "magic cookie" */
u_char xmit_busy; /* transmitter is busy */
u_char txb_cnt; /* Number of transmit buffers */
u_char txb_next; /* Pointer to next buffer ready to xmit */
u_short txb_next_len; /* next xmit buffer length */
u_char txb_inuse; /* data has been buffered in interface memory */
u_char tx_page_start; /* first page of TX buffer area */
u_char rec_page_start; /* first page of RX ring-buffer */
u_char rec_page_stop; /* last page of RX ring-buffer */
u_char txbuf[1550];
u_char rxbuf[1550];
} se_softc[NSE];
/* softc flags */
#define SEF_ALIVE 0x0001
#define SEF_OPEN 0x0002
#define SEF_WMODE 0x0004
#define SEF_WRTTN 0x0008
#define SEF_CMD 0x0010
#define SEF_LEOT 0x0020
#define SEF_MOVED 0x0040
#define SEF_RESET 0x0080
/* xsense sense key */
#define XSK_NOSENCE 0x0
#define XSK_NOTUSED1 0x1
#define XSK_NOTRDY 0x2
#define XSK_MEDERR 0x3
#define XSK_HRDWERR 0x4
#define XSK_ILLREQ 0x5
#define XSK_UNTATTEN 0x6
#define XSK_DATAPROT 0x7
#define XSK_BLNKCHK 0x8
#define XSK_VENDOR 0x9
#define XSK_CPYABORT 0xa
#define XSK_ABORTCMD 0xb
#define XSK_NOTUSEDC 0xc
#define XSK_VOLOVER 0xd
#define XSK_NOTUSEDE 0xe
#define XSK_REVERVED 0xf
#define EPCLEARTALLIES 0
#define EPSENDETHERPACKET 1
#define EPRECIEVEETHERPACKET 2
#define EPSETETHERADDR 3
#define EPSETMULTICASTADDR 4
#define EPSETPACKETRECEPTMODE 5
#define EPETHERNETRECIEVE 6
int seident __P((register struct se_softc *, register struct x68k_device *));
int sefind __P((struct x68k_device *xd));
void seintr __P((register int unit, int stat));
void se_init __P((struct se_softc *));
int se_reset __P((struct se_softc *));
void se_stop __P((struct se_softc *));
int se_start __P((struct ifnet *));
int se_ioctl __P((struct ifnet *, int, caddr_t));
int se_probe();
void sestart __P((register int unit));
int se_watchdog __P((struct ifnet *));
void sestrategy __P((struct buf *bp));
static int seerror __P((int unit, register struct se_softc *sc, register struct\
x68k_device *am, int stat));
static void sefinish __P((int unit, register struct se_softc *sc, register stru\
ct buf *bp));
void seustart __P((register int unit));
extern int scsireq();
extern void scsistart();
extern void disksort();
static struct scsi_fmt_cdb secmd[NSE];
struct scsi_fmt_sense sesense[NSE];
#if 0
static struct scsi_fmt_cdb se_read_cmd = { 6, CMD_READ };
static struct scsi_fmt_cdb se_write_cmd = { 6, CMD_WRITE };
#endif
static struct scsi_fmt_cdb se_setmcast = {
6,
CMD_SET_MULTICASTADDR, 0, 0, 0, 8, 0
};
static struct scsi_fmt_cdb se_setprm = {
6,
CMD_SET_PACKETRECEPTMODE, 0, 0, 0, 0, 0
};
static struct scsi_fmt_cdb se_sendpkt = {
6,
CMD_SEND_ETHERPACKET, 0, 0, 0, 0, 0
};
static struct scsi_fmt_cdb se_recpkt = {
6,
CMD_RECIEVE_ETHERPACKET, 0x1f, 0xff, 0xff, 0xff, 0
};
u_char mcastaddr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
struct buf setab[NSE];
#define seunit(x) (minor(x) & 3)
#define sepunit(x) ((x) & 7)
#ifdef DEBUG
#define SE_OPEN 0x0001
#define SE_GO 0x0002
#define SE_FMKS 0x0004
#define SE_OPENSTAT 0x0008
#define SE_BRESID 0x0010
#define SE_ODDIO 0x0020
#define SEB_ERROR 0x0040
int sedebug = SE_ODDIO|SE_BRESID|SE_GO|SEB_ERROR;
#endif
struct driver sedriver = {
sefind, "se", (int (*)())0, (int (*)())0,
(int (*)())seintr,
(int (*)())0
};
static inline void se_rint();
static inline void se_xmit();
static inline char *se_ring_copy();
extern int ether_output();
#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1518
#define ETHER_ADDR_LEN 6
#define ETHER_HDR_SIZE 14
char se_name[] = "DP8390 SCSI Ethernet Adapter (Ether+)";
static char zero = 0;
static u_char ones = 0xff;
/*
* XXX These two should be moved to locore, and maybe changed to use shorts
* instead of bytes. The reason for these is that bcopy and bzero use longs,
* which the ethernet cards can't handle.
*/
void
bbcopy (char *src, char *dest, int len)
{
while (len--) {
*dest++ = *src++;
}
}
int
sefind(xd)
register struct x68k_device *xd;
{
register struct se_softc *sc = &se_softc[xd->x68k_unit];
struct buf *bp;
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
int stat;
int s, i;
for (bp = setab; bp < &setab[NSE]; bp++)
bp->b_actb = &bp->b_actf;
/*
* I seem to be interrupted every leap year while in here..
*
* Note: do this *only* if you don't need interrupt driven
* SCSI in here. We currently don't, as all I/O in here
* is done with PIO, but beware..
*/
sc->sc_xd = xd;
sc->sc_punit = sepunit(xd->x68k_flags);
sc->sc_type = seident(sc, xd);
if (sc->sc_type < 0)
return(0);
sc->sc_dq.dq_ctlr = xd->x68k_ctlr;
sc->sc_dq.dq_unit = xd->x68k_unit;
sc->sc_dq.dq_slave = xd->x68k_slave;
sc->sc_dq.dq_driver = &sedriver;
sc->sc_flags = SEF_ALIVE;
/*
* Initialize ifnet structure
*/
/* XXX: se->se_dev.dv_unit, se_cd.cd_name */
sprintf(ifp->if_xname, "%s%d", sedriver.d_name, xd->x68k_unit);
ifp->if_softc = sc;
ifp->if_output = ether_output;
ifp->if_start = se_start;
ifp->if_ioctl = se_ioctl;
ifp->if_watchdog = se_watchdog;
ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST);
/*
* Attach the interface
*/
if_attach(ifp);
ether_ifattach(ifp);
#if NBPFILTER > 0
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
/*
* Print additional info when attached
*/
printf(" address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
stat = scsi_scsi_cmd(xd->x68k_ctlr, xd->x68k_slave, xd->x68k_unit, &se_setmcast,
mcastaddr, 8);
if (stat != 0)
printf("sefind:WARNING!! SET_MULTICAST_ADDR FAILED(stat == %d)\n", stat);
stat = scsi_scsi_cmd(xd->x68k_ctlr, xd->x68k_slave, xd->x68k_unit,
&se_setprm, NULL, 0);
if (stat != 0)
printf("sefind:WARNING!! SET_PACKET_RECEPT_MODE FAILED(stat == %d)\n", stat);
se_setprm.cdb[2] = 0x04;
stat = scsi_scsi_cmd(xd->x68k_ctlr, xd->x68k_slave, xd->x68k_unit,
&se_setprm, NULL, 0);
if (stat != 0)
printf("sefind:WARNING!! SET_PACKET_RECEPT_MODE FAILED(stat == %d)\n", stat);
return(1);
}
void
se_ethernet_read_setup(sc)
register struct se_softc *sc;
{
int unit;
int ctlr, slave;
ctlr = sc->sc_xd->x68k_ctlr;
slave = sc->sc_xd->x68k_slave;
unit = sc->sc_punit;
scsi_scsi_cmd(ctlr, slave, unit, &se_recpkt, sc->rxbuf, 0);
}
int
seident(sc, xd)
register struct se_softc *sc;
register struct x68k_device *xd;
{
int unit;
int ctlr, slave;
int i, stat, inqlen;
char idstr[32];
u_int memsize;
int flags = 0;
char manu[32];
char model[32];
char version[32];
register int tries = 10;
struct etherplus_inquiry {
u_char eaddr[6];
u_char filler0[14];
u_char serialno[12];
u_char filler1[28];
};
struct se_inquiry {
struct scsi_inquiry inqbuf;
struct etherplus_inquiry ep_inquiry;
} se_inqbuf;
static struct scsi_fmt_cdb se_inq = {
6,
CMD_INQUIRY, 0, 0, 0, sizeof(se_inqbuf), 0
};
ctlr = xd->x68k_ctlr;
slave = xd->x68k_slave;
unit = sc->sc_punit;
inqlen = 0x05; /* min */
se_inq.cdb[4] = 0x05;
if (scsi_inquire(ctlr, slave, unit, (struct scsi_inquiry *)&se_inqbuf))
goto failed;
if (se_inqbuf.inqbuf.type != 0x09) /* not communication device */
goto failed;
/* now get additonal info */
inqlen = 0x60; /* Inquiry returns inqbuf.len == 0. (T_T) */
se_inq.cdb[4] = inqlen;
bzero(&se_inqbuf, sizeof(se_inqbuf));
while ((stat = scsi_scsi_cmd(ctlr, slave, unit, &se_inq,
(u_char *)&se_inqbuf, inqlen)) == SCSI_BUSY) {
DELAY(10000);
}
if (stat == 0xff) {
printf("se%d: Cant handle this Ethernet device\n", xd->x68k_unit);
goto failed;
}
if (stat != 0)
goto failed;
if (inqlen >= 32) {
strncpy(manu, se_inqbuf.inqbuf.vendor_id, 8);
for (i = 7; i >= 0; --i)
if (manu[i] != ' ')
break;
manu[i+1] = 0;
strncpy(model, se_inqbuf.inqbuf.product_id, 16);
for (i = 7; i > 0; --i)
if (model[i] != ' ')
break;
model[i+1] = 0;
strncpy(version, se_inqbuf.inqbuf.rev, 4);
for (i = 3; i > 0; --i)
if (version[i] != ' ')
break;
version[i+1] = 0;
printf("se%d: %s %s rev %s,", xd->x68k_unit, manu, model, version);
} else if (inqlen == 5)
/* great it's a stupid device, doesn't know it's know name */
idstr[0] = idstr[8] = '\0';
else
idstr[8] = '\0';
if (bcmp("Ether+", model, 6) == 0) {
memsize = 8192; /* XXX */
sc->type_str = "Ether+";
sc->sc_datalen[CMD_REQUEST_SENSE] = 26;
sc->sc_datalen[CMD_INQUIRY] = 96;
sc->sc_datalen[CMD_MODE_SELECT] = 17;
sc->sc_datalen[CMD_MODE_SENSE] = 17;
sc->txb_cnt = 1; /* XXX */
sc->tx_page_start = 0;
for (i = 0; i < ETHER_ADDR_LEN; ++i)
sc->sc_arpcom.ac_enaddr[i] = se_inqbuf.ep_inquiry.eaddr[i];
} else {
if (idstr[8] == '\0')
printf("se%d: No ID, assuming Archive\n", xd->x68k_unit);
else
printf("se%d: Unsupported Ethernet device\n", xd->x68k_unit);
sc->sc_datalen[CMD_REQUEST_SENSE] = 8;
sc->sc_datalen[CMD_INQUIRY] = 5;
sc->sc_datalen[CMD_MODE_SELECT] = 12;
sc->sc_datalen[CMD_MODE_SENSE] = 12;
}
return(se_inqbuf.inqbuf.type);
failed:
return(-1);
}
void
sestrategy(bp)
register struct buf *bp;
{
register int unit = seunit(bp->b_dev);
register struct buf *dp = &setab[unit];
int s;
printf("sestrategy()\n");
#if 0
bp->b_actf = NULL;
s = splbio();
bp->b_actb = dp->b_actb;
*dp->b_actb = bp;
dp->b_actb = &bp->b_actf;
#else
s = splbio();
disksort(dp, bp);
#endif
if (dp->b_active == 0) {
dp->b_active = 1;
seustart(unit);
}
splx(s);
}
void
seustart(unit)
register int unit;
{
register struct se_softc *sc = &se_softc[unit];
register struct scsi_queue *dq = &sc->sc_dq;
register struct buf *bp = setab[unit].b_actf;
register struct scsi_fmt_cdb *cmd = &secmd[sc->sc_punit];
long nblks;
printf("seustart(%d)\n", unit);
if (bp->b_flags & B_READ)
sc->sc_flags &= ~SEF_WRTTN;
else
sc->sc_flags |= SEF_WRTTN;
nblks = bp->b_bcount >> DEV_BSHIFT;
dq->dq_cdb = cmd;
dq->dq_bp = bp;
dq->dq_flags = DQ_DISCONNECT; /* SCSI Disconnect */
if (scsireq(dq)) {
sestart(unit);
}
else {
printf("seustart: scsireq() returns 0.\n");
}
}
/*
* Return:
* 0 if not really an error
* <0 if we should do a retry
* >0 if a fatal error
*/
seerror(unit, sc, am, stat)
int unit, stat;
struct se_softc *sc;
register struct x68k_device *am;
{
int cond = 1;
sesense[unit].status = stat;
/* sexsense must have been called before seerror() */
if (stat & STS_CHECKCOND) {
struct scsi_xsense *sp;
scsi_request_sense(am->x68k_ctlr, am->x68k_slave,
sc->sc_punit, sesense[unit].sense,
8/*sizeof(sesense[unit].sense)*/);
sp = (struct scsi_xsense *)sesense[unit].sense;
/* prtkey(unit, sc); */
printf("se%d: scsi sense class %d, code %d", unit,
sp->class, sp->code);
if (sp->class == 7) {
printf(", key %d", sp->key);
if (sp->valid)
printf(", blk %d", *(int *)&sp->info1);
switch (sp->key) {
/* no sense, try again */
case 0:
cond = -1;
break;
/* recovered error, not a problem */
case 1:
cond = 0;
break;
/* else, unrecovered error */
}
}
printf("\n");
}
return(cond);
}
void
sestart(unit)
register int unit;
{
struct x68k_device *am = se_softc[unit].sc_xd;
printf("sestart(%d)\n", unit);
if (am->x68k_dk >= 0) {
dk_busy |= 1 << am->x68k_dk;
}
scsistart(am->x68k_ctlr);
}
sexsense(ctlr, slave, unit, sc)
int ctlr, slave, unit;
struct se_softc *sc;
{
struct scsi_xsense *sensebuf;
unsigned len;
int s;
len = sc->sc_datalen[CMD_REQUEST_SENSE];
scsi_request_sense(ctlr, slave, unit, sesense[unit].sense, len);
}
void
sefinish(unit, sc, bp)
int unit;
register struct se_softc *sc;
register struct buf *bp;
{
struct buf *dp;
printf("sefinish(%d)\n", unit);
#if 0
setab[unit].b_errcnt = 0;
if (dp = bp->b_actf)
dp->b_actb = bp->b_actb;
else
setab[unit].b_actb = bp->b_actb;
*bp->b_actb = dp;
#else
setab[unit].b_errcnt = 0;
setab[unit].b_actf = bp->b_actf;
#endif
bp->b_resid = 0;
biodone(bp);
scsifree(&sc->sc_dq);
if (setab[unit].b_actf)
seustart(unit);
else
setab[unit].b_active = 0;
}
/*
* Reset interface.
*/
int
se_reset(sc)
struct se_softc *sc;
{
int s;
printf("se_reset()\n");
sc->sc_flags |= SEF_RESET;
/*
* Stop interface and re-initialize.
*/
se_stop(sc);
se_init(sc);
}
/*
* Take interface offline.
*/
void
se_stop(sc)
struct se_softc *sc;
{
int n = 5000;
printf("se_stop()\n");
/*
* Stop everything on the interface, and select page 0 registers.
*/
/*
* Wait for interface to enter stopped state, but limit # of checks
* to 'n' (about 5ms). It shouldn't even take 5us on modern
* DS8390's, but just in case it's an old one.
*/
}
/*
* Device timeout/watchdog routine. Entered if the device neglects to
* generate an interrupt after a transmit has been started on it.
*/
int
se_watchdog(ifp)
struct ifnet *ifp;
{
struct se_softc *sc = ifp->if_softc;
log(LOG_ERR, "se%d: device timeout\n", unit);
++sc->sc_arpcom.ac_if.if_oerrors;
se_reset(sc);
}
/*
* Initialize device.
*/
void
se_init(sc)
struct se_softc *sc;
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
int i, s;
u_char command;
struct x68k_device *xd = sc->sc_xd;
int unit = ifp->if_unit;
register struct buf *bp = setab[xd->x68k_unit].b_actf;
int ctlr, slave;
int stat;
printf("se_init()\n");
#if 0 /* NetBSD 1.1 ??? */
/* address not known */
if (ifp->if_addrlist == (struct ifaddr *)0)
return;
#endif
/* sefinish(unit, sc, bp);*/
/*
* Initialize the NIC in the exact order outlined in the NS manual.
* This init procedure is "mandatory"...don't change what or when
* things happen.
*/
/* Reset transmitter flags. */
sc->xmit_busy = 0;
sc->sc_arpcom.ac_if.if_timer = 0;
sc->txb_inuse = 0;
sc->txb_next = 0;
/*
* Set 'running' flag, and clear output active flag.
*/
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
sc->sc_flags &= ~SEF_RESET;
/*
* ...and attempt to start output
*/
se_start(ifp);
}
/*
* This routine actually starts the transmission on the interface
*/
static inline void se_xmit(ifp)
struct ifnet *ifp;
{
struct se_softc *sc = ifp->if_softc;
u_short len = sc->txb_next_len;
register struct buf *bp = malloc(sizeof *bp, M_DEVBUF, M_WAITOK);
register struct scsi_fmt_cdb *cmd = &secmd[sc->sc_punit];
struct x68k_device *xd = sc->sc_xd;
int stat;
int s;
printf("se_xmit()\n");
/*
* Set NIC for page 0 register access
*/
/*
* Set TX buffer start page
*/
sc->xmit_busy = 1;
sc->sc_flags |= SEF_CMD;
cmd->len = 6; /* all Ether+ commands are cdb6 */
cmd->cdb[0] = CMD_SEND_ETHERPACKET;
cmd->cdb[1] = 0x00; /* logical blocks */
cmd->cdb[2] = 0x00;
cmd->cdb[3] = len >> 8;
cmd->cdb[4] = len & 0xff;
cmd->cdb[5] = 0x00;
sc->sc_cmd = cmd->cdb[0];
bp->b_flags = B_BUSY|B_WRITE;
bp->b_dev = sc->sc_punit; /* XXX */
bp->b_data = sc->txbuf;
bp->b_bcount = len;
bp->b_resid = len;
bp->b_blkno = 0;
bp->b_error = 0;
ifp->if_timer = 4;
sestrategy(bp);
printf("se_xmit: waiting...\n");
biowait(bp);
printf("se_xmit: returned...\n");
}
/*
* This routine actually starts the transmission on the interface
*/
static inline void se_rxmit(unit)
int unit;
{
struct se_softc *sc = &se_softc[unit];
register struct buf *bp = malloc(sizeof *bp, M_DEVBUF, M_WAITOK);
register struct scsi_fmt_cdb *cmd = &secmd[sc->sc_punit];
int stat;
int s;
printf("se_rxmit(%d)\n", unit);
/*
* Set NIC for page 0 register access
*/
/*
* Set TX buffer start page
*/
sc->xmit_busy = 1;
sc->sc_flags |= SEF_CMD;
cmd->len = 6; /* all Ether+ commands are cdb6 */
cmd->cdb[0] = CMD_RECIEVE_ETHERPACKET;
cmd->cdb[1] = sc->sc_punit << 5; /* logical blocks and timeout*/
cmd->cdb[2] = 0x00;
cmd->cdb[3] = 0x06;
cmd->cdb[4] = 0x0f;
cmd->cdb[5] = 0x00;
sc->sc_cmd = cmd->cdb[0];
bzero(sc->rxbuf, (size_t)4);
#if 0
s = splbio();
while (bp->b_flags & B_BUSY) {
if (bp->b_flags & B_DONE)
break;
bp->b_flags |= B_WANTED;
sleep((caddr_t)bp, PRIBIO);
}
splx(s);
#endif
bp->b_flags = B_BUSY|B_READ;
bp->b_dev = sc->sc_punit; /* XXX */
bp->b_data = sc->rxbuf;
bp->b_bcount = 1551;
bp->b_resid = 1551;
bp->b_blkno = 0;
bp->b_error = 0;
sestrategy(bp);
sc->xmit_busy = 0;
}
/*
* Start output on interface.
* We make two assumptions here:
* 1) that the current priority is set to splnet _before_ this code
* is called *and* is returned to the appropriate priority after
* return
* 2) that the IFF_OACTIVE flag is checked before this code is called
* (i.e. that the output part of the interface is idle)
*/
int
se_start(ifp)
struct ifnet *ifp;
{
struct se_softc *sc = if->if_softc;
int unit;
int ctlr, slave;
struct mbuf *m0, *m;
caddr_t buffer;
int len;
u_short pktlen;
int s;
printf("se_start()\n");
if (sc->sc_flags & SEF_RESET) {
printf("reset operation is now in progress.\n");
return;
}
/* Don't transmit if interface is busy or not running */
if ((sc->sc_arpcom.ac_if.if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
return 0;
ctlr = sc->sc_xd->x68k_ctlr;
slave = sc->sc_xd->x68k_slave;
unit = sc->sc_punit;
outloop:
#if 0
pktlen = sc->rxbuf[2] | (sc->rxbuf[3] << 8);
if (pktlen > 4) {
/*
* The byte count includes the FCS - Frame Check Sequence (a
* 32 bit CRC).
*/
if ((pktlen >= ETHER_MIN_LEN) && (pktlen <= ETHER_MAX_LEN)) {
/*
* Go get packet. pktlen - 4 removes CRC from length.
* (packet_ptr + 1) points to data just after the packet ring
* header (+4 bytes)
*/
se_get_packet(sc, (caddr_t)(sc->rxbuf), pktlen - 4);
++sc->sc_arpcom.ac_if.if_ipackets;
} else {
/*
* Really BAD...probably indicates that the ring pointers
* are corrupted. Also seen on early rev chips under
* high load - the byte order of the length gets switched.
*/
log(LOG_ERR,
"se%d: shared memory corrupt - invalid packet length %d\n",
unit, pktlen);
se_reset(sc);
return;
}
}
printf("get finished\n");
#endif
/*
* See if there are buffered packets and an idle transmitter -
* should never happen at this point.
*/
printf("sc->txb_inuse == %d\n", sc->txb_inuse);
if (sc->txb_inuse)
if (sc->xmit_busy == 0) {
printf("%s: packets buffers, but transmitter idle\n",
ifp->if_xname);
se_xmit(ifp);
} else {
/* See if there is room to put another packet in the buffer. */
printf("se_start: no room.\n");
/* No room. Indicate this to the outside world and exit. */
ifp->if_flags |= IFF_OACTIVE;
return;
}
IF_DEQUEUE(&sc->sc_arpcom.ac_if.if_snd, m);
if (m == 0) {
/*
* The following isn't pretty; we are using the !OACTIVE flag to
* indicate to the outside world that we can accept an additional
* packet rather than that the transmitter is _actually_
* active. Indeed, the transmitter may be active, but if we haven't
* filled the secondary buffer with data then we still want to
* accept more.
* Note that it isn't necessary to test the txb_inuse flag -
* we wouldn't have tried to de-queue the packet in the first place
* if it was set.
*/
printf("se_start: no packet?\n");
ifp->if_flags &= ~IFF_OACTIVE;
return;
}
/*
* Copy the mbuf chain into the transmit buffer
*/
m0 = m;
buffer = sc->txbuf;
for (len = 0; m != 0; m = m->m_next) {
bbcopy(mtod(m, caddr_t), buffer, m->m_len);
buffer += m->m_len;
len += m->m_len;
}
sc->txb_next_len = max(len, ETHER_MIN_LEN);
printf("se_start: next length == %d\n", sc->txb_next_len);
sc->txb_inuse++;
if (sc->xmit_busy == 0){
printf("se_start: no busy.\n");
se_xmit(ifp);
} else
printf("se_start: busy.\n");
/*
* If there is BPF support in the configuration, tap off here.
* The following has support for converting trailer packets
* back to normal.
*/
#if NBPFILTER > 0
if (sc->bpf) {
u_short etype;
int off, datasize, resid;
struct ether_header *eh;
struct trailer_header {
u_short ether_type;
u_short ether_residual;
} trailer_header;
char ether_packet[ETHER_MAX_LEN];
char *ep;
ep = ether_packet;
/*
* We handle trailers below:
* Copy ether header first, then residual data,
* then data. Put all this in a temporary buffer
* 'ether_packet' and send off to bpf. Since the
* system has generated this packet, we assume
* that all of the offsets in the packet are
* correct; if they're not, the system will almost
* certainly crash in m_copydata.
* We make no assumptions about how the data is
* arranged in the mbuf chain (i.e. how much
* data is in each mbuf, if mbuf clusters are
* used, etc.), which is why we use m_copydata
* to get the ether header rather than assume
* that this is located in the first mbuf.
*/
/* copy ether header */
m_copydata(m0, 0, sizeof(struct ether_header), ep);
eh = (struct ether_header *) ep;
ep += sizeof(struct ether_header);
etype = ntohs(eh->ether_type);
if (etype >= ETHERTYPE_TRAIL &&
etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
datasize = ((etype - ETHERTYPE_TRAIL) << 9);
off = datasize + sizeof(struct ether_header);
/* copy trailer_header into a data structure */
m_copydata(m0, off, sizeof(struct trailer_header),
&trailer_header.ether_type);
/* copy residual data */
m_copydata(m0, off+sizeof(struct trailer_header),
resid = ntohs(trailer_header.ether_residual) -
sizeof(struct trailer_header), ep);
ep += resid;
/* copy data */
m_copydata(m0, sizeof(struct ether_header),
datasize, ep);
ep += datasize;
/* restore original ether packet type */
eh->ether_type = trailer_header.ether_type;
bpf_tap(sc->bpf, ether_packet, ep - ether_packet);
} else
bpf_mtap(sc->bpf, m0);
}
#endif
printf("put finished\n");
m_freem(m0);
ifp->if_flags &= ~IFF_OACTIVE;
}
/*
* Ethernet interface receiver interrupt.
*/
static inline void
se_rint(unit)
int unit;
{
register struct se_softc *sc = &se_softc[unit];
u_char boundry, current;
struct se_ring *packet_ptr;
/* MOVED to se_start() */
}
/*
* Ethernet interface interrupt processor
*/
void
seintr(unit, stat)
register int unit;
int stat;
{
struct se_softc *sc = &se_softc[unit];
register struct scsi_queue *dq = &sc->sc_dq;
register struct scsi_xsense *xp = (struct scsi_xsense *)sesense[unit].sense;
register struct buf *bp = setab[unit].b_actf;
struct x68k_device *am = sc->sc_xd;
int s;
u_char rxmit_flag = 0;
u_char isr;
int cond;
printf("seintr(%d,%d)\n", unit, stat);
/*
* clear watchdog timer
*/
sc->sc_arpcom.ac_if.if_timer = 0;
#ifdef DIAGNOSTIC
if (bp == NULL) {
printf("se%d: bp == NULL\n", unit);
return;
}
#endif
if (stat == SCSI_IO_TIMEOUT) {
printf("se%d: timeout error\n", unit);
}
if (am->x68k_dk >= 0)
dk_busy &=~ (1 << am->x68k_dk);
if (stat != 0) {
if (stat > 0) {
#ifdef DEBUG
if (sedebug & SEB_ERROR)
printf("se%d: seintr: bad scsi status 0x%x\n",
unit, stat);
#endif
cond = seerror(unit, sc, am, stat);
}
bp->b_flags |= B_ERROR;
bp->b_error = EIO;
} else {
/* scsi command completed ok */
printf("seintr: cdb0 = %d\n", dq->dq_cdb->cdb[0]);
if(setab[unit].b_active == 1) {
if ((dq->dq_cdb->cdb[0]) == 5) {
printf("seintr: sent\n");
++sc->sc_arpcom.ac_if.if_opackets;
sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
--sc->txb_inuse;
bp->b_resid = 0;
/*
* Switch buffers if we are doing double-buffered transmits
*/
if ((sc->txb_next == 0) && (sc->txb_cnt > 1))
sc->txb_next = 1;
else
sc->txb_next = 0;
/*
* Set a timer just in case we never hear from the board again
*/
sc->xmit_busy = 0;
rxmit_flag = 1;
}
}
}
#ifdef DEBUG
if ((sedebug & SE_BRESID) && bp->b_resid != 0)
printf("b_resid %d b_flags 0x%x b_error 0x%x\n",
bp->b_resid, bp->b_flags, bp->b_error);
#endif
/*
* reset tx busy and output active flags
*/
sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
--sc ->txb_inuse;
sefinish(unit, sc, bp);
if (rxmit_flag == 1)
se_rxmit(unit);
}
#ifdef __STDC__
secommand(int unit, u_int command, int cnt)
#else
secommand(unit, command, cnt)
int unit;
u_int command;
int cnt;
#endif
{
register struct se_softc *sc = &se_softc[unit];
register struct buf *bp = malloc(sizeof *bp, M_DEVBUF, M_WAITOK);
register struct scsi_fmt_cdb *cmd = &secmd[unit];
register cmdcnt;
int s;
printf("secommand()\n");
cmd->len = 6; /* all Ether+ commands are cdb6 */
cmd->cdb[1] = sc->sc_punit;
cmd->cdb[2] = cmd->cdb[3] = cmd->cdb[4] = cmd->cdb[5] = 0;
cmdcnt = 0;
/*
* XXX Assumption is that everything except Archive can take
* repeat count in cdb block.
*/
switch (command) {
case EPCLEARTALLIES:
cmd->cdb[0] = CMD_CLEAR_TALLIES;
*(u_char *)(&cmd->cdb[1]) = 0x00;
*(u_char *)(&cmd->cdb[2]) = 0x00;
*(u_char *)(&cmd->cdb[3]) = 0x00;
*(u_char *)(&cmd->cdb[4]) = 0x00;
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPSENDETHERPACKET:
cmd->cdb[0] = CMD_SEND_ETHERPACKET;
cmd->cdb[1] |= 0x00; /* logical blocks */
*(u_char *)(&cmd->cdb[2]) = 0x00;
*(u_char *)(&cmd->cdb[3]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[4]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPRECIEVEETHERPACKET:
cmd->cdb[0] = CMD_RECIEVE_ETHERPACKET;
cmd->cdb[1] |= 0x1f; /* logical blocks and timeout period(msb)*/
*(u_char *)(&cmd->cdb[2]) = 0xff; /* timeout period */
*(u_char *)(&cmd->cdb[3]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[4]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPSETETHERADDR:
cmd->cdb[0] = CMD_SET_ETHERADDR;
cmd->cdb[1] |= 0x00; /* logical blocks */
*(u_char *)(&cmd->cdb[2]) = 0x00;
*(u_char *)(&cmd->cdb[3]) = 0x00;
*(u_char *)(&cmd->cdb[4]) = 0x06;
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPSETMULTICASTADDR:
cmd->cdb[0] = CMD_SET_MULTICASTADDR;
cmd->cdb[1] |= 0x00; /* logical blocks */
*(u_char *)(&cmd->cdb[2]) = 0x00;
*(u_char *)(&cmd->cdb[3]) = 0x00;
*(u_char *)(&cmd->cdb[4]) = 0x08;
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPSETPACKETRECEPTMODE:
cmd->cdb[0] = CMD_SET_PACKETRECEPTMODE;
cmd->cdb[1] |= 0x00; /* logical blocks */
*(u_char *)(&cmd->cdb[2]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[3]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[4]) = 0xff; /* XXX */
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
case EPETHERNETRECIEVE:
cmd->cdb[0] = CMD_ETHERNET_RECIEVE;
cmd->cdb[1] |= 0x00; /* logical blocks */
*(u_char *)(&cmd->cdb[2]) = 0x00;
*(u_char *)(&cmd->cdb[3]) = 0x00;
*(u_char *)(&cmd->cdb[4]) = 0x00;
*(u_char *)(&cmd->cdb[5]) = 0x00;
break;
default:
printf("se%d: secommand bad command 0x%x\n",
unit, command);
}
sc->sc_flags |= SEF_CMD;
sc->sc_cmd = cmd->cdb[0];
again:
#if 0
s = splbio();
while (bp->b_flags & B_BUSY) {
if (bp->b_flags & B_DONE)
break;
bp->b_flags |= B_WANTED;
sleep((caddr_t)bp, PRIBIO);
}
splx(s);
#endif
bp->b_flags = B_BUSY|B_READ;
bp->b_dev = (dev_t)unit; /* XXX */
bp->b_bcount = 0;
bp->b_resid = 0;
bp->b_blkno = 0;
bp->b_error = 0;
sestrategy(bp);
biowait(bp);
if (--cnt > 0)
goto again;
sc->sc_flags &= ~(SEF_CMD|SEF_WRTTN);
}
/*
* Process an ioctl request. This code needs some work - it looks
* pretty ugly.
*/
int
se_ioctl(ifp, command, data)
register struct ifnet *ifp;
int command;
caddr_t data;
{
register struct ifaddr *ifa = (struct ifaddr *)data;
struct se_softc *sc = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *)data;
int s, error = 0;
printf("se_ioctl()\n");
switch (command) {
case SIOCSIFADDR:
printf("se_ioctl:SIOCSIFADDR\n");
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
se_init(sc); /* before arpwhohas */
/*
* See if another station has *our* IP address.
* i.e.: There is an address conflict! If a
* conflict exists, a message is sent to the
* console.
*/
((struct arpcom *)ifp)->ac_ipaddr =
IA_SIN(ifa)->sin_addr;
arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
break;
#endif
#ifdef NS
/*
* XXX - This code is probably wrong
*/
case AF_NS:
{
register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
if (ns_nullhost(*ina))
ina->x_host =
*(union ns_host *)(sc->sc_arpcom.ac_enaddr);
else {
/*
*
*/
bbcopy((caddr_t)ina->x_host.c_host,
(caddr_t)sc->sc_arpcom.ac_enaddr,
sizeof(sc->sc_arpcom.ac_enaddr));
}
/*
* Set new address
*/
se_init(sc);
break;
}
#endif
default:
se_init(sc);
break;
}
break;
case SIOCSIFFLAGS:
printf("se_ioctl:SIOCSIFFLAGS\n");
if (((ifp->if_flags & IFF_UP) == 0) &&
((ifp->if_flags & IFF_RUNNING) != 0)) {
/*
* If interface is marked down and it is running, then
* stop it.
*/
se_stop(sc);
ifp->if_flags &= ~IFF_RUNNING;
} else if ((ifp->if_flags & IFF_UP) != 0 &&
((ifp->if_flags & IFF_RUNNING) == 0)) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
se_init(sc);
} else {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
se_stop(sc);
se_init(sc);
}
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
printf("se_ioctl:SIOC***MULTI\n");
/* Update our multicast list. */
error = (command == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_arpcom) :
ether_delmulti(ifr, &sc->sc_arpcom);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
se_stop(sc); /* XXX for ds_setmcaf? */
se_init(sc);
error = 0;
}
break;
default:
printf("se_ioctl:invalid IOCTL\n");
error = EINVAL;
}
printf("IOCTL finished.\n");
return (error);
}
/*
* Macro to calculate a new address within shared memory when given an offset
* from an address, taking into account ring-wrap.
*/
#define ringoffset(sc, start, off, type) \
((type)( ((caddr_t)(start)+(off) >= (sc)->smem_end) ? \
(((caddr_t)(start)+(off))) - (sc)->smem_end \
+ (sc)->smem_ring: \
((caddr_t)(start)+(off)) ))
/*
* Retreive packet from shared memory and send to the next level up via
* ether_input(). If there is a BPF listener, give a copy to BPF, too.
*/
se_get_packet(sc, buf, len)
struct se_softc *sc;
char *buf;
u_short len;
{
struct ether_header *eh;
struct mbuf *m, *head, *se_ring_to_mbuf();
u_short off;
int resid;
u_short etype;
struct trailer_header {
u_short trail_type;
u_short trail_residual;
} trailer_header;
/* Allocate a header mbuf */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0)
goto bad;
m->m_pkthdr.rcvif = &sc->sc_arpcom.ac_if;
m->m_pkthdr.len = len;
m->m_len = 0;
head = m;
eh = (struct ether_header *)buf;
/* The following sillines is to make NFS happy */
#define EROUND ((sizeof(struct ether_header) + 3) & ~3)
#define EOFF (EROUND - sizeof(struct ether_header))
/*
* The following assumes there is room for
* the ether header in the header mbuf
*/
head->m_data += EOFF;
bbcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header));
buf += sizeof(struct ether_header);
head->m_len += sizeof(struct ether_header);
len -= sizeof(struct ether_header);
etype = ntohs((u_short)eh->ether_type);
/*
* Deal with trailer protocol:
* If trailer protocol, calculate the datasize as 'off',
* which is also the offset to the trailer header.
* Set resid to the amount of packet data following the
* trailer header.
* Finally, copy residual data into mbuf chain.
*/
if (etype >= ETHERTYPE_TRAIL &&
etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
off = (etype - ETHERTYPE_TRAIL) << 9;
if ((off + sizeof(struct trailer_header)) > len)
goto bad; /* insanity */
eh->ether_type = *ringoffset(sc, buf, off, u_short *);
resid = ntohs(*ringoffset(sc, buf, off+2, u_short *));
if ((off + resid) > len) goto bad; /* insanity */
resid -= sizeof(struct trailer_header);
if (resid < 0) goto bad; /* insanity */
m = se_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid);
if (m == 0) goto bad;
len = off;
head->m_pkthdr.len -= 4; /* subtract trailer header */
}
/*
* Pull packet off interface. Or if this was a trailer packet,
* the data portion is appended.
*/
m = se_ring_to_mbuf(sc, buf, m, len);
if (m == 0) goto bad;
#if NBPFILTER > 0
/*
* Check if there's a BPF listener on this interface.
* If so, hand off the raw packet to bpf.
*/
if (sc->bpf) {
bpf_mtap(sc->bpf, head);
/*
* 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.
*
* XXX This test does not support multicasts.
*/
if ((sc->sc_arpcom.ac_if.if_flags & IFF_PROMISC) &&
bcmp(eh->ether_dhost, sc->sc_arpcom.ac_enaddr,
sizeof(eh->ether_dhost)) != 0 &&
bcmp(eh->ether_dhost, etherbroadcastaddr,
sizeof(eh->ether_dhost)) != 0) {
m_freem(head);
return;
}
}
#endif
/*
* Fix up data start offset in mbuf to point past ether header
*/
m_adj(head, sizeof(struct ether_header));
ether_input(&sc->sc_arpcom.ac_if, eh, head);
return;
bad: if (head)
m_freem(head);
return;
}
/*
* Supporting routines
*/
/*
* Given a source and destination address, copy 'amount' of a packet from
* the ring buffer into a linear destination buffer. Takes into account
* ring-wrap.
*/
static inline char *
se_ring_copy(sc,src,dst,amount)
struct se_softc *sc;
char *src;
char *dst;
u_short amount;
{
u_short tmp_amount;
bbcopy(src, dst, amount);
return(src + amount);
}
/*
* Copy data from receive buffer to end of mbuf chain
* allocate additional mbufs as needed. return pointer
* to last mbuf in chain.
* sc = ed info (softc)
* src = pointer in ed ring buffer
* dst = pointer to last mbuf in mbuf chain to copy to
* amount = amount of data to copy
*/
struct mbuf *
se_ring_to_mbuf(sc,src,dst,total_len)
struct se_softc *sc;
char *src;
struct mbuf *dst;
u_short total_len;
{
register struct mbuf *m = dst;
while (total_len) {
register u_short amount = min(total_len, M_TRAILINGSPACE(m));
if (amount == 0) { /* no more data in this mbuf, alloc another */
/*
* If there is enough data for an mbuf cluster, attempt
* to allocate one of those, otherwise, a regular
* mbuf will do.
* Note that a regular mbuf is always required, even if
* we get a cluster - getting a cluster does not
* allocate any mbufs, and one is needed to assign
* the cluster to. The mbuf that has a cluster
* extension can not be used to contain data - only
* the cluster can contain data.
*/
dst = m;
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0)
return (0);
if (total_len >= MINCLSIZE)
MCLGET(m, M_DONTWAIT);
m->m_len = 0;
dst->m_next = m;
amount = min(total_len, M_TRAILINGSPACE(m));
}
src = se_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount);
m->m_len += amount;
total_len -= amount;
}
return (m);
}
/*
* Compute the multicast address filter from the list of multicast addresses we
* need to listen to.
*/
void
se_getmcaf(ac, af)
struct arpcom *ac;
u_long *af;
{
struct ifnet *ifp = &ac->ac_if;
struct ether_multi *enm;
register u_char *cp, c;
register u_long crc;
register int i, len;
struct ether_multistep step;
/*
* 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) {
ifp->if_flags |= IFF_ALLMULTI;
af[0] = af[1] = 0xffffffff;
return;
}
af[0] = af[1] = 0;
ETHER_FIRST_MULTI(step, ac, enm);
while (enm != NULL) {
if (bcmp(enm->enm_addrlo, enm->enm_addrhi,
sizeof(enm->enm_addrlo)) != 0) {
/*
* 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] = 0xffffffff;
return;
}
cp = enm->enm_addrlo;
crc = 0xffffffff;
for (len = sizeof(enm->enm_addrlo); --len >= 0;) {
c = *cp++;
for (i = 8; --i >= 0;) {
if (((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01)) {
crc <<= 1;
crc ^= 0x04c11db6 | 1;
} else
crc <<= 1;
c >>= 1;
}
}
/* Just want the 6 most significant bits. */
crc >>= 26;
/* Turn on the corresponding bit in the filter. */
af[crc >> 5] |= 1 << ((crc & 0x1f) ^ 24);
ETHER_NEXT_MULTI(step, enm);
}
ifp->if_flags &= ~IFF_ALLMULTI;
}
#endif