* Copyright (c) 1990, 1991 William F. Jolitz.
* Copyright (c) 1990 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)if_ne.c 7.4 (Berkeley) 5/21/91
* 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
= {
#define ETHER_MAX_LEN 1536
* 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 + ETHER_MIN_LEN)
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
);
#define PAT(n) (0xa55a + 37*(n))
for (i
= 0; i
< 0xfff0; i
+=4) {
/* Extract board address */
nefetch ((caddr_t
)boarddata
, 0, sizeof(boarddata
));
for(i
=0; i
< 6; i
++) ns
->ns_addr
[i
] = boarddata
[i
];
* Fetch from onboard ROM/RAM
nefetch (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
);
outb (nec
+ds0_rbcr1
, len
>>8);
outb (nec
+ds0_rsar0
, ad
);
outb (nec
+ds0_rsar1
, ad
>>8);
/* Execute & extract from card */
outb (nec
+ds_cmd
, DSCM_RREAD
|DSCM_PG0
|DSCM_START
);
insw (nec
+ne_data
, up
, len
/2);
/* Wait till done, then shutdown feature */
while ((inb (nec
+ds0_isr
) & DSIS_RDC
) == 0) ;
outb (nec
+ds0_isr
, DSIS_RDC
);
neput (up
, ad
, len
) caddr_t up
; {
outb (nec
+ds_cmd
, DSCM_NODMA
|DSCM_PG0
|DSCM_START
);
/* Setup for remote dma */
outb (nec
+ds0_isr
, DSIS_RDC
);
if(len
&1) len
++; /* roundup to words */
outb (nec
+ds0_rbcr0
, len
);
outb (nec
+ds0_rbcr1
, len
>>8);
outb (nec
+ds0_rsar0
, ad
);
outb (nec
+ds0_rsar1
, ad
>>8);
/* Execute & stuff to card */
outb (nec
+ds_cmd
, DSCM_RWRITE
|DSCM_PG0
|DSCM_START
);
outsw (nec
+ne_data
, up
, len
/2);
/* Wait till done, then shutdown feature */
while ((inb (nec
+ds0_isr
) & DSIS_RDC
) == 0) ;
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
);
* Copy the mbuf chain into the transmit buffer
ns
->ns_flags
|= DSF_LOCK
; /* prevent entering nestart */
buffer
= TBUF
; len
= i
= 0;
for (m0
= m
; m
!= 0; m
= m
->m_next
)
if (m
->m_len
&1 && t
> m
->m_len
) {
neput(mtod(m
, caddr_t
), buffer
, m
->m_len
- 1);
m
->m_data
+= m
->m_len
- 1;
neput(mtod(m
, caddr_t
), buffer
, m
->m_len
);
* Init transmit length registers, and set transmit start flag.
if (len
< ETHER_MIN_LEN
) len
= ETHER_MIN_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
);
/* buffer successor/predecessor in ring? */
#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 */
outb(nec
+ds_cmd
,DSCM_NODMA
|DSCM_START
);
/* need to read these registers to clear status */
(void) inb(nec
+ ds0_rsr
);
/* We received something; rummage thru tiny ring buffer */
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
);
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
) {
/* Extract header from microcephalic board */
nefetch(&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)+ ns
->ns_ph
.pr_sz0
);
printf("Bogus Sts %x\n", ns
->ns_ph
.pr_status
);
nxt
= ns
->ns_ph
.pr_nxtpg
;
if ( nxt
>= RBUF
/DS_PGSIZE
&& nxt
<= RBUFEND
/DS_PGSIZE
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
);
outb(nec
+ds_cmd
, DSCM_START
|DSCM_NODMA
);
ns
->ns_flags
&= ~DSF_LOCK
;
/* 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
);
log(LOG_ERR
, "ne%d: error: isr %x\n", ns
-ne_softc
, isr
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);
* 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
< ETHER_MIN_LEN
|| len
> ETHER_MAX_LEN
)
/* this need not be so torturous - one/two bcopys at most into mbufs */
nefetch(ns
->ns_pb
, ns
->ns_ba
, min(len
,DS_PGSIZE
-sizeof(ns
->ns_ph
)));
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
));
if(++ns
->ns_cur
> 0x7f) ns
->ns_cur
= 0x46;
b
= ns
->ns_cur
*DS_PGSIZE
;
nefetch(p
, b
, DS_PGSIZE
);
p
+= DS_PGSIZE
; l
-= DS_PGSIZE
;
if(++ns
->ns_cur
> 0x7f) ns
->ns_cur
= 0x46;
b
= ns
->ns_cur
*DS_PGSIZE
;
/* don't forget checksum! */
len
-= sizeof(struct ether_header
) + sizeof(long);
neread(ns
,(caddr_t
)(ns
->ns_pb
), len
);
* 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
,