* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.noredist.c%
* @(#)if_we.c 5.2 (Berkeley) %G%
* 8/28/89 - Initial version(if_wd.c), Tim L Tucker
* Western Digital 8003 ethernet/starlan adapter
* Supports the following interface cards:
* WD8003E, WD8003EBT, WD8003S, WD8003SBT
* The Western Digital card is one of many AT/MCA ethernet interfaces
* based on the National N8390/NS32490 Network Interface chip set.
#include "../net/netisr.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#include "../netns/ns_if.h"
#include "machine/isa/if_wereg.h"
#include "machine/isa/device.h"
* This constant should really be 60 because the we adds 4 bytes of crc.
* However when set to 60 our packets are ignored by deuna's , 3coms are
* okay ??????????????????????????????????????????
#define ETHER_HDR_SIZE 14
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* qe_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
struct arpcom we_ac
; /* Ethernet common part */
#define we_if we_ac.ac_if /* network-visible interface */
#define we_addr we_ac.ac_enaddr /* hardware Ethernet address */
u_char we_flags
; /* software state */
u_char we_type
; /* interface type code */
u_short we_vector
; /* interrupt vector */
caddr_t we_io_ctl_addr
; /* i/o bus address, control */
caddr_t we_io_nic_addr
; /* i/o bus address, NS32490 */
caddr_t we_vmem_addr
; /* card RAM virtual memory base */
u_long we_vmem_size
; /* card RAM bytes */
caddr_t we_vmem_ring
; /* receive ring RAM vaddress */
int weprobe(), weattach(), weintr();
int weinit(), weoutput(), weioctl(), wereset();
struct isa_driver wedriver
= {
* Probe the WD8003 to see if it's there
register struct we_softc
*sc
= &we_softc
[is
->id_unit
];
* Here we check the card ROM, if the checksum passes, and the
* type code and ethernet address check out, then we know we have
* Autoconfiguration: No warning message is printed on error.
for (sum
= 0, i
= 0; i
< 8; ++i
)
sum
+= inb(is
->id_iobase
+ WD_ROM_OFFSET
+ i
);
sc
->we_type
= inb(is
->id_iobase
+ WD_ROM_OFFSET
+ 6);
if ((sc
->we_type
!= WD_ETHER
) && (sc
->we_type
!= WD_STARLAN
)
&& (sc
->we_type
!= WD_ETHER2
))
* Setup card RAM area and i/o addresses
* Kernel Virtual to segment C0000-DFFFF?????
sc
->we_io_ctl_addr
= is
->id_iobase
;
sc
->we_io_nic_addr
= sc
->we_io_ctl_addr
+ WD_NIC_OFFSET
;
sc
->we_vector
= is
->id_irq
;
sc
->we_vmem_addr
= (caddr_t
)is
->id_maddr
;
sc
->we_vmem_size
= is
->id_msize
;
sc
->we_vmem_ring
= sc
->we_vmem_addr
+ (WD_PAGE_SIZE
* WD_TXBUF_SIZE
);
* Save board ROM station address
for (i
= 0; i
< ETHER_ADDR_LEN
; ++i
)
sc
->we_addr
[i
] = inb(sc
->we_io_ctl_addr
+ WD_ROM_OFFSET
+ i
);
* Mapin interface memory, setup memory select register
/* wem.ms_addr = (u_long)sc->we_vmem_addr >> 13; */
wem
.ms_addr
= (u_long
)(0xd0000)>> 13;
outb(sc
->we_io_ctl_addr
, wem
.ms_byte
);
* clear interface memory, then sum to make sure its valid
for (i
= 0; i
< sc
->we_vmem_size
; ++i
)
sc
->we_vmem_addr
[i
] = 0x0;
for (sum
= 0, i
= 0; i
< sc
->we_vmem_size
; ++i
)
sum
+= sc
->we_vmem_addr
[i
];
printf("we%d: wd8003 dual port RAM address error\n", is
->id_unit
);
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct we_softc
*sc
= &we_softc
[is
->id_unit
];
register struct ifnet
*ifp
= &sc
->we_if
;
* Initialize ifnet structure
ifp
->if_unit
= is
->id_unit
;
ifp
->if_flags
= IFF_BROADCAST
|IFF_NOTRAILERS
;
ifp
->if_output
= weoutput
;
((sc
->we_type
!= WD_STARLAN
) ? "ethernet" : "starlan"),
ether_sprintf(sc
->we_addr
));
printf("we%d: reset\n", unit
);
we_softc
[unit
].we_flags
&= ~WDF_RUNNING
;
* Take interface offline.
register struct we_softc
*sc
= &we_softc
[unit
];
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
* Initialization of interface (really just NS32490).
register struct we_softc
*sc
= &we_softc
[unit
];
register struct ifnet
*ifp
= &sc
->we_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if (sc
->we_flags
& WDF_RUNNING
)
* Initialize NS32490 in order given in NSC NIC manual.
* this is stock code...please see the National manual for details.
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
outb(sc
->we_io_nic_addr
+ WD_P0_DCR
, WD_D_CONFIG
);
outb(sc
->we_io_nic_addr
+ WD_P0_RBCR0
, 0);
outb(sc
->we_io_nic_addr
+ WD_P0_RBCR1
, 0);
outb(sc
->we_io_nic_addr
+ WD_P0_RCR
, WD_R_MON
);
outb(sc
->we_io_nic_addr
+ WD_P0_TCR
, WD_T_CONFIG
);
outb(sc
->we_io_nic_addr
+ WD_P0_TPSR
, 0);
outb(sc
->we_io_nic_addr
+ WD_P0_PSTART
, WD_TXBUF_SIZE
);
outb(sc
->we_io_nic_addr
+ WD_P0_PSTOP
,
sc
->we_vmem_size
/ WD_PAGE_SIZE
);
outb(sc
->we_io_nic_addr
+ WD_P0_BNRY
, WD_TXBUF_SIZE
);
outb(sc
->we_io_nic_addr
+ WD_P0_ISR
, 0xff);
outb(sc
->we_io_nic_addr
+ WD_P0_IMR
, WD_I_CONFIG
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
for (i
= 0; i
< ETHER_ADDR_LEN
; ++i
)
outb(sc
->we_io_nic_addr
+ WD_P1_PAR0
+ i
, sc
->we_addr
[i
]);
for (i
= 0; i
< ETHER_ADDR_LEN
; ++i
) /* == broadcast addr */
outb(sc
->we_io_nic_addr
+ WD_P1_MAR0
+ i
, 0xff);
outb(sc
->we_io_nic_addr
+ WD_P1_CURR
, WD_TXBUF_SIZE
);
outb(sc
->we_io_nic_addr
+ WD_P1_COMMAND
, wecmd
.cs_byte
);
outb(sc
->we_io_nic_addr
+ WD_P0_RCR
, WD_R_CONFIG
);
* Take the interface out of reset, program the vector,
* enable interrupts, and tell the world we are up.
ifp
->if_flags
|= IFF_UP
| IFF_RUNNING
;
sc
->we_flags
|= WDF_RUNNING
;
sc
->we_flags
&= ~WDF_TXBUSY
;
* Start output on interface.
register struct we_softc
*sc
= &we_softc
[unit
];
* The NS32490 has only one transmit buffer, if it is busy we
* must wait until the transmit interrupt completes.
if (sc
->we_flags
& WDF_TXBUSY
) {
IF_DEQUEUE(&sc
->we_if
.if_snd
, m
);
sc
->we_flags
|= WDF_TXBUSY
;
* Copy the mbuf chain into the transmit buffer
buffer
= sc
->we_vmem_addr
;
for (m0
= m
; m
!= 0; m
= m
->m_next
) {
bcopy(mtod(m
, caddr_t
), buffer
, m
->m_len
);
* If this was a broadcast packet loop it
* back because the hardware can't hear its own
if (bcmp((caddr_t
)(mtod(m0
, struct ether_header
*)->ether_dhost
),
(caddr_t
)etherbroadcastaddr
,
sizeof(etherbroadcastaddr
)) == 0) {
* Init transmit length registers, and set transmit start flag.
len
= MAX(len
, ETHER_MIN_LEN
);
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
outb(sc
->we_io_nic_addr
+ WD_P0_TBCR0
, len
& 0xff);
outb(sc
->we_io_nic_addr
+ WD_P0_TBCR1
, len
>> 8);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
* Ethernet interface interrupt processor
register struct we_softc
*sc
= &we_softc
[0];
union we_interrupt weisr
;
/* disable onboard interrupts, then get interrupt status */
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
/* outb(sc->we_io_nic_addr + WD_P0_IMR, 0); */
weisr
.is_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_ISR
);
printf("weintr %x ", inb(sc
->we_io_nic_addr
+ WD_P0_ISR
));
outb(sc
->we_io_nic_addr
+WD_P0_IMR
,0xff);
outb(sc
->we_io_nic_addr
+ WD_P0_ISR
, 0xff);
/* need to read these registers to clear status */
sc
->we_if
.if_collisions
+=
inb(sc
->we_io_nic_addr
+ WD_P0_TBCR0
);
/* need to read these registers to clear status */
(void) inb(sc
->we_io_nic_addr
+ 0xD);
(void) inb(sc
->we_io_nic_addr
+ 0xE);
(void) inb(sc
->we_io_nic_addr
+ 0xF);
/* normal transmit complete */
/* normal receive notification */
/* try to start transmit */
/* re-enable onboard interrupts */
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
outb(sc
->we_io_nic_addr
+ WD_P0_IMR
, WD_I_CONFIG
);
* Ethernet interface transmit interrupt.
register struct we_softc
*sc
= &we_softc
[unit
];
* Do some statistics (assume page zero of NIC mapped in)
sc
->we_flags
&= ~WDF_TXBUSY
;
sc
->we_if
.if_collisions
+= inb(sc
->we_io_nic_addr
+ WD_P0_TBCR0
);
* Ethernet interface receiver interrupt.
register struct we_softc
*sc
= &we_softc
[unit
];
register struct mbuf
**m
;
* Traverse the receive ring looking for packets to pass back.
* The search is complete when we find a descriptor not in use.
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
bnry
= inb(sc
->we_io_nic_addr
+ WD_P0_BNRY
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
curr
= inb(sc
->we_io_nic_addr
+ WD_P1_CURR
);
printf("Bd %x cur %x ", bnry
, curr
);
/* get pointer to this buffer header structure */
wer
= (struct we_ring
*)(sc
->we_vmem_addr
+ (bnry
<< 8));
len
= wer
->we_count
- 4; /* count includes CRC */
pkt
= (caddr_t
)(wer
+ 1) /*- 2*/; /* 2 - word align pkt data */
count
= len
/*+ 2*/; /* copy two extra bytes */
endp
= (caddr_t
)(sc
->we_vmem_addr
+ sc
->we_vmem_size
);
/* pull packet out of dual ported RAM */
/* drop chain if can't get another buffer */
MGET(*m
, M_DONTWAIT
, MT_DATA
);
/* fill mbuf and attach to packet list */
mlen
= MIN(mlen
, endp
- pkt
);
bcopy(pkt
, mtod(*m
, caddr_t
), mlen
);
/* wrap memory pointer around circ buffer */
pkt
= (caddr_t
)sc
->we_vmem_ring
;
/* skip aligment bytes, send packet up to higher levels */
/* advance on chip Boundry register */
printf("nx %x ", wer
->we_next_packet
);
bnry
= wer
->we_next_packet
;
wecmd
.cs_byte
= inb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
);
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
/* watch out for NIC overflow, reset Boundry if invalid */
if ((bnry
- 1) < WD_TXBUF_SIZE
) {
outb(sc
->we_io_nic_addr
+ WD_P0_BNRY
,
(sc
->we_vmem_size
/ WD_PAGE_SIZE
) - 1);
outb(sc
->we_io_nic_addr
+ WD_P0_BNRY
, bnry
-1);
/* refresh our copy of CURR */
outb(sc
->we_io_nic_addr
+ WD_P0_COMMAND
, wecmd
.cs_byte
);
curr
= inb(sc
->we_io_nic_addr
+ WD_P1_CURR
);
printf("bd %x cur %x ", bnry
-1, curr
);
* Ethernet output routine.
* Encapsulate a packet of type family for the local net.
register struct we_softc
*sc
= &we_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct ether_header
*eh
;
if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
)) {
switch (dst
->sa_family
) {
/* Note: we ignore usetrailers */
idst
= ((struct sockaddr_in
*)dst
)->sin_addr
;
if (!arpresolve(&sc
->we_ac
, m
, &idst
, edst
, &usetrailers
))
return (0); /* if not yet resolved */
bcopy((caddr_t
)&(((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
),
(caddr_t
)edst
, sizeof (edst
));
eh
= (struct ether_header
*)dst
->sa_data
;
bcopy((caddr_t
)eh
->ether_dhost
, (caddr_t
)edst
, sizeof (edst
));
printf("we%d: can't handle af%d\n", ifp
->if_unit
,
* Add local net header. If no space in first mbuf,
if (m
->m_off
> MMAXOFF
|| MMINOFF
+ ETHER_HDR_SIZE
> m
->m_off
) {
m
= m_get(M_DONTWAIT
, MT_HEADER
);
m
->m_len
= ETHER_HDR_SIZE
;
m
->m_off
-= ETHER_HDR_SIZE
;
m
->m_len
+= ETHER_HDR_SIZE
;
eh
= mtod(m
, struct ether_header
*);
eh
->ether_type
= htons((u_short
)type
);
bcopy((caddr_t
)edst
, (caddr_t
)eh
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)sc
->we_addr
, (caddr_t
)eh
->ether_shost
,
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
* Process an ioctl request.
register struct ifnet
*ifp
;
struct we_softc
*sc
= &we_softc
[ifp
->if_unit
];
struct ifaddr
*ifa
= (struct ifaddr
*)data
;
int s
= splimp(), error
= 0;
switch(ifa
->ifa_addr
.sa_family
) {
((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
*)(sc
->we_addr
);
wesetaddr(ina
->x_host
.c_host
, ifp
->if_unit
);
if (((ifp
->if_flags
& IFF_UP
) == 0) &&
(sc
->we_flags
& WDF_RUNNING
)) {
} else if (((ifp
->if_flags
& IFF_UP
) == IFF_UP
) &&
((sc
->we_flags
& WDF_RUNNING
) == 0))
* set ethernet address for unit
wesetaddr(physaddr
, unit
)
register struct we_softc
*sc
= &we_softc
[unit
];
* Rewrite ethernet address, and then force restart of NIC
for (i
= 0; i
< ETHER_ADDR_LEN
; i
++)
sc
->we_addr
[i
] = physaddr
[i
];
sc
->we_flags
&= ~WDF_RUNNING
;
* Pass a packet to the higher levels.
register struct we_softc
*sc
;
* Get ethernet protocol type out of ether header
eh
= mtod(m
, struct ether_header
*);
type
= ntohs((u_short
)eh
->ether_type
);
m
->m_off
+= ETHER_HDR_SIZE
;
m
->m_len
-= ETHER_HDR_SIZE
;
* Insert ifp pointer at start of packet
m
->m_off
-= sizeof (struct ifnet
*);
m
->m_len
+= sizeof (struct ifnet
*);
*(mtod(m
, struct ifnet
**)) = &sc
->we_if
;