* Isolan AT 4141-0 Ethernet driver
* Copyright (C) 1993, Paul Richards. 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.
1) Add working multicast support
2) Use better allocation of memory to card
3) Advertise for more packets until all transmit buffers are full
4) Add more of the timers/counters e.g. arpcom.opackets etc.
#include "net/if_types.h"
#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"
#define ETHER_MAX_LEN 1518
char *card_type
[] = {"Unknown",
char *ic_type
[] = {"Unknown",
struct arpcom arpcom
; /* Ethernet common part */
int ic_type
; /* Am 7990 or Am79960 */
struct init_block
*init_block
; /* Lance initialisation block */
caddr_t bpf
; /* BPF "magic cookie" */
/* Function prototypes */
static int is_probe(struct isa_device
*);
static int is_attach(struct isa_device
*);
static void is_watchdog(int);
static int is_ioctl(struct ifnet
*, int, caddr_t
);
static void is_init(int);
static void is_start(struct ifnet
*);
static void recv_print(int, int);
static void xmit_print(int, int);
static inline void is_rint(int unit
);
static inline void isread(struct is_softc
*, unsigned char*, int);
struct isa_driver isdriver
= {
outw(is_softc
[unit
].rap
,port
);
outw(is_softc
[unit
].rdp
,val
);
u_short
isrdcsr(unit
,port
)
outw(is_softc
[unit
].rap
,port
);
return(inw(is_softc
[unit
].rdp
));
struct isa_device
*isa_dev
;
int unit
= isa_dev
->id_unit
;
is_softc
[unit
].iobase
= isa_dev
->id_iobase
;
* It's impossible to do a non-invasive probe of the
* LANCE and PCnet_ISA. The LANCE requires setting the
* STOP bit to access the registers and the PCnet_ISA
* address port resets to an unknown state!!
* Check for BICC cards first since for the NE2100 and
* PCnet-ISA cards this write will hit the Address PROM.
printf("Dumping io space for is%d starting at %x\n",unit
,is_softc
[unit
].iobase
);
printf(" %x ",inb(is_softc
[unit
].iobase
+i
));
if (nports
= bicc_probe(unit
))
if (nports
= ne2100_probe(unit
))
struct is_softc
*is
= &is_softc
[unit
];
is
->rap
= is
->iobase
+ NE2100_RAP
;
is
->rdp
= is
->iobase
+ NE2100_RDP
;
if (is
->ic_type
= lance_probe(unit
)) {
* Extract the physical MAC address from ROM
for(i
=0;i
<ETHER_ADDR_LEN
;i
++)
is
->arpcom
.ac_enaddr
[i
]=inb(is
->iobase
+i
);
* Return number of I/O ports used by card
struct is_softc
*is
= &is_softc
[unit
];
is
->rap
= is
->iobase
+ BICC_RAP
;
is
->rdp
= is
->iobase
+ BICC_RDP
;
if (is
->ic_type
= lance_probe(unit
)) {
* Extract the physical ethernet address from ROM
for(i
=0;i
<ETHER_ADDR_LEN
;i
++)
is
->arpcom
.ac_enaddr
[i
]=inb(is
->iobase
+(i
*2));
* Return number of I/O ports used by card
* Determine which, if any, of the LANCE or
* PCnet-ISA are present on the card.
* Have to reset the LANCE to get any
* stable information from it.
if (isrdcsr(unit
,0) != STOP
)
* This either isn't a LANCE
* or there's a major problem.
* Depending on which controller it is, CSR3 will have
* different settable bits. Write to them all and see which ones
iswrcsr(unit
,3, LANCE_MASK
);
if (isrdcsr(unit
,3) == LANCE_MASK
)
if (isrdcsr(unit
,3) == PCnet_ISA_MASK
)
is_reset(int unit
, int uban
)
struct is_softc
*is
= &is_softc
[unit
];
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.
struct isa_device
*isa_dev
;
int unit
= isa_dev
->id_unit
;
struct is_softc
*is
= &is_softc
[unit
];
struct ifnet
*ifp
= &is
->arpcom
.ac_if
;
ifp
->if_name
= isdriver
.name
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_NOTRAILERS
;
ifp
->if_output
= ether_output
;
ifp
->if_start
= is_start
;
ifp
->if_ioctl
= is_ioctl
;
ifp
->if_reset
= is_reset
;
ifp
->if_watchdog
= is_watchdog
;
* XXX -- not sure this is right place to do this
* Allocate memory for use by Lance
* transmit and receive buffers.
* XXX - hopefully have better way to get dma'able memory later,
* this code assumes that the physical memory address returned
* from malloc will be below 16Mb. The Lance's address registers
#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \
+ sizeof(struct init_block) + 8)
is
->init_block
= (struct init_block
*)malloc(MAXMEM
,M_TEMP
,M_NOWAIT
);
printf("is%d : Couldn't allocate memory for card\n",unit
);
* XXX -- should take corrective action if not
* quadword alilgned, the 8 byte slew factor in MAXMEM
if ((u_long
)is
->init_block
& 0x3)
printf("is%d: memory allocated not quadword aligned\n");
isa_dmacascade(isa_dev
->id_drq
);
* Search down the ifa address list looking
* for the AF_LINK type entry
while ((ifa
!= 0) && (ifa
->ifa_addr
!= 0) &&
(ifa
->ifa_addr
->sa_family
!= AF_LINK
))
* If we find an AF_LINK type entry, we will fill
* in the hardware address for this interface.
if ((ifa
!= 0) && (ifa
->ifa_addr
!= 0)) {
* Fill in the link level address for this interface
sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
sdl
->sdl_type
= IFT_ETHER
;
sdl
->sdl_alen
= ETHER_ADDR_LEN
;
bcopy(is
->arpcom
.ac_enaddr
, LLADDR(sdl
), ETHER_ADDR_LEN
);
printf ("is%d: address %s\n", unit
,
ether_sprintf(is
->arpcom
.ac_enaddr
)) ;
printf("%s, %s\n",ic_type
[is
->ic_type
],card_type
[is
->card_type
]);
bpfattach(&is
->bpf
, ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
log(LOG_ERR
, "is%d: device timeout\n", unit
);
/* Lance initialisation block set up */
struct is_softc
*is
= &is_softc
[unit
];
* At this point we assume that the
* memory allocated to the Lance is
* quadword aligned. If it isn't
* then the initialisation is going
* Set up lance initialisation block
temp
= (void *)is
->init_block
;
temp
+= sizeof(struct init_block
);
is
->rd
= (struct mds
*) temp
;
is
->td
= (struct mds
*) (temp
+ (NRBUF
*sizeof(struct mds
)));
temp
+= (NRBUF
+NTBUF
) * sizeof(struct mds
);
is
->init_block
->mode
= 0;
for (i
=0; i
<ETHER_ADDR_LEN
; i
++)
is
->init_block
->padr
[i
] = is
->arpcom
.ac_enaddr
[i
];
is
->init_block
->ladrf
[i
] = MULTI_INIT_ADDR
;
is
->init_block
->rdra
= kvtop(is
->rd
);
is
->init_block
->rlen
= ((kvtop(is
->rd
) >> 16) & 0xff) | (RLEN
<<13);
is
->init_block
->tdra
= kvtop(is
->td
);
is
->init_block
->tlen
= ((kvtop(is
->td
) >> 16) & 0xff) | (TLEN
<<13);
* Set up receive ring descriptors
is
->rbuf
= (unsigned char *)temp
;
for (i
=0; i
<NRBUF
; i
++) {
(is
->rd
+i
)->addr
= kvtop(temp
);
(is
->rd
+i
)->flags
= ((kvtop(temp
) >> 16) & 0xff) | OWN
;
(is
->rd
+i
)->bcnt
= -BUFSIZE
;
* Set up transmit ring descriptors
is
->tbuf
= (unsigned char *)temp
;
for (i
=0; i
<NTBUF
; i
++) {
(is
->td
+i
)->addr
= kvtop(temp
);
(is
->td
+i
)->flags
= ((kvtop(temp
) >> 16) & 0xff);
* Initialization of interface; set up initialization block
* and transmit/receive descriptor rings.
register struct is_softc
*is
= &is_softc
[unit
];
struct ifnet
*ifp
= &is
->arpcom
.ac_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0) return;
is
->last_rd
= is
->last_td
= is
->no_td
= 0;
/* Set up lance's memory area */
/* No byte swapping etc */
/* Give lance the physical address of its memory area */
iswrcsr(unit
,1,kvtop(is
->init_block
));
iswrcsr(unit
,2,(kvtop(is
->init_block
) >> 16) & 0xff);
/* OK, let's try and initialise the Lance */
/* Wait for initialisation to finish */
if (isrdcsr(unit
,0)&IDON
)
if (isrdcsr(unit
,0)&IDON
) {
iswrcsr(unit
,0,STRT
|IDON
|INEA
);
ifp
->if_flags
|= IFF_RUNNING
;
ifp
->if_flags
&= ~IFF_OACTIVE
;
printf("is%d: card failed to initialise\n", unit
);
* 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
*is
= &is_softc
[unit
];
if ((is
->arpcom
.ac_if
.if_flags
& IFF_RUNNING
) == 0)
cdm
= (is
->td
+ is
->last_td
);
IF_DEQUEUE(&is
->arpcom
.ac_if
.if_snd
, m
);
* Copy the mbuf chain into the transmit buffer
buffer
= is
->tbuf
+(BUFSIZE
*is
->last_td
);
for (m0
=m
; m
!= 0; m
=m
->m_next
) {
bcopy(mtod(m
,caddr_t
),buffer
,m
->m_len
);
int off
, datasize
, resid
;
char ether_packet
[ETHER_MAX_LEN
];
* 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.
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
),
(caddr_t
)&trailer_header
.ether_type
);
resid
= trailer_header
.ether_residual
-
sizeof(struct trailer_header
);
m_copydata(m0
, off
+sizeof(struct trailer_header
),
m_copydata(m0
, sizeof(struct ether_header
),
/* restore original ether packet type */
eh
->ether_type
= trailer_header
.ether_type
;
bpf_tap(is
->bpf
, ether_packet
, ep
- ether_packet
);
len
= MAX(len
,ETHER_MIN_LEN
);
* Init transmit registers, and set transmit start flag.
cdm
->flags
|= (OWN
|STP
|ENP
);
xmit_print(unit
,is
->last_td
);
iswrcsr(unit
,0,TDMD
|INEA
);
if (++is
->last_td
>= NTBUF
)
}while(++is
->no_td
< NTBUF
);
is
->arpcom
.ac_if
.if_flags
|= IFF_OACTIVE
;
printf("no_td = %x, last_td = %x\n",is
->no_td
, is
->last_td
);
register struct is_softc
*is
= &is_softc
[unit
];
while((isr
=isrdcsr(unit
,0))&INTR
) {
printf("is%d: BABL\n",unit
);
is
->arpcom
.ac_if
.if_oerrors
++;
printf("is%d: CERR\n",unit
);
is
->arpcom
.ac_if
.if_collisions
++;
printf("is%d: MISS\n",unit
);
is
->arpcom
.ac_if
.if_ierrors
++;
printf("is%d: MERR\n",unit
);
iswrcsr(unit
,0,BABL
|CERR
|MISS
|MERR
|INEA
);
printf("is%d: !(isr&RXON)\n", unit
);
is
->arpcom
.ac_if
.if_ierrors
++;
printf("is%d: !(isr&TXON)\n", unit
);
is
->arpcom
.ac_if
.if_oerrors
++;
/* reset watchdog timer */
is
->arpcom
.ac_if
.if_timer
= 0;
/* reset watchdog timer */
is
->arpcom
.ac_if
.if_timer
= 0;
iswrcsr(unit
,0,TINT
|INEA
);
struct is_softc
*is
= &is_softc
[unit
];
register struct ifnet
*ifp
= &is
->arpcom
.ac_if
;
is
->arpcom
.ac_if
.if_opackets
++;
if ((i
=is
->last_td
- is
->no_td
) < 0)
printf("Trans cdm = %x\n",cdm
);
is
->arpcom
.ac_if
.if_flags
&= ~IFF_OACTIVE
;
if (++rmd == NRBUF) rmd=0, cdm=is->rd; else ++cdm
/* only called from one place, so may as well integrate */
static inline void is_rint(int unit
)
register struct is_softc
*is
=&is_softc
[unit
];
register int rmd
= is
->last_rd
;
struct mds
*cdm
= (is
->rd
+ rmd
);
/* Out of sync with hardware, should never happen */
printf("is%d: error: out of sync\n",unit
);
iswrcsr(unit
,0,RINT
|INEA
);
/* Process all buffers with valid data */
while (!(cdm
->flags
&OWN
)) {
/* Clear interrupt to avoid race condition */
iswrcsr(unit
,0,RINT
|INEA
);
printf("is%d: FRAM\n",unit
);
printf("is%d: OFLO\n",unit
);
printf("is%d: CRC\n",unit
);
printf("is%d: RBUFF\n",unit
);
if (cdm
->flags
&(STP
|ENP
) != (STP
|ENP
)) {
iswrcsr(unit
,0,RINT
|INEA
);
}while (!(cdm
->flags
&(OWN
|ERR
|STP
|ENP
)));
printf("is%d: Chained buffer\n",unit
);
if ((cdm
->flags
& (OWN
|ERR
|STP
|ENP
)) != ENP
) {
recv_print(unit
,is
->last_rd
);
isread(is
,is
->rbuf
+(BUFSIZE
*rmd
),(int)cdm
->mcnt
);
is
->arpcom
.ac_if
.if_ipackets
++;
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.
isread(struct is_softc
*is
, unsigned char *buf
, int len
)
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
, &is
->arpcom
.ac_if
);
* Check if there's a BPF listener on this interface.
* If so, hand off the raw packet to bpf.
* 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 ((is
->arpcom
.ac_if
.if_flags
& IFF_PROMISC
) &&
bcmp(eh
->ether_dhost
, is
->arpcom
.ac_enaddr
,
sizeof(eh
->ether_dhost
)) != 0 &&
bcmp(eh
->ether_dhost
, etherbroadcastaddr
,
sizeof(eh
->ether_dhost
)) != 0) {
ether_input(&is
->arpcom
.ac_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
*is
= &is_softc
[unit
];
struct ifreq
*ifr
= (struct ifreq
*)data
;
switch (ifa
->ifa_addr
->sa_family
) {
is_init(ifp
->if_unit
); /* 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
((struct arpcom
*)ifp
)->ac_ipaddr
=
arpwhohas((struct arpcom
*)ifp
, &IA_SIN(ifa
)->sin_addr
);
* XXX - This code is probably wrong
register struct ns_addr
*ina
= &(IA_SNS(ifa
)->sns_addr
);
*(union ns_host
*)(is
->arpcom
.ac_enaddr
);
bcopy((caddr_t
)ina
->x_host
.c_host
,
(caddr_t
)is
->arpcom
.ac_enaddr
,
sizeof(is
->arpcom
.ac_enaddr
));
* If interface is marked down and it is running, then stop it
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ifp
->if_flags
& IFF_RUNNING
) {
ifp
->if_flags
&= ~IFF_RUNNING
;
* If interface is marked up and it is stopped, then start it
if ((ifp
->if_flags
& IFF_UP
) &&
(ifp
->if_flags
& IFF_RUNNING
) == 0)
if (ifp
->if_flags
& IFF_DEBUG
)
if (ifp
->if_flags
& IFF_PROMISC
) {
* Set promiscuous mode on interface.
* XXX - for multicasts to work, we would need to
* write 1's in all bits of multicast
* hashing array. For now we assume that
* this was done in is_init().
is
->init_block
->mode
= PROM
;
* XXX - for multicasts to work, we would need to
* rewrite the multicast hashing array with the
* proper hash (would have been destroyed above).
{ /* Don't know about this */};
bcopy((caddr_t
)is
->arpcom
.ac_enaddr
, (caddr_t
) &ifr
->ifr_data
,
sizeof(is
->arpcom
.ac_enaddr
));
register struct is_softc
*is
=&is_softc
[unit
];
printf("is%d: Receive buffer %d, len = %d\n",unit
,no
,len
);
printf("is%d: Status %x\n",unit
,isrdcsr(unit
,0));
printf("is%d: data: ", unit
);
printf("%x ",*(is
->rbuf
+(BUFSIZE
*no
)+i
));
register struct is_softc
*is
=&is_softc
[unit
];
printf("is%d: Transmit buffer %d, len = %d\n",unit
,no
,len
);
printf("is%d: Status %x\n",unit
,isrdcsr(unit
,0));
printf("is%d: addr %x, flags %x, bcnt %x, mcnt %x\n",
unit
,rmd
->addr
,rmd
->flags
,rmd
->bcnt
,rmd
->mcnt
);
printf("is%d: data: ", unit
);
printf("%x ",*(is
->tbuf
+(BUFSIZE
*no
)+i
));