* 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
* Isolink 4110-2 Ethernet 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_isreg.h"
#include "i386/isa/icu.h"
/* Function prototypes */
int isprobe(), isattach();
int isioctl(),isinit(),isstart();
struct isa_driver isdriver
= {
* 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 */
struct init_block init_block
;
unsigned char *rbuf
,*tbuf
;
register struct is_softc
*ns
= &is_softc
[0];
/* Stop the lance chip, put it known state */
/* Extract board address */
for(i
=0; i
< 6; i
++) ns
->ns_addr
[i
] = inb(isc
+(i
*2));
printf("is%d: reset\n", unit
);
* 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 is_softc
*is
= &is_softc
[unit
];
register struct ifnet
*ifp
= &is
->ns_if
;
isa_dmacascade(dvp
->id_drq
);
ifp
->if_name
= isdriver
.name
;
printf (" ethernet address %s", ether_sprintf(is
->ns_addr
)) ;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_NOTRAILERS
;
ifp
->if_output
= ether_output
;
/* Lance initialisation block set up */
/* Temporary hack, will use kmem_alloc in future */
#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) + 8)
static u_char lance_mem
[MAXMEM
];
/* Align message descriptors on quad word boundary
temp
= (u_long
) &lance_mem
;
temp
= (temp
+8) - (temp
%8);
rd
= (struct mds
*) temp
;
td
= (struct mds
*) (temp
+ (NRBUF
*sizeof(struct mds
)));
temp
+= (NRBUF
+NTBUF
) * sizeof(struct mds
);
/* Get ethernet address */
init_block
.padr
[i
] = inb(isc
+(i
*2));
/* Clear multicast address for now */
init_block
.rdra
= kvtop(rd
);
init_block
.rlen
= ((kvtop(rd
) >> 16) & 0xff) | (RLEN
<<13);
init_block
.tdra
= kvtop(td
);
init_block
.tlen
= ((kvtop(td
) >> 16) & 0xff) | (TLEN
<<13);
/* Set up receive ring descriptors */
rbuf
= (unsigned char *)temp
;
for (i
=0; i
<NRBUF
; i
++) {
(rd
+i
)->addr
= kvtop(temp
);
(rd
+i
)->flags
= ((kvtop(temp
) >> 16) & 0xff) | OWN
;
/* Set up transmit ring descriptors */
tbuf
= (unsigned char *)temp
;
printf("rd = %x,td = %x, rbuf = %x, tbuf = %x,td+1=%x\n",rd
,td
,rbuf
,tbuf
,td
+1);
for (i
=0; i
<NTBUF
; i
++) {
(td
+i
)->addr
= kvtop(temp
);
(td
+i
)->flags
= ((kvtop(temp
) >> 16) & 0xff);
* Initialization of interface; set up initialization block
* and transmit/receive descriptor rings.
register struct is_softc
*ns
= &is_softc
[unit
];
struct ifnet
*ifp
= &ns
->ns_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0) return;
ns
->last_rd
= ns
->last_td
= ns
->no_td
= 0;
/* Set up lance's memory area */
/* Stop Lance to get access to other registers */
/* I wish I knew what this was */
/* Give lance the physical address of its memory area */
iswrcsr(1,kvtop(&init_block
));
iswrcsr(2,(kvtop(&init_block
) >> 16) & 0xff);
/* OK, let's try and initialise the Lance */
/* Wait for initialisation to finish */
iswrcsr(0,STRT
|IDON
|INEA
);
ns
->ns_if
.if_flags
|= IFF_RUNNING
;
printf("Isolink card failed to initialise\n");
* 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 is_softc
*ns
= &is_softc
[ifp
->if_unit
];
if ((ns
->ns_if
.if_flags
& IFF_RUNNING
) == 0)
cdm
= (td
+ ns
->last_td
);
IF_DEQUEUE(&ns
->ns_if
.if_snd
, m
);
* Copy the mbuf chain into the transmit buffer
buffer
= tbuf
+(BUFSIZE
*ns
->last_td
);
for (m0
=m
; m
!= 0; m
=m
->m_next
) {
bcopy(mtod(m
,caddr_t
),buffer
,m
->m_len
);
len
= MAX(len
,ETHER_MIN_LEN
);
* Init transmit registers, and set transmit start flag.
cdm
->flags
|= (OWN
|STP
|ENP
);
if (++ns
->last_td
>= NTBUF
)
}while(++ns
->no_td
< NTBUF
);
ns
->ns_if
.if_flags
|= IFF_OACTIVE
;
printf("no_td = %x, last_td = %x\n",ns
->no_td
, ns
->last_td
);
register struct is_softc
*ns
= &is_softc
[unit
];
while((isr
=isrdcsr(0))&INTR
) {
iswrcsr(0,BABL
|CERR
|MISS
|MERR
|INEA
);
struct is_softc
*is
= &is_softc
[unit
];
register struct ifnet
*ifp
= &is
->ns_if
;
if ((i
=is
->last_td
- is
->no_td
) < 0)
printf("Trans cdm = %x\n",cdm
);
is
->ns_if
.if_flags
&= ~IFF_OACTIVE
;
if (++rmd == NRBUF) rmd=0, cdm=rd; else ++cdm
register struct is_softc
*is
=&is_softc
[unit
];
register int rmd
= is
->last_rd
;
struct mds
*cdm
= (rd
+ rmd
);
/* Out of sync with hardware, should never happen */
printf("is0 error: out of sync\n");
/* Process all buffers with valid data */
while (!(cdm
->flags
&OWN
)) {
/* Clear interrupt to avoid race condition */
if (cdm
->flags
&(STP
|ENP
) != (STP
|ENP
)) {
}while (!(cdm
->flags
&(OWN
|ERR
|STP
|ENP
)));
printf("Chained buffer\n");
if ((cdm
->flags
& (OWN
|ERR
|STP
|ENP
)) != ENP
) {
isread(is
,rbuf
+(BUFSIZE
*rmd
),cdm
->mcnt
);
printf("is->last_rd = %x, cdm = %x\n",is
->last_rd
,cdm
);
* Pass a packet to the higher levels.
* We deal with the trailer protocol here.
register struct is_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
);
len
= len
- sizeof(struct ether_header
) - 4;
#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
= isget(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
isget(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 is_softc
*ns
= &is_softc
[ifp
->if_unit
];
struct ifreq
*ifr
= (struct ifreq
*)data
;
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
->sa_family
) {
isinit(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
));
isinit(ifp
->if_unit
); /* does ne_setaddr() */
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ifp
->if_flags
& IFF_RUNNING
) {
ifp
->if_flags
&= ~IFF_RUNNING
;
} else if (ifp
->if_flags
& IFF_UP
&&
(ifp
->if_flags
& IFF_RUNNING
) == 0)
bcopy((caddr_t
)ns
->ns_addr
, (caddr_t
) &ifr
->ifr_data
,
printf("Receive buffer %d, len = %d\n",no
,len
);
printf("Status %x\n",isrdcsr(0));
printf("%x ",*(rbuf
+(BUFSIZE
*no
)+i
));
printf("Transmit buffer %d, len = %d\n",no
,len
);
printf("Status %x\n",isrdcsr(0));
printf("addr %x, flags %x, bcnt %x, mcnt %x\n",
rmd
->addr
,rmd
->flags
,rmd
->bcnt
,rmd
->mcnt
);
printf("%x ",*(tbuf
+(BUFSIZE
*no
)+i
));