Goto sanos source index
//
// ne2000.c
//
// NE2000 network card driver
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 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. Neither the name of the project 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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
// SUCH DAMAGE.
//
#include <os/krnl.h>
//
// Page 0 register offsets
//
#define NE_P0_CR 0x00 // Command Register
#define NE_P0_CLDA0 0x01 // Current Local DMA Addr low (read)
#define NE_P0_PSTART 0x01 // Page Start register (write)
#define NE_P0_CLDA1 0x02 // Current Local DMA Addr high (read)
#define NE_P0_PSTOP 0x02 // Page Stop register (write)
#define NE_P0_BNRY 0x03 // Boundary Pointer
#define NE_P0_TSR 0x04 // Transmit Status Register (read)
#define NE_P0_TPSR 0x04 // Transmit Page Start (write)
#define NE_P0_NCR 0x05 // Number of Collisions Reg (read)
#define NE_P0_TBCR0 0x05 // Transmit Byte count, low (write)
#define NE_P0_FIFO 0x06 // FIFO register (read)
#define NE_P0_TBCR1 0x06 // Transmit Byte count, high (write)
#define NE_P0_ISR 0x07 // Interrupt Status Register
#define NE_P0_CRDA0 0x08 // Current Remote DMA Addr low (read)
#define NE_P0_RSAR0 0x08 // Remote Start Address low (write)
#define NE_P0_CRDA1 0x09 // Current Remote DMA Addr high (read)
#define NE_P0_RSAR1 0x09 // Remote Start Address high (write)
#define NE_P0_RBCR0 0x0A // Remote Byte Count low (write)
#define NE_P0_RBCR1 0x0B // Remote Byte Count high (write)
#define NE_P0_RSR 0x0C // Receive Status (read)
#define NE_P0_RCR 0x0C // Receive Configuration Reg (write)
#define NE_P0_CNTR0 0x0D // Frame alignment error counter (read)
#define NE_P0_TCR 0x0D // Transmit Configuration Reg (write)
#define NE_P0_CNTR1 0x0E // CRC error counter (read)
#define NE_P0_DCR 0x0E // Data Configuration Reg (write)
#define NE_P0_CNTR2 0x0F // Missed packet counter (read)
#define NE_P0_IMR 0x0F // Interrupt Mask Register (write)
//
// Page 1 register offsets
//
#define NE_P1_CR 0x00 // Command Register
#define NE_P1_PAR0 0x01 // Physical Address Register 0
#define NE_P1_PAR1 0x02 // Physical Address Register 1
#define NE_P1_PAR2 0x03 // Physical Address Register 2
#define NE_P1_PAR3 0x04 // Physical Address Register 3
#define NE_P1_PAR4 0x05 // Physical Address Register 4
#define NE_P1_PAR5 0x06 // Physical Address Register 5
#define NE_P1_CURR 0x07 // Current RX ring-buffer page
#define NE_P1_MAR0 0x08 // Multicast Address Register 0
#define NE_P1_MAR1 0x09 // Multicast Address Register 1
#define NE_P1_MAR2 0x0A // Multicast Address Register 2
#define NE_P1_MAR3 0x0B // Multicast Address Register 3
#define NE_P1_MAR4 0x0C // Multicast Address Register 4
#define NE_P1_MAR5 0x0D // Multicast Address Register 5
#define NE_P1_MAR6 0x0E // Multicast Address Register 6
#define NE_P1_MAR7 0x0F // Multicast Address Register 7
//
// Page 2 register offsets
//
#define NE_P2_CR 0x00 // Command Register
#define NE_P2_PSTART 0x01 // Page Start (read)
#define NE_P2_CLDA0 0x01 // Current Local DMA Addr 0 (write)
#define NE_P2_PSTOP 0x02 // Page Stop (read)
#define NE_P2_CLDA1 0x02 // Current Local DMA Addr 1 (write)
#define NE_P2_RNPP 0x03 // Remote Next Packet Pointer
#define NE_P2_TPSR 0x04 // Transmit Page Start (read)
#define NE_P2_LNPP 0x05 // Local Next Packet Pointer
#define NE_P2_ACU 0x06 // Address Counter Upper
#define NE_P2_ACL 0x07 // Address Counter Lower
#define NE_P2_RCR 0x0C // Receive Configuration Register (read)
#define NE_P2_TCR 0x0D // Transmit Configuration Register (read)
#define NE_P2_DCR 0x0E // Data Configuration Register (read)
#define NE_P2_IMR 0x0F // Interrupt Mask Register (read)
//
// Command Register (CR)
//
#define NE_CR_STP 0x01 // Stop
#define NE_CR_STA 0x02 // Start
#define NE_CR_TXP 0x04 // Transmit Packet
#define NE_CR_RD0 0x08 // Remote DMA Command 0
#define NE_CR_RD1 0x10 // Remote DMA Command 1
#define NE_CR_RD2 0x20 // Remote DMA Command 2
#define NE_CR_PS0 0x40 // Page Select 0
#define NE_CR_PS1 0x80 // Page Select 1
#define NE_CR_PAGE_0 0x00 // Select Page 0
#define NE_CR_PAGE_1 0x40 // Select Page 1
#define NE_CR_PAGE_2 0x80 // Select Page 2
//
// Interrupt Status Register (ISR)
//
#define NE_ISR_PRX 0x01 // Packet Received
#define NE_ISR_PTX 0x02 // Packet Transmitted
#define NE_ISR_RXE 0x04 // Receive Error
#define NE_ISR_TXE 0x08 // Transmission Error
#define NE_ISR_OVW 0x10 // Overwrite
#define NE_ISR_CNT 0x20 // Counter Overflow
#define NE_ISR_RDC 0x40 // Remote Data Complete
#define NE_ISR_RST 0x80 // Reset status
//
// Interrupt Mask Register (IMR)
#define NE_IMR_PRXE 0x01 // Packet Received Interrupt Enable
#define NE_IMR_PTXE 0x02 // Packet Transmit Interrupt Enable
#define NE_IMR_RXEE 0x04 // Receive Error Interrupt Enable
#define NE_IMR_TXEE 0x08 // Transmit Error Interrupt Enable
#define NE_IMR_OVWE 0x10 // Overwrite Error Interrupt Enable
#define NE_IMR_CNTE 0x20 // Counter Overflow Interrupt Enable
#define NE_IMR_RDCE 0x40 // Remote DMA Complete Interrupt Enable
//
// Data Configuration Register (DCR)
#define NE_DCR_WTS 0x01 // Word Transfer Select
#define NE_DCR_BOS 0x02 // Byte Order Select
#define NE_DCR_LAS 0x04 // Long Address Select
#define NE_DCR_LS 0x08 // Loopback Select
#define NE_DCR_AR 0x10 // Auto-initialize Remote
#define NE_DCR_FT0 0x20 // FIFO Threshold Select 0
#define NE_DCR_FT1 0x40 // FIFO Threshold Select 1
//
// Transmit Configuration Register (TCR)
#define NE_TCR_CRC 0x01 // Inhibit CRC
#define NE_TCR_LB0 0x02 // Loopback Control 0
#define NE_TCR_LB1 0x04 // Loopback Control 1
#define NE_TCR_ATD 0x08 // Auto Transmit Disable
#define NE_TCR_OFST 0x10 // Collision Offset Enable
//
// Transmit Status Register (TSR)
#define NE_TSR_PTX 0x01 // Packet Transmitted
#define NE_TSR_COL 0x04 // Transmit Collided
#define NE_TSR_ABT 0x08 // Transmit Aborted
#define NE_TSR_CRS 0x10 // Carrier Sense Lost
#define NE_TSR_FU 0x20 // FIFO Underrun
#define NE_TSR_CDH 0x40 // CD Heartbeat
#define NE_TSR_OWC 0x80 // Out of Window Collision
//
// Receiver Configuration Register (RCR)
//
#define NE_RCR_SEP 0x01 // Save Errored Packets
#define NE_RCR_AR 0x02 // Accept Runt packet
#define NE_RCR_AB 0x04 // Accept Broadcast
#define NE_RCR_AM 0x08 // Accept Multicast
#define NE_RCR_PRO 0x10 // Promiscuous Physical
#define NE_RCR_MON 0x20 // Monitor Mode
//
// Receiver Status Register (RSR)
#define NE_RSR_PRX 0x01 // Packet Received Intact
#define NE_RSR_CRC 0x02 // CRC Error
#define NE_RSR_FAE 0x04 // Frame Alignment Error
#define NE_RSR_FO 0x08 // FIFO Overrun
#define NE_RSR_MPA 0x10 // Missed Packet
#define NE_RSR_PHY 0x20 // Physical Address
#define NE_RSR_DIS 0x40 // Receiver Disabled
#define NE_RSR_DFR 0x80 // Deferring
//
// Novell NE2000
//
#define NE_NOVELL_NIC_OFFSET 0x00
#define NE_NOVELL_ASIC_OFFSET 0x10
#define NE_NOVELL_DATA 0x00
#define NE_NOVELL_RESET 0x0F
#define NE_PAGE_SIZE 256 // Size of RAM pages in bytes
#define NE_TXBUF_SIZE 6 // Size of TX buffer in pages
#define NE_TX_BUFERS 2 // Number of transmit buffers
#define NE_TIMEOUT 10000
#define NE_TXTIMEOUT 30000
//
// Receive ring descriptor
//
struct recv_ring_desc {
unsigned char rsr; // Receiver status
unsigned char next_pkt; // Pointer to next packet
unsigned short count; // Bytes in packet (length + 4)
};
//
// NE2000 NIC status
//
struct ne {
dev_t devno; // Device number
struct eth_addr hwaddr; // MAC address
unsigned short iobase; // Configured I/O base
unsigned short irq; // Configured IRQ
unsigned short membase; // Configured memory base
unsigned short memsize; // Configured memory size
unsigned short asic_addr; // ASIC I/O bus address
unsigned short nic_addr; // NIC (DP8390) I/O bus address
struct interrupt intr; // Interrupt object for driver
struct dpc dpc; // DPC for driver
unsigned short rx_ring_start; // Start address of receive ring
unsigned short rx_ring_end; // End address of receive ring
unsigned char rx_page_start; // Start of receive ring
unsigned char rx_page_stop; // End of receive ring
unsigned char next_pkt; // Next unread received packet
struct event rdc; // Remote DMA completed event
struct event ptx; // Packet transmitted event
struct mutex txlock; // Transmit lock
};
struct netstats *netstats;
static void ne_readmem(struct ne *ne, unsigned short src, void *dst, unsigned short len) {
// Word align length
if (len & 1) len++;
// Abort any remote DMA already in progress
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STA);
// Setup DMA byte count
outp(ne->nic_addr + NE_P0_RBCR0, (unsigned char) len);
outp(ne->nic_addr + NE_P0_RBCR1, (unsigned char) (len >> 8));
// Setup NIC memory source address
outp(ne->nic_addr + NE_P0_RSAR0, (unsigned char) src);
outp(ne->nic_addr + NE_P0_RSAR1, (unsigned char) (src >> 8));
// Select remote DMA read
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD0 | NE_CR_STA);
// Read NIC memory
insw(ne->asic_addr + NE_NOVELL_DATA, dst, len >> 1);
}
static int ne_probe(struct ne *ne) {
unsigned char byte;
// Reset
byte = inp(ne->asic_addr + NE_NOVELL_RESET);
outp(ne->asic_addr + NE_NOVELL_RESET, byte);
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STP);
//msleep(5000);
msleep(100);
// Test for a generic DP8390 NIC
byte = inp(ne->nic_addr + NE_P0_CR);
byte &= NE_CR_RD2 | NE_CR_TXP | NE_CR_STA | NE_CR_STP;
if (byte != (NE_CR_RD2 | NE_CR_STP)) return 0;
byte = inp(ne->nic_addr + NE_P0_ISR);
byte &= NE_ISR_RST;
if (byte != NE_ISR_RST) return 0;
return 1;
}
void ne_get_packet(struct ne *ne, unsigned short src, char *dst, unsigned short len) {
if (src + len > ne->rx_ring_end) {
unsigned short split = ne->rx_ring_end - src;
ne_readmem(ne, src, dst, split);
len -= split;
src = ne->rx_ring_start;
dst += split;
}
ne_readmem(ne, src, dst, len);
}
void ne_receive(struct ne *ne)
{
struct recv_ring_desc packet_hdr;
unsigned short packet_ptr;
unsigned short len;
unsigned char bndry;
struct pbuf *p, *q;
int rc;
// Set page 1 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_PAGE_1 | NE_CR_RD2 | NE_CR_STA);
while (ne->next_pkt != inp(ne->nic_addr + NE_P1_CURR)) {
// Get pointer to buffer header structure
packet_ptr = ne->next_pkt * NE_PAGE_SIZE;
// Read receive ring descriptor
ne_readmem(ne, packet_ptr, &packet_hdr, sizeof(struct recv_ring_desc));
// Allocate packet buffer
len = packet_hdr.count - sizeof(struct recv_ring_desc);
p = pbuf_alloc(PBUF_RAW, len, PBUF_RW);
// Get packet from nic and send to upper layer
if (p != NULL) {
packet_ptr += sizeof(struct recv_ring_desc);
for (q = p; q != NULL; q = q->next) {
ne_get_packet(ne, packet_ptr, q->payload, (unsigned short) q->len);
packet_ptr += q->len;
}
//kprintf("ne2000: received packet, %d bytes\n", len);
rc = dev_receive(ne->devno, p);
if (rc < 0) {
kprintf("ne2000: error %d processing packet\n", rc);
pbuf_free(p);
}
} else {
// Drop packet
kprintf("ne2000: packet dropped\n");
netstats->link.memerr++;
netstats->link.drop++;
}
// Update next packet pointer
ne->next_pkt = packet_hdr.next_pkt;
// Set page 0 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_PAGE_0 | NE_CR_RD2 | NE_CR_STA);
// Update boundry pointer
bndry = ne->next_pkt - 1;
if (bndry < ne->rx_page_start) bndry = ne->rx_page_stop - 1;
outp(ne->nic_addr + NE_P0_BNRY, bndry);
//kprintf("start: %02x stop: %02x next: %02x bndry: %02x\n", ne->rx_page_start, ne->rx_page_stop, ne->next_pkt, bndry);
// Set page 1 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_PAGE_1 | NE_CR_RD2 | NE_CR_STA);
}
}
void ne_dpc(void *arg) {
struct ne *ne = arg;
unsigned char isr;
//kprintf("ne2000: dpc\n");
// Select page 0
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STA);
// Loop until there are no pending interrupts
while ((isr = inp(ne->nic_addr + NE_P0_ISR)) != 0) {
// Reset bits for interrupts being acknowledged
outp(ne->nic_addr + NE_P0_ISR, isr);
// Packet received
if (isr & NE_ISR_PRX) {
//kprintf("ne2000: new packet arrived\n");
ne_receive(ne);
}
// Packet transmitted
if (isr & NE_ISR_PTX) {
//kprintf("ne2000: packet transmitted\n");
set_event(&ne->ptx);
}
// Remote DMA complete
if (isr & NE_ISR_RDC) {
//kprintf("ne2000: remote DMA complete\n");
set_event(&ne->rdc);
}
// Select page 0
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STA);
}
eoi(ne->irq);
}
int ne_handler(struct context *ctxt, void *arg) {
struct ne *ne = (struct ne *) arg;
// Queue DPC to service interrupt
queue_irq_dpc(&ne->dpc, ne_dpc, ne);
return 0;
}
int ne_transmit(struct dev *dev, struct pbuf *p) {
struct ne *ne = dev->privdata;
unsigned short dma_len;
unsigned short dst;
unsigned char *data;
int len;
int wrap;
unsigned char save_byte[2];
struct pbuf *q;
//kprintf("ne_transmit: transmit packet len=%d\n", p->tot_len);
// Get transmit lock
if (wait_for_object(&ne->txlock, NE_TXTIMEOUT) < 0) {
kprintf(KERN_WARNING "ne2000: timeout waiting for tx lock\n");
return -ETIMEOUT;
}
// We need to transfer a whole number of words
dma_len = p->tot_len;
if (dma_len & 1) dma_len++;
// Clear packet transmitted and dma complete event
reset_event(&ne->ptx);
reset_event(&ne->rdc);
// Set page 0 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STA);
// Reset remote DMA complete flag
outp(ne->nic_addr + NE_P0_ISR, NE_ISR_RDC);
// Set up DMA byte count
outp(ne->nic_addr + NE_P0_RBCR0, (unsigned char) dma_len);
outp(ne->nic_addr + NE_P0_RBCR1, (unsigned char) (dma_len >> 8));
// Set up destination address in NIC memory
dst = ne->rx_page_stop; // for now we only use one tx buffer
outp(ne->nic_addr + NE_P0_RSAR0, (dst * NE_PAGE_SIZE));
outp(ne->nic_addr + NE_P0_RSAR1, (dst * NE_PAGE_SIZE) >> 8);
// Set remote DMA write
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD1 | NE_CR_STA);
wrap = 0;
for (q = p; q != NULL; q = q->next) {
len = q->len;
if (len > 0) {
data = q->payload;
// Finish the last word
if (wrap) {
save_byte[1] = *data;
outpw((unsigned short) (ne->asic_addr + NE_NOVELL_DATA), *(unsigned short *) save_byte);
data++;
len--;
wrap = 0;
}
// Output contiguous words
if (len > 1) {
outsw(ne->asic_addr + NE_NOVELL_DATA, data, len >> 1);
data += len & ~1;
len &= 1;
}
// Save last byte if necessary
if (len == 1) {
save_byte[0] = *data;
wrap = 1;
}
}
}
// Output last byte
if (wrap) {
outpw((unsigned short) (ne->asic_addr + NE_NOVELL_DATA), *(unsigned short *) save_byte);
}
// Wait for remote DMA complete
if (wait_for_object(&ne->rdc, NE_TIMEOUT) < 0) {
kprintf(KERN_WARNING "ne2000: timeout waiting for remote dma to complete\n");
release_mutex(&ne->txlock);
return -EIO;
}
// Set TX buffer start page
outp(ne->nic_addr + NE_P0_TPSR, (unsigned char) dst);
// Set TX length (packets smaller than 64 bytes must be padded)
if (p->tot_len > 64) {
outp(ne->nic_addr + NE_P0_TBCR0, p->tot_len);
outp(ne->nic_addr + NE_P0_TBCR1, p->tot_len >> 8);
} else {
outp(ne->nic_addr + NE_P0_TBCR0, 64);
outp(ne->nic_addr + NE_P0_TBCR1, 0);
}
// Set page 0 registers, transmit packet, and start
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_TXP | NE_CR_STA);
// Wait for packet transmitted
if (wait_for_object(&ne->ptx, NE_TIMEOUT) < 0) {
kprintf(KERN_WARNING "ne2000: timeout waiting for packet transmit\n");
release_mutex(&ne->txlock);
return -EIO;
}
//kprintf("ne_transmit: packet transmitted\n");
pbuf_free(p);
release_mutex(&ne->txlock);
return 0;
}
int ne_ioctl(struct dev *dev, int cmd, void *args, size_t size) {
return -ENOSYS;
}
int ne_attach(struct dev *dev, struct eth_addr *hwaddr) {
struct ne *ne = dev->privdata;
*hwaddr = ne->hwaddr;
return 0;
}
int ne_detach(struct dev *dev) {
return 0;
}
struct driver ne_driver = {
"ne2000",
DEV_TYPE_PACKET,
ne_ioctl,
NULL,
NULL,
ne_attach,
ne_detach,
ne_transmit
};
int ne_setup(unsigned short iobase, int irq, unsigned short membase, unsigned short memsize, struct unit *unit) {
struct ne *ne;
unsigned char romdata[16];
int i;
char str[20];
// Allocate device structure
ne = (struct ne *) kmalloc(sizeof(struct ne));
if (!ne) return -ENOMEM;
memset(ne, 0, sizeof(struct ne));
// Setup NIC configuration
ne->iobase = iobase;
ne->irq = irq;
ne->membase = membase;
ne->memsize = memsize;
ne->nic_addr = ne->iobase + NE_NOVELL_NIC_OFFSET;
ne->asic_addr = ne->iobase + NE_NOVELL_ASIC_OFFSET;
ne->rx_page_start = ne->membase / NE_PAGE_SIZE;
ne->rx_page_stop = ne->rx_page_start + (ne->memsize / NE_PAGE_SIZE) - NE_TXBUF_SIZE * NE_TX_BUFERS;
ne->next_pkt = ne->rx_page_start + 1;
ne->rx_ring_start = ne->rx_page_start * NE_PAGE_SIZE;
ne->rx_ring_end = ne->rx_page_stop * NE_PAGE_SIZE;
// Probe for NE2000 card
if (!ne_probe(ne)) return 0;
// Initialize network interface
init_event(&ne->ptx, 0, 0);
init_event(&ne->rdc, 0, 0);
init_mutex(&ne->txlock, 0);
// Install interrupt handler
init_dpc(&ne->dpc);
register_interrupt(&ne->intr, IRQ2INTR(ne->irq), ne_handler, ne);
enable_irq(ne->irq);
// Set page 0 registers, abort remote DMA, stop NIC
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STP);
// Set FIFO threshold to 8, no auto-init remote DMA, byte order=80x86, word-wide DMA transfers
outp(ne->nic_addr + NE_P0_DCR, NE_DCR_FT1 | NE_DCR_WTS | NE_DCR_LS);
// Get Ethernet MAC address
ne_readmem(ne, 0, romdata, 16);
for (i = 0; i < ETHER_ADDR_LEN; i++) ne->hwaddr.addr[i] = romdata[i * 2];
// Set page 0 registers, abort remote DMA, stop NIC
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STP);
// Clear remote byte count registers
outp(ne->nic_addr + NE_P0_RBCR0, 0);
outp(ne->nic_addr + NE_P0_RBCR1, 0);
// Initialize receiver (ring-buffer) page stop and boundry
outp(ne->nic_addr + NE_P0_PSTART, ne->rx_page_start);
outp(ne->nic_addr + NE_P0_PSTOP, ne->rx_page_stop);
outp(ne->nic_addr + NE_P0_BNRY, ne->rx_page_start);
// Enable the following interrupts: receive/transmit complete, receive/transmit error,
// receiver overwrite and remote dma complete.
outp(ne->nic_addr + NE_P0_IMR, NE_IMR_PRXE | NE_IMR_PTXE | NE_IMR_RXEE | NE_IMR_TXEE | NE_IMR_OVWE | NE_IMR_RDCE);
// Set page 1 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_PAGE_1 | NE_CR_RD2 | NE_CR_STP);
// Copy out our station address
for (i = 0; i < ETHER_ADDR_LEN; i++) outp(ne->nic_addr + NE_P1_PAR0 + i, ne->hwaddr.addr[i]);
// Set current page pointer
outp(ne->nic_addr + NE_P1_CURR, ne->next_pkt);
// Initialize multicast address hashing registers to not accept multicasts
for (i = 0; i < 8; i++) outp(ne->nic_addr + NE_P1_MAR0 + i, 0);
// Set page 0 registers
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STP);
// Accept broadcast packets
outp(ne->nic_addr + NE_P0_RCR, NE_RCR_AB);
// Take NIC out of loopback
outp(ne->nic_addr + NE_P0_TCR, 0);
// Clear any pending interrupts
outp(ne->nic_addr + NE_P0_ISR, 0xFF);
// Start NIC
outp(ne->nic_addr + NE_P0_CR, NE_CR_RD2 | NE_CR_STA);
// Create packet device
ne->devno = dev_make("eth#", &ne_driver, unit, ne);
kprintf(KERN_INFO "%s: NE2000 iobase 0x%x irq %d mac %s\n", device(ne->devno)->name, ne->iobase, ne->irq, ether2str(&ne->hwaddr, str));
return 0;
}
int __declspec(dllexport) install(struct unit *unit, char *opts) {
unsigned short iobase = 0x280;
int irq = 9;
unsigned short membase = 16 * 1024;
unsigned short memsize = 16 * 1024;
struct resource *memres;
if (unit) {
iobase = (unsigned short) get_unit_iobase(unit);
irq = get_unit_irq(unit);
memres = get_unit_resource(unit, RESOURCE_MEM, 0);
if (memres) {
membase = (unsigned short) memres->start;
memsize = (unsigned short) memres->len;
}
}
iobase = get_num_option(opts, "iobase", iobase);
irq = get_num_option(opts, "irq", irq);
membase = get_num_option(opts, "membase", membase);
memsize = get_num_option(opts, "memsize", memsize);
return ne_setup(iobase, irq, membase, memsize, unit);
}
int __stdcall start(hmodule_t hmod, int reason, void *reserved2) {
netstats = get_netstats();
return 1;
}