/**************************************************************************
NETBOOT - BOOTP/TFTP Bootstrap Program
This code is based heavily on David Greenman's if_ed.c driver
Copyright (C) 1993-1994, David Greenman, Martin Renters.
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 are the authors responsible for
the proper functioning of this software, nor do the authors assume any
responsibility for damages incurred with its use.
**************************************************************************/
unsigned short eth_nic_base
;
unsigned short eth_asic_base
;
unsigned char eth_tx_start
;
unsigned char eth_vendor
;
unsigned char eth_memsize
;
unsigned char *eth_node_addr
;
/**************************************************************************
The following two variables are used externally
**************************************************************************/
char packet
[ETH_MAX_PACKET
];
/**************************************************************************
ETH_PROBE - Look for an adapter
**************************************************************************/
eth_vendor
= VENDOR_NONE
;
/******************************************************************
******************************************************************/
for (eth_asic_base
= WD_LOW_BASE
; eth_asic_base
<= WD_HIGH_BASE
;
chksum
+= inb(i
+eth_asic_base
);
if ((chksum
& 0x00FF) == 0x00FF)
if (eth_asic_base
<= WD_HIGH_BASE
) { /* We've found a board */
eth_nic_base
= eth_asic_base
+ WD_NIC_ADDR
;
c
= inb(eth_asic_base
+WD_BID
); /* Get board id */
for (brd
= wd_boards
; brd
->name
; brd
++)
printf("\r\nUnknown Ethernet type %x\r\n", c
);
return(0); /* Unknown type */
eth_memsize
= brd
->memsize
;
if ((c
== TYPE_WD8013EP
) &&
(inb(eth_asic_base
+ WD_ICR
) & WD_ICR_16BIT
)) {
if ((c
& WD_SOFTCONFIG
) && (!(eth_flags
& FLAG_790
))) {
eth_bmem
= (char *)(0x80000 |
((inb(eth_asic_base
+ WD_MSR
) & 0x3F) << 13));
eth_bmem
= (char *)WD_DEFAULT_MEM
;
outb(eth_asic_base
+ WD_MSR
, 0x80); /* Reset */
printf("\r\n%s base 0x%x, memory 0x%X, addr ",
brd
->name
, eth_asic_base
, eth_bmem
);
printhb((int)(arptable
[ARP_CLIENT
].node
[i
] =
inb(i
+eth_asic_base
+WD_LAR
)));
if (eth_flags
& FLAG_790
) {
outb(eth_asic_base
+WD_MSR
, WD_MSR_MENB
);
outb(eth_asic_base
+0x04, (inb(eth_asic_base
+0x04) |
(((unsigned)eth_bmem
>> 13) & 0x0F) |
(((unsigned)eth_bmem
>> 11) & 0x40) |
(inb(eth_asic_base
+0x0B) & 0x0B));
outb(eth_asic_base
+0x04, (inb(eth_asic_base
+0x04) &
outb(eth_asic_base
+WD_MSR
,
(((unsigned)eth_bmem
>> 13) & 0x3F) | 0x40);
if (eth_flags
& FLAG_16BIT
) {
if (eth_flags
& FLAG_790
) {
eth_laar
= inb(eth_asic_base
+ WD_LAAR
);
outb(eth_asic_base
+ WD_LAAR
, WD_LAAR_M16EN
);
outb(eth_asic_base
+ WD_LAAR
, (eth_laar
=
WD_LAAR_M16EN
| WD_LAAR_L16EN
| 1));
/******************************************************************
Search for NE1000/2000 if no WD/SMC cards
******************************************************************/
if (eth_vendor
!= VENDOR_WD
) {
char romdata
[16], testbuf
[32];
char test
[] = "NE1000/2000 memory";
eth_bmem
= (char *)0; /* No shared memory */
eth_asic_base
= NE_BASE
+ NE_ASIC_OFFSET
;
eth_vendor
= VENDOR_NOVELL
;
c
= inb(eth_asic_base
+ NE_RESET
);
outb(eth_asic_base
+ NE_RESET
, c
);
outb(eth_nic_base
+ D8390_P0_COMMAND
, D8390_COMMAND_STP
|
outb(eth_nic_base
+ D8390_P0_RCR
, D8390_RCR_MON
);
outb(eth_nic_base
+ D8390_P0_DCR
, D8390_DCR_FT1
| D8390_DCR_LS
);
outb(eth_nic_base
+ D8390_P0_PSTART
, MEM_8192
);
outb(eth_nic_base
+ D8390_P0_PSTOP
, MEM_16384
);
eth_pio_write(test
, 8192, sizeof(test
));
eth_pio_read(8192, testbuf
, sizeof(test
));
if (!bcompare(test
, testbuf
, sizeof(test
))) {
outb(eth_nic_base
+ D8390_P0_DCR
, D8390_DCR_WTS
|
D8390_DCR_FT1
| D8390_DCR_LS
);
outb(eth_nic_base
+ D8390_P0_PSTART
, MEM_16384
);
outb(eth_nic_base
+ D8390_P0_PSTOP
, MEM_32768
);
eth_pio_write(test
, 16384, sizeof(test
));
eth_pio_read(16384, testbuf
, sizeof(test
));
if (!bcompare(testbuf
, test
, sizeof(test
))) return(0);
eth_pio_read(0, romdata
, 16);
printf("\r\nNE1000/NE2000 base 0x%x, addr ", eth_nic_base
);
printhb((int)(arptable
[ARP_CLIENT
].node
[i
] = romdata
[i
+ ((eth_flags
& FLAG_16BIT
) ? i
: 0)]));
if (eth_vendor
== VENDOR_NONE
) return(0);
eth_node_addr
= arptable
[ARP_CLIENT
].node
;
/**************************************************************************
ETH_RESET - Reset adapter
**************************************************************************/
if (eth_flags
& FLAG_790
)
outb(eth_nic_base
+D8390_P0_COMMAND
,
D8390_COMMAND_PS0
| D8390_COMMAND_STP
);
outb(eth_nic_base
+D8390_P0_COMMAND
,
D8390_COMMAND_PS0
| D8390_COMMAND_RD2
|
if (eth_flags
& FLAG_16BIT
)
outb(eth_nic_base
+D8390_P0_DCR
, 0x49);
outb(eth_nic_base
+D8390_P0_DCR
, 0x48);
outb(eth_nic_base
+D8390_P0_RBCR0
, 0);
outb(eth_nic_base
+D8390_P0_RBCR1
, 0);
outb(eth_nic_base
+D8390_P0_RCR
, 4); /* allow broadcast frames */
outb(eth_nic_base
+D8390_P0_TCR
, 2);
outb(eth_nic_base
+D8390_P0_TPSR
, eth_tx_start
);
outb(eth_nic_base
+D8390_P0_PSTART
, eth_tx_start
+ D8390_TXBUF_SIZE
);
if (eth_flags
& FLAG_790
) outb(eth_nic_base
+ 0x09, 0);
outb(eth_nic_base
+D8390_P0_PSTOP
, eth_memsize
);
outb(eth_nic_base
+D8390_P0_BOUND
, eth_tx_start
+ D8390_TXBUF_SIZE
);
outb(eth_nic_base
+D8390_P0_ISR
, 0xFF);
outb(eth_nic_base
+D8390_P0_IMR
, 0);
if (eth_flags
& FLAG_790
)
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS1
|
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS1
|
D8390_COMMAND_RD2
| D8390_COMMAND_STP
);
outb(eth_nic_base
+D8390_P1_PAR0
+i
, eth_node_addr
[i
]);
outb(eth_nic_base
+D8390_P1_MAR0
+i
, 0xFF);
outb(eth_nic_base
+D8390_P1_CURR
, eth_tx_start
+ D8390_TXBUF_SIZE
+1);
if (eth_flags
& FLAG_790
)
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
D8390_COMMAND_RD2
| D8390_COMMAND_STA
);
outb(eth_nic_base
+D8390_P0_ISR
, 0xFF);
outb(eth_nic_base
+D8390_P0_TCR
, 0);
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
**************************************************************************/
char *d
; /* Destination */
unsigned short t
; /* Type */
unsigned short s
; /* size */
if (eth_vendor
== VENDOR_WD
) { /* Memory interface */
if (eth_flags
& FLAG_16BIT
) {
outb(eth_asic_base
+ WD_LAAR
, eth_laar
| WD_LAAR_M16EN
);
if (eth_flags
& FLAG_790
) {
outb(eth_asic_base
+ WD_MSR
, WD_MSR_MENB
);
bcopy(d
, eth_bmem
, 6); /* dst */
bcopy(eth_node_addr
, eth_bmem
+6, ETHER_ADDR_SIZE
); /* src */
*(eth_bmem
+12) = t
>>8; /* type */
bcopy(p
, eth_bmem
+14, s
);
while (s
< ETH_MIN_PACKET
) *(eth_bmem
+(s
++)) = 0;
if (eth_flags
& FLAG_790
) {
outb(eth_asic_base
+ WD_MSR
, 0);
if (eth_flags
& FLAG_16BIT
) {
outb(eth_asic_base
+ WD_LAAR
, eth_laar
& ~WD_LAAR_M16EN
);
if (eth_vendor
== VENDOR_NOVELL
) { /* Programmed I/O */
type
= (t
>> 8) | (t
<< 8);
eth_pio_write(d
, eth_tx_start
<<8, 6);
eth_pio_write(eth_node_addr
, (eth_tx_start
<<8)+6, 6);
eth_pio_write(&type
, (eth_tx_start
<<8)+12, 2);
eth_pio_write(p
, (eth_tx_start
<<8)+14, s
);
if (s
< ETH_MIN_PACKET
) s
= ETH_MIN_PACKET
;
if (eth_flags
& FLAG_790
)
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
D8390_COMMAND_RD2
| D8390_COMMAND_STA
);
outb(eth_nic_base
+D8390_P0_TPSR
, eth_tx_start
);
outb(eth_nic_base
+D8390_P0_TBCR0
, s
);
outb(eth_nic_base
+D8390_P0_TBCR1
, s
>>8);
if (eth_flags
& FLAG_790
)
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
D8390_COMMAND_TXP
| D8390_COMMAND_STA
);
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
|
D8390_COMMAND_TXP
| D8390_COMMAND_RD2
|
/**************************************************************************
ETH_POLL - Wait for a frame
**************************************************************************/
unsigned char bound
,curr
,rstat
;
struct ringbuffer pkthdr
;
rstat
= inb(eth_nic_base
+D8390_P0_RSR
);
if (rstat
& D8390_RSTAT_OVER
) {
if (!(rstat
& D8390_RSTAT_PRX
)) return(0);
bound
= inb(eth_nic_base
+D8390_P0_BOUND
)+1;
if (bound
== eth_memsize
) bound
= eth_tx_start
+ D8390_TXBUF_SIZE
;
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS1
);
curr
= inb(eth_nic_base
+D8390_P1_CURR
);
outb(eth_nic_base
+D8390_P0_COMMAND
, D8390_COMMAND_PS0
);
if (curr
== eth_memsize
) curr
=eth_tx_start
+ D8390_TXBUF_SIZE
;
if (curr
== bound
) return(0);
if (eth_vendor
== VENDOR_WD
) {
if (eth_flags
& FLAG_16BIT
) {
outb(eth_asic_base
+ WD_LAAR
, eth_laar
| WD_LAAR_M16EN
);
if (eth_flags
& FLAG_790
) {
outb(eth_asic_base
+ WD_MSR
, WD_MSR_MENB
);
if (eth_flags
& FLAG_PIO
)
eth_pio_read(pktoff
, &pkthdr
, 4);
bcopy(eth_bmem
+ pktoff
, &pkthdr
, 4);
len
= pkthdr
.len
- 4; /* sub CRC */
if (len
> 1514) len
= 1514;
bound
= pkthdr
.bound
; /* New bound ptr */
if ( (pkthdr
.status
& D8390_RSTAT_PRX
) && (len
> 14) && (len
< 1518)) {
len
= (eth_memsize
<< 8) - pktoff
;
if (packetlen
> len
) { /* We have a wrap-around */
if (eth_flags
& FLAG_PIO
)
eth_pio_read(pktoff
, p
, len
);
bcopy(eth_bmem
+ pktoff
, p
, len
);
pktoff
= (eth_tx_start
+ D8390_TXBUF_SIZE
) << 8;
if (eth_flags
& FLAG_PIO
)
eth_pio_read(pktoff
, p
, packetlen
);
bcopy(eth_bmem
+ pktoff
, p
, packetlen
);
type
= (packet
[12]<<8) | packet
[13];
if (eth_vendor
== VENDOR_WD
) {
if (eth_flags
& FLAG_790
) {
outb(eth_asic_base
+ WD_MSR
, 0);
if (eth_flags
& FLAG_16BIT
) {
outb(eth_asic_base
+ WD_LAAR
, eth_laar
&
if (bound
== (eth_tx_start
+ D8390_TXBUF_SIZE
))
outb(eth_nic_base
+D8390_P0_BOUND
, bound
-1);
if (ret
&& (type
== ARP
)) {
struct arprequest
*arpreq
;
arpreq
= (struct arprequest
*)&packet
[ETHER_HDR_SIZE
];
convert_ipaddr(&reqip
, arpreq
->tipaddr
);
if ((ntohs(arpreq
->opcode
) == ARP_REQUEST
) &&
(reqip
== arptable
[ARP_CLIENT
].ipaddr
)) {
arpreq
->opcode
= htons(ARP_REPLY
);
bcopy(arpreq
->sipaddr
, arpreq
->tipaddr
, 4);
bcopy(arpreq
->shwaddr
, arpreq
->thwaddr
, 6);
bcopy(arptable
[ARP_CLIENT
].node
, arpreq
->shwaddr
, 6);
convert_ipaddr(arpreq
->sipaddr
, &reqip
);
eth_transmit(arpreq
->thwaddr
, ARP
, sizeof(struct arprequest
),
/**************************************************************************
NE1000/NE2000 Support Routines
**************************************************************************/
static inline unsigned short inw(unsigned short a
)
asm volatile( "inw %1, %0" : "=a" (d
) : "d" (a
));
static inline void outw(unsigned short a
, unsigned short d
)
asm volatile( "outw %0, %1" : : "a" (d
), "d" (a
));
/**************************************************************************
ETH_PIO_READ - Read a frame via Programmed I/O
**************************************************************************/
eth_pio_read(src
, dst
, cnt
, init
)
outb(eth_nic_base
+ D8390_P0_COMMAND
, D8390_COMMAND_RD2
|
outb(eth_nic_base
+ D8390_P0_RBCR0
, cnt
);
outb(eth_nic_base
+ D8390_P0_RBCR1
, cnt
>>8);
outb(eth_nic_base
+ D8390_P0_RSAR0
, src
);
outb(eth_nic_base
+ D8390_P0_RSAR1
, src
>>8);
outb(eth_nic_base
+ D8390_P0_COMMAND
, D8390_COMMAND_RD0
|
if (eth_flags
& FLAG_16BIT
) {
*((unsigned short *)dst
) = inw(eth_asic_base
+ NE_DATA
);
*(dst
++) = inb(eth_asic_base
+ NE_DATA
);
/**************************************************************************
ETH_PIO_WRITE - Write a frame via Programmed I/O
**************************************************************************/
eth_pio_write(src
, dst
, cnt
, init
)
outb(eth_nic_base
+ D8390_P0_COMMAND
, D8390_COMMAND_RD2
|
outb(eth_nic_base
+ D8390_P0_ISR
, D8390_ISR_RDC
);
outb(eth_nic_base
+ D8390_P0_RBCR0
, cnt
);
outb(eth_nic_base
+ D8390_P0_RBCR1
, cnt
>>8);
outb(eth_nic_base
+ D8390_P0_RSAR0
, dst
);
outb(eth_nic_base
+ D8390_P0_RSAR1
, dst
>>8);
outb(eth_nic_base
+ D8390_P0_COMMAND
, D8390_COMMAND_RD1
|
if (eth_flags
& FLAG_16BIT
) {
if (cnt
& 1) cnt
++; /* Round up */
outw(eth_asic_base
+ NE_DATA
, *((unsigned short *)src
));
outb(eth_asic_base
+ NE_DATA
, *(src
++));
while((inb(eth_nic_base
+ D8390_P0_ISR
) & D8390_ISR_RDC
)
/**************************************************************************
ETH_PIO_READ - Dummy routine when NE2000 not compiled in
**************************************************************************/