* Copyright (C) 1990,91 W. Jolitz
* Copyright (c) 1990 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_ne.c 7.1 (Berkeley) %G%
* Parts inspired from Tim Tucker's if_wd driver for the wd8003,
* insight on the ne2000 gained from Robert Clements PC/FTP driver.
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/if_ether.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/if_nereg.h"
#include "i386/isa/icu.h"
int neprobe(), neattach(), neintr();
int nestart(),neinit(), ether_output(), neioctl();
struct isa_driver nedriver
= {
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* ns_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
struct arpcom ns_ac
; /* Ethernet common part */
#define ns_if ns_ac.ac_if /* network-visible interface */
#define ns_addr ns_ac.ac_enaddr /* hardware Ethernet address */
#define DSF_LOCK 1 /* block re-entering enstart */
int ns_ba
; /* byte addr in buffer ram of inc pkt */
int ns_cur
; /* current page being filled */
struct prhdr ns_ph
; /* hardware header of incoming packet*/
struct ether_header ns_eh
; /* header of incoming packet */
u_char ns_pb
[2048 /*ETHERMTU+sizeof(long)*/];
#define ENBUFSIZE (sizeof(struct ether_header) + ETHERMTU + 2 + 64)
register struct ne_softc
*ns
= &ne_softc
[0];
outb(nec
+ds_cmd
, DSCM_STOP
|DSCM_NODMA
);
while ((inb(nec
+ds0_isr
)&DSIS_RESET
) == 0 && i
-- > 0);
/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
outb(nec
+ds0_dcr
, DSDC_WTS
|DSDC_BMS
|DSDC_FT1
);
outb(nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_STOP
);
/* check cmd reg and fail if not right */
if ((i
=inb(nec
+ds_cmd
)) != (DSCM_NODMA
|DSCM_PG0
|DSCM_STOP
))
outb(nec
+ds0_rcr
, DSRC_MON
);
outb(nec
+ds0_pstart
, RBUF
/DS_PGSIZE
);
outb(nec
+ds0_pstop
, RBUFEND
/DS_PGSIZE
);
outb(nec
+ds0_bnry
, RBUFEND
/DS_PGSIZE
);
outb(nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG1
|DSCM_STOP
);
outb(nec
+ds1_curr
, RBUF
/DS_PGSIZE
);
outb(nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_STOP
);
fetchrom (boarddata
, 0, sizeof(boarddata
));
for (i = 0; i < 0xfff0; i+=4) {
if (pat == 0xa55a+i*37) {
if (rom) { rom=0; printf(" %x", i); }
if (!rom) { rom=1; printf("..%x ", i); }
/* extract board address */
for(i
=0; i
< 6; i
++) ns
->ns_addr
[i
] = boarddata
[i
];
fetchrom (up
, ad
, len
) u_short
*up
; {
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_STOP
);
outb (nec
+ds0_isr
, DSIS_RDC
);
outb (nec
+ds0_rbcr0
, len
&0xff);
outb (nec
+ds0_rbcr1
, (len
>>8)&0xff);
outb (nec
+ds0_rsar0
, ad
&0xff);
outb (nec
+ds0_rsar1
, (ad
>>8)&0xff);
outb (nec
+ds_cmd
, DSCM_RREAD
|DSCM_PG0
|DSCM_START
);
insw (nec
+ne_data
, up
, len
/2);
while ((inb (nec
+ds0_isr
) & DSIS_RDC
) == 0) pausestr("fetchrom",0);
outb (nec
+ds0_isr
, DSIS_RDC
);
fetchram (up
, ad
, len
) caddr_t up
; {
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_START
);
outb (nec
+ds0_isr
, DSIS_RDC
);
outb (nec
+ds0_rbcr0
, len
&0xff);
outb (nec
+ds0_rbcr1
, (len
>>8)&0xff);
outb (nec
+ds0_rsar0
, ad
&0xff);
outb (nec
+ds0_rsar1
, (ad
>>8)&0xff);
outb (nec
+ds_cmd
, DSCM_RREAD
|DSCM_PG0
|DSCM_START
);
insw (nec
+ne_data
, up
, len
/2);
while ((inb (nec
+ds0_isr
) & DSIS_RDC
) == 0) pausestr("fetchram",0);
outb (nec
+ds0_isr
, DSIS_RDC
);
putram (up
, ad
, len
) caddr_t up
; {
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_START
);
outb (nec
+ds0_isr
, DSIS_RDC
);
outb (nec
+ds0_rbcr0
, len
&0xff);
outb (nec
+ds0_rbcr1
, (len
>>8)&0xff);
outb (nec
+ds0_rsar0
, ad
&0xff);
outb (nec
+ds0_rsar1
, (ad
>>8)&0xff);
outb (nec
+ds_cmd
, DSCM_RWRITE
|DSCM_PG0
|DSCM_START
);
outsw (nec
+ne_data
, up
, len
/2);
while ((inb (nec
+ds0_isr
) & DSIS_RDC
) == 0)
if(pausestr("putram",0)<0) break;
outb (nec
+ds0_isr
, DSIS_RDC
);
printf("ne%d: reset\n", unit
);
ne_softc
[unit
].ns_flags
&= ~DSF_LOCK
;
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets. We get the ethernet address here.
register struct ne_softc
*ns
= &ne_softc
[unit
];
register struct ifnet
*ifp
= &ns
->ns_if
;
ifp
->if_name
= nedriver
.name
;
printf (" ethernet address %s", ether_sprintf(ns
->ns_addr
)) ;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_NOTRAILERS
;
ifp
->if_output
= ether_output
;
* Initialization of interface; set up initialization block
* and transmit/receive descriptor rings.
register struct ne_softc
*ns
= &ne_softc
[unit
];
struct ifnet
*ifp
= &ns
->ns_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0) return;
if (ifp
->if_flags
& IFF_RUNNING
) return;
/* set physical address on ethernet */
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG1
|DSCM_STOP
);
for (i
=0 ; i
< 6 ; i
++) outb(nec
+ds1_par0
+i
,ns
->ns_addr
[i
]);
/* clr logical address hash filter for now */
for (i
=0 ; i
< 8 ; i
++) outb(nec
+ds1_mar0
+i
,0xff);
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_STOP
);
outb (nec
+ds0_isr
, 0xff);
/* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
outb(nec
+ds0_dcr
, DSDC_WTS
|DSDC_BMS
|DSDC_FT1
);
outb (nec
+ds0_rcr
, DSRC_MON
);
outb(nec
+ds0_pstart
, RBUF
/DS_PGSIZE
);
outb(nec
+ds0_pstop
, RBUFEND
/DS_PGSIZE
);
outb(nec
+ds0_bnry
, RBUF
/DS_PGSIZE
);
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG1
|DSCM_STOP
);
outb(nec
+ds1_curr
, RBUF
/DS_PGSIZE
);
ns
->ns_cur
= RBUF
/DS_PGSIZE
;
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_START
);
outb (nec
+ds0_rcr
, DSRC_AB
);
outb(nec
+ds0_dcr
, DSDC_WTS
|DSDC_BMS
|DSDC_FT1
);
outb (nec
+ds0_imr
, 0xff);
ns
->ns_if
.if_flags
|= IFF_RUNNING
;
ns
->ns_oactive
= 0; ns
->ns_mask
= ~0;
* Setup output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
* called only at splimp or interrupt level.
register struct ne_softc
*ns
= &ne_softc
[ifp
->if_unit
];
* The DS8390 has only one transmit buffer, if it is busy we
* must wait until the transmit interrupt completes.
outb(nec
+ds_cmd
,DSCM_NODMA
|DSCM_START
);
if (ns
->ns_flags
& DSF_LOCK
)
if (inb(nec
+ds_cmd
) & DSCM_TRANS
)
if ((ns
->ns_if
.if_flags
& IFF_RUNNING
) == 0)
IF_DEQUEUE(&ns
->ns_if
.if_snd
, m
);
if ((m
->m_flags
& M_PKTHDR
) == 0)
printf("should panic: no packet header\n");
* Copy the mbuf chain into the transmit buffer
ns
->ns_flags
|= DSF_LOCK
; /* prevent entering nestart */
buffer
= TBUF
; len
= i
= 0;
dprintf(DPAGIN
,"\n before: ");
for (m0
= m
; m
!= 0; m
= m
->m_next
) {
dprintf(DPAGIN
,"%d ", m
->m_len
);
if (m
->m_flags
& M_PKTHDR
&& total
!= m
->m_pkthdr
.len
)
printf("hdr fucked, len %d should be %d\n", total
, m
->m_pkthdr
.len
);
dprintf(DPAGIN
,"\n after: ");
if (m
->m_len
&1 && t
> m
->m_len
) {
dprintf(DPAGIN
|DPAUSE
,"+%d:%d ", m
->m_len
, m
->m_next
->m_len
);
putram(mtod(m
, caddr_t
), buffer
, m
->m_len
- 1);
m
->m_data
+= m
->m_len
- 1;
if(m
== 0) panic("bloowie!");
dprintf(DPAGIN
|DPAUSE
, "\n rewritten as: ");
for (mp
= m
; mp
!= 0; mp
= mp
->m_next
)
dprintf(DPAGIN
,"%d ", mp
->m_len
);
dprintf(DPAGIN
|DPAUSE
,"%d ", m
->m_len
);
putram(mtod(m
, caddr_t
), buffer
, m
->m_len
);
/*for(j=0; i < len;i++,j++) puthex(mtod(m,u_char *)[j]);
dprintf(DPAGIN
|DPAUSE
,"send %d\n", total
);
* Init transmit length registers, and set transmit start flag.
if(len
< 0 || len
> 1536)
pg("T Bogus Length %d\n", len
);
dprintf(DEXPAND
,"snd %d ", len
);
outb(nec
+ds0_tbcr0
,len
&0xff);
outb(nec
+ds0_tbcr1
,(len
>>8)&0xff);
outb(nec
+ds0_tpsr
, TBUF
/DS_PGSIZE
);
outb(nec
+ds_cmd
, DSCM_TRANS
|DSCM_NODMA
|DSCM_START
);
#define succ(n) (((n)+1 >= RBUFEND/DS_PGSIZE) ? RBUF/DS_PGSIZE : (n)+1)
#define pred(n) (((n)-1 < RBUF/DS_PGSIZE) ? RBUFEND/DS_PGSIZE-1 : (n)-1)
register struct ne_softc
*ns
= &ne_softc
[unit
];
/* save cmd, clear interrupt */
dprintf(DEXPAND
,"|ppl %x isr %x ", ppl
, isr
);
outb(nec
+ds_cmd
,DSCM_NODMA
|DSCM_START
);
if (isr
& (/*DSIS_RXE|DSIS_TXE|*/DSIS_ROVRN
))
log(LOG_ERR
, "ne%d: error: isr %x\n", ns
-ne_softc
, isr
/*, DSIS_BITS*/);
outb(nec
+ds_cmd
, DSCM_STOP
|DSCM_NODMA
);
outb(nec
+ds_cmd
, DSCM_STOP
|DSCM_NODMA
|DSCM_PG1
);
pend
= inb(nec
+ds1_curr
);
outb(nec
+ds_cmd
, DSCM_STOP
|DSCM_NODMA
|DSCM_PG0
);
lastfree
= inb(nec
+ds0_bnry
);
printf("Cur %x pend %x lastfree %x ", ns
->ns_cur
, pend
, lastfree
);
if (lastfree
>= RBUFEND
/DS_PGSIZE
)
lastfree
= RBUF
/DS_PGSIZE
;
/* something in the buffer? */
if (pend
!= succ(lastfree
)) {
fetchram(&ns
->ns_ph
,ns
->ns_cur
*DS_PGSIZE
, sizeof(ns
->ns_ph
));
ns
->ns_ba
= ns
->ns_cur
*DS_PGSIZE
+sizeof(ns
->ns_ph
);
if (ns
->ns_ph
.pr_status
& DSRS_RPC
)
nxt
= ns
->ns_ph
.pr_nxtpg
;
if ( nxt
>= RBUF
/DS_PGSIZE
&& nxt
<= RBUFEND
/DS_PGSIZE
&& nxt
<= pend
)
else ns
->ns_cur
= nxt
= pend
;
outb(nec
+ds0_bnry
, lastfree
);
} else ns
->ns_cur
= pend
;
outb(nec
+ds0_tcr
,DSTC_LB0
);
outb(nec
+ds0_rcr
, DSRC_MON
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
);
outb (nec
+ds0_rcr
, DSRC_AB
);
/* need to read these registers to clear status */
(void) inb(nec
+ ds0_rsr
);
if (isr
& (DSIS_RX
|DSIS_RXE
|DSIS_ROVRN
)) {
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
|DSCM_PG1
);
pend
= inb(nec
+ds1_curr
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
|DSCM_PG0
);
lastfree
= inb(nec
+ds0_bnry
);
dprintf(DEXPAND
,"cur %x pnd %x lfr %x ", ns
->ns_cur
, pend
, lastfree
);
if (lastfree
>= RBUFEND
/DS_PGSIZE
)
lastfree
= RBUF
/DS_PGSIZE
;
if (pend
< lastfree
&& ns
->ns_cur
< pend
)
else if (ns
->ns_cur
> lastfree
)
/* something in the buffer? */
while (pend
!= lastfree
) {
fetchram(&ns
->ns_ph
,lastfree
*DS_PGSIZE
,
ns
->ns_ba
= lastfree
*DS_PGSIZE
+sizeof(ns
->ns_ph
);
if (ns
->ns_ph
.pr_status
== DSRS_RPC
||
ns
->ns_ph
.pr_status
== 0x21)
printf("cur %x pnd %x lfr %x ", ns
->ns_cur
, pend
, lastfree
);
printf("nxt %x len %x ", ns
->ns_ph
.pr_nxtpg
, (ns
->ns_ph
.pr_sz1
<<8)+
pg("Bogus Sts %x ", ns
->ns_ph
.pr_status
);
nxt
= ns
->ns_ph
.pr_nxtpg
;
dprintf(DEXPAND
,"nxt %x ", nxt
);
if ( nxt
>= RBUF
/DS_PGSIZE
&& nxt
<= RBUFEND
/DS_PGSIZE
&& nxt
<= pend
)
else ns
->ns_cur
= nxt
= pend
;
outb(nec
+ds0_bnry
, pred(nxt
));
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
|DSCM_PG1
);
pend
= inb(nec
+ds1_curr
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
|DSCM_PG0
);
} /*else ns->ns_cur = pend;*/
dprintf(DEXPAND
,"cur %x pnd %x lfR %x ", ns
->ns_cur
, pend
, lastfree
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
);
ns
->ns_flags
&= ~DSF_LOCK
;
dprintf(DEXPAND
," clsn");
/* need to read these registers to clear status */
ns
->ns_if
.if_collisions
+= inb(nec
+ds0_tbcr0
);
ns
->ns_flags
&= ~DSF_LOCK
;
ns
->ns_if
.if_collisions
+= inb(nec
+ds0_tbcr0
);
outb(nec
+ds0_tcr
, DSTC_LB0
);
outb(nec
+ds0_rcr
, DSRC_MON
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
);
outb(nec
+ds0_rcr
, DSRC_AB
);
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_START
);
outb (nec
+ds0_imr
, 0xff);
if(++cnt
% 10 == 0) dprintf(DEXPAND
,"\n");
* Ethernet interface receiver interface.
* If input error just drop packet.
* Otherwise examine packet to determine type. If can't determine length
* from type, then have to drop packet. Othewise decapsulate
* packet based on type and pass to type specific higher-level
register struct ne_softc
*ns
;
len
= ns
->ns_ph
.pr_sz0
+ (ns
->ns_ph
.pr_sz1
<<8);
if(len
< 60 || len
> 1536) {
pg(DEXPAND
,"R Bogus Length %d", len
);
fetchram(ns
->ns_pb
,ns
->ns_ba
,min(len
,DS_PGSIZE
-sizeof(ns
->ns_ph
)));
if (!bcmp((caddr_t
)ns
->ns_pb
, (caddr_t
)ns
->ns_addr
, 6)
&& !bcmp((caddr_t
)ns
->ns_pb
, (caddr_t
)etherbroadcastaddr
, 6)) {
printf("G%x ", ns
->ns_cur
);
printf("P%x ", ns->ns_cur);*/
if(len
> DS_PGSIZE
-sizeof(ns
->ns_ph
)) {
int l
= len
- (DS_PGSIZE
-sizeof(ns
->ns_ph
)), b
;
u_char
*p
= ns
->ns_pb
+ (DS_PGSIZE
-sizeof(ns
->ns_ph
));
dprintf(DEXPAND
,"len %d(%d|", len
, p
- ns
->ns_pb
);
if(++ns
->ns_cur
> 0x7f) ns
->ns_cur
= 0x46;
b
= ns
->ns_cur
*DS_PGSIZE
;
p
+= DS_PGSIZE
; l
-= DS_PGSIZE
;
if(++ns
->ns_cur
> 0x7f) ns
->ns_cur
= 0x46;
b
= ns
->ns_cur
*DS_PGSIZE
;
dprintf(DEXPAND
,"%d|", p
- ns
->ns_pb
);
dprintf(DEXPAND
,"%d) ", l
);
sizeof(struct ether_header
)
+ sizeof(long); /* don't forget checksum! */
neread(ns
,(caddr_t
)(ns
->ns_pb
), len
);
if(n
) { downcnt
= 0xffff; return(0); }
if(--downcnt
> 0) return(0);
pg(" <%s> recur %d sts %x ", s
, recur
, inb (nec
+ds0_isr
));
* Pass a packet to the higher levels.
* We deal with the trailer protocol here.
register struct ne_softc
*ns
;
register struct ether_header
*eh
;
register struct ifqueue
*inq
;
* Deal with trailer protocol: if type is trailer type
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
eh
= (struct ether_header
*)buf
;
eh
->ether_type
= ntohs((u_short
)eh
->ether_type
);
#define nedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
if (eh
->ether_type
>= ETHERTYPE_TRAIL
&&
eh
->ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (eh
->ether_type
- ETHERTYPE_TRAIL
) * 512;
if (off
>= ETHERMTU
) return; /* sanity */
eh
->ether_type
= ntohs(*nedataaddr(eh
, off
, u_short
*));
resid
= ntohs(*(nedataaddr(eh
, off
+2, u_short
*)));
if (off
+ resid
> len
) return; /* sanity */
* Pull packet off interface. Off is nonzero if packet
* has trailing header; neget will then force this header
* information to be at the front, but we still have to drop
* the type and length which are at the front of any trailer data.
m
= neget(buf
, len
, off
, &ns
->ns_if
);
ether_input(&ns
->ns_if
, eh
, m
);
* Pull read data off a interface.
* Len is length of data, with local net header stripped.
* Off is non-zero if a trailer protocol was used, and
* gives the offset of the trailer information.
* We copy the trailer information and then all the normal
* data into mbufs. When full cluster sized units are present
neget(buf
, totlen
, off0
, ifp
)
struct mbuf
*top
, **mp
, *m
, *p
;
register caddr_t cp
= buf
;
buf
+= sizeof(struct ether_header
);
cp
+= off
+ 2 * sizeof(u_short
);
totlen
-= 2 * sizeof(u_short
);
MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
m
->m_pkthdr
.len
= totlen
;
MGET(m
, M_DONTWAIT
, MT_DATA
);
len
= min(totlen
, epkt
- cp
);
m
->m_len
= len
= min(len
, MCLBYTES
);
* Place initial small packet/header at end of mbuf.
if (top
== 0 && len
+ max_linkhdr
<= m
->m_len
)
m
->m_data
+= max_linkhdr
;
bcopy(cp
, mtod(m
, caddr_t
), (unsigned)len
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
struct ne_softc
*ns
= &ne_softc
[ifp
->if_unit
];
struct ifreq
*ifr
= (struct ifreq
*)data
;
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
->sa_family
) {
neinit(ifp
->if_unit
); /* before arpwhohas */
((struct arpcom
*)ifp
)->ac_ipaddr
=
arpwhohas((struct arpcom
*)ifp
, &IA_SIN(ifa
)->sin_addr
);
register struct ns_addr
*ina
= &(IA_SNS(ifa
)->sns_addr
);
ina
->x_host
= *(union ns_host
*)(ns
->ns_addr
);
* The manual says we can't change the address
* while the receiver is armed,
ifp
->if_flags
&= ~IFF_RUNNING
;
bcopy((caddr_t
)ina
->x_host
.c_host
,
(caddr_t
)ns
->ns_addr
, sizeof(ns
->ns_addr
));
neinit(ifp
->if_unit
); /* does ne_setaddr() */
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ifp
->if_flags
& IFF_RUNNING
) {
ifp
->if_flags
&= ~IFF_RUNNING
;
outb(nec
+ds_cmd
,DSCM_STOP
|DSCM_NODMA
);
} else if (ifp
->if_flags
& IFF_UP
&&
(ifp
->if_flags
& IFF_RUNNING
) == 0)
bcopy((caddr_t
)ns
->ns_addr
, (caddr_t
) &ifr
->ifr_data
,
register struct ifnet
*ifp
;
register struct sockaddr_in
*sin
;
ifp
->if_addr
= *(struct sockaddr
*)sin
;
ifp
->if_net
= in_netof(sin
->sin_addr
);
ifp
->if_host
[0] = in_lnaof(sin
->sin_addr
);
if (nepaddr
[ifp
->if_unit
][0] == '3')
nepaddr
[ifp
->if_unit
][0] = ifp
->if_host
[0] << 1;
sin
= (struct sockaddr_in
*)&ifp
->if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= in_makeaddr(ifp
->if_net
, INADDR_ANY
);
ifp
->if_flags
|= IFF_BROADCAST
;