📄 ne.c
字号:
/* * Copyright (c) 1995, David Greenman * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $Id: if_ed.c,v 1.148 1999/01/19 00:21:38 peter Exp $ *//* * Ported to Roadrunner by Frank W. Miller */#include <bus/isa.h>#include <dev/ne.h>#include <errno.h>#include <net/arp.h>#include <net/ether.h>#include <net/netif.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys.h>#include <sys/buf.h>#include <sys/i8259.h>#include <sys/intr.h>#include <sys/ne2000.h>#include <sys/time.h>#define MIN_PKT_SIZE 64struct ne_softc { struct isa_device isa_dev; /* ISA bus parameters */ u_char hwaddr[ETHER_ADDR_LEN]; /* Ethernet MAC address */ u_short asic_addr; /* ASIC I/O bus address */ u_short nic_addr; /* NIC (DP8390) I/O bus address */ u_short mem_start; /* NIC memory start address */ u_short mem_end; /* NIC memory end address */ u_short mem_size; /* Total NIC memory size */ u_short mem_ring; /* Start of RX ring-buffer */ u_char mem_shared; /* NIC memory shared with host */ u_char tx_busy; /* Transmitter busy */ u_char txb_cnt; /* Number of TX buffers */ u_char txb_inuse; /* Number of TX buffers in-use */ u_char txb_new; /* Ptr to where new buffer is added */ u_char txb_next_tx; /* Ptr to next buffer ready for TX */ u_short txb_len[8]; /* Buffered TX buffer lengths */ u_char tx_page_start; /* First page of TX buffer area */ u_char rec_page_start; /* First page of RX ring-buffer */ u_char rec_page_stop; /* Last page of RX ring-buffer */ u_char next_packet; /* Ptr to next unread RX packet */ struct bufq sndq; /* Send queue */};static struct ne_softc sc;static u_short pad[MIN_PKT_SIZE >> 1];static voidne_readmem(u_short src, u_short * dst, u_short len){ len = ALIGN(len, 2); /* Abort any remote DMA already in progress */ outb(sc.nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* Setup DMA byte count */ outb(sc.nic_addr + ED_P0_RBCR0, len); outb(sc.nic_addr + ED_P0_RBCR1, len >> 8); /* Setup NIC memory source address */ outb(sc.nic_addr + ED_P0_RSAR0, src); outb(sc.nic_addr + ED_P0_RSAR1, src >> 8); /* Select remote DMA read */ outb(sc.nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); /* Read NIC memory */ insw(sc.asic_addr + ED_NOVELL_DATA, dst, len >> 1);}static intne_probe(){ u_char byte; u_char romdata[16]; int i; /* Reset */ byte = inb(sc.asic_addr + ED_NOVELL_RESET); outb(sc.asic_addr + ED_NOVELL_RESET, byte); outb(sc.nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); DELAY(5000); /* Test for a generic DP8390 NIC */ byte = inb(sc.nic_addr + ED_P0_CR); byte &= ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP; if (byte != (ED_CR_RD2 | ED_CR_STP)) return 0; byte = inb(sc.nic_addr + ED_P0_ISR); byte &= ED_ISR_RST; if (byte != ED_ISR_RST) return 0; /* Assume a 16-bit adapter interface */ sc.mem_start = 16384; sc.mem_size = 16384; sc.mem_end = sc.mem_start + sc.mem_size; sc.tx_page_start = sc.mem_size / ED_PAGE_SIZE; /* Use two TX buffers */ sc.txb_cnt = 2; /* Setup RX ring */ sc.mem_ring = sc.mem_start + sc.txb_cnt * ED_PAGE_SIZE * ED_TXBUF_SIZE; sc.rec_page_start = sc.tx_page_start + sc.txb_cnt * ED_TXBUF_SIZE; sc.rec_page_stop = sc.tx_page_start + sc.mem_size / ED_PAGE_SIZE; /* Get Ethernet MAC address */ ne_readmem(0, (u_short *) romdata, 16); for (i = 0; i < ETHER_ADDR_LEN; i++) sc.hwaddr[i] = romdata[i * 2]; /* Clear any pending interrupts */ outb(sc.nic_addr + ED_P0_ISR, 0xff); return 1;}static voidne_recv_ring_copy(u_short src, u_short * dst, u_short len){ /* Check whether copy wraps to lower address in ring buffer */ if (src + len > sc.mem_end) { u_short tmp = sc.mem_end - src; ne_readmem(src, dst, tmp); len -= tmp; src = sc.mem_ring; dst += tmp; } ne_readmem(src, dst, len);}static buf_tne_get_packet(u_short src, u_short len){ buf_t b; disable; b = _bget(ETHER_PKT_LEN); enable; if (b == NULL) {#if _DEBUG kprintf("ne_get_packet: no buffers\n");#endif return NULL; } blen(b) = len; ne_recv_ring_copy(src, (u_short *) bstart(b), len); return b;}static voidne_rxisr(){ struct recv_ring_desc packet_hdr; u_short packet_ptr; buf_t b; u_char boundry; int len; /* Set page 1 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); while (sc.next_packet != inb(sc.nic_addr + ED_P1_CURR)) { /* Get ptr to buffer header structure */ packet_ptr = sc.mem_ring; packet_ptr += (sc.next_packet - sc.rec_page_start) * ED_PAGE_SIZE; /* Read receive ring descriptor */ ne_readmem(packet_ptr, (u_short *) & packet_hdr, sizeof(struct recv_ring_desc)); /* Check for ring pointer corruption */ if (packet_hdr.next_packet < sc.rec_page_start || packet_hdr.next_packet >= sc.rec_page_stop) {#if _DEBUG kprintf("ne_rxisr: receive ring corrupted\n");#endif return; } len = packet_hdr.count - sizeof(struct recv_ring_desc); b = ne_get_packet(packet_ptr + sizeof(struct recv_ring_desc), len); if (b != NULL) netif_input(b); /* Update next packet pointer */ sc.next_packet = packet_hdr.next_packet; /* Set page 0 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STA); /* Update boundry pointer */ boundry = sc.next_packet - 1; if (boundry < sc.rec_page_start) boundry = sc.rec_page_stop - 1; outb(sc.nic_addr + ED_P0_BNRY, boundry); /* Set page 1 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); }}static voidne_transmit(){ u_short len; len = sc.txb_len[sc.txb_next_tx]; /* Set page 0 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STA); /* Set TX buffer start page */ outb(sc.nic_addr + ED_P0_TPSR, sc.tx_page_start + sc.txb_next_tx * ED_TXBUF_SIZE); /* Set TX length */ outb(sc.nic_addr + ED_P0_TBCR0, len); outb(sc.nic_addr + ED_P0_TBCR1, len >> 8); /* Set page 0 registers, transmit packet, and start */ outb(sc.nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA); sc.tx_busy = 1; /* Point to next transmit buffer slot and wrap if necessary */ sc.txb_next_tx++; if (sc.txb_next_tx == sc.txb_cnt) sc.txb_next_tx = 0;}static voidne_isr(void *params){ u_char isr; /* Set page 0 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STA); /* Loop until there are no pending interrupts */ while ((isr = inb(sc.nic_addr + ED_P0_ISR)) != 0) { /* Reset bits for interrupts being acknowledged */ outb(sc.nic_addr + ED_P0_ISR, isr); /* TX interrupts */ if (isr & (ED_ISR_PTX | ED_ISR_TXE)) { if (isr & ED_ISR_TXE) kprintf("ne_isr: transmit error\n"); sc.tx_busy = 0; if (sc.txb_inuse && --sc.txb_inuse) ne_transmit(); } /* RX interrupts */ if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) { if (isr & ED_ISR_OVW) kprintf("ne_isr: receive ring buffer overflow\n"); else { if (isr & ED_ISR_RXE) kprintf("ne_isr: receive error\n"); /* Receive packet */ ne_rxisr(); } } /* Return NIC CR to known state */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STA); } /* XXX Hard coded for the moment */ outb(I8259_SLV_CTRL, 0x61); outb(I8259_MSTR_CTRL, I8259_EOI_CAS);}intne_init(){ ipaddr ip; char s[20]; int i; bzero((char *) pad, MIN_PKT_SIZE); sc.isa_dev.id_iobase = 0x300; sc.isa_dev.id_irq = 9; sc.isa_dev.id_maddr = 0xd8000; sc.asic_addr = sc.isa_dev.id_iobase + ED_NOVELL_ASIC_OFFSET; sc.nic_addr = sc.isa_dev.id_iobase + ED_NOVELL_NIC_OFFSET; if (!ne_probe()) {#if _DEBUG kprintf("ne_init: no adapter found\n");#endif return EFAIL; } ether2str(sc.hwaddr, s); kprintf("ne_init: NE2000 iobase 0x%x irq %d hwaddr %s\n", sc.isa_dev.id_iobase, sc.isa_dev.id_irq, s); bcopy(sc.hwaddr, netif_primary.hwaddr, ETHER_ADDR_LEN); netif_primary.state = NETIF_STATE_UP; str2ip(IPADDR, &ip); arp_insert(ARP_ENTRY_RESOLVED, ARP_TTL_INFINITE, ip, sc.hwaddr); binitq(&(sc.sndq)); isr_inst(IRQ2INTR(sc.isa_dev.id_irq), ne_isr, NULL); intr_unmask(IRQ2INTR(sc.isa_dev.id_irq)); sc.tx_busy = 0; sc.txb_inuse = 0; sc.txb_new = 0; sc.txb_next_tx = 0; sc.next_packet = sc.rec_page_start + 1; /* Set page 0 registers, abort remote DMA, stop NIC */ outb(sc.nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); /* * Set FIFO threshold to 8, no auto-init remote DMA, byte order=80x86, * word-wide DMA transfers */ outb(sc.nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS); /* Clear remote byte count registers */ outb(sc.nic_addr + ED_P0_RBCR0, 0); outb(sc.nic_addr + ED_P0_RBCR1, 0); /* Don't store incoming packets in memory for the moment */ outb(sc.nic_addr + ED_P0_RCR, ED_RCR_MON); /* Place NIC in internal loopback mode */ outb(sc.nic_addr + ED_P0_TCR, ED_TCR_LB0); /* Initialize transmit/receive (ring-buffer) page start */ outb(sc.nic_addr + ED_P0_TPSR, sc.tx_page_start); outb(sc.nic_addr + ED_P0_PSTART, sc.rec_page_start); /* Initialize receiver (ring-buffer) page stop and boundry */ outb(sc.nic_addr + ED_P0_PSTOP, sc.rec_page_stop); outb(sc.nic_addr + ED_P0_BNRY, sc.rec_page_start); /* Clear any pending interrupts */ outb(sc.nic_addr + ED_P0_ISR, 0xff); /* * Enable the following interrupts: receive/transmit complete, * receive/transmit error, and receiver overwrite. Counter overflow * and remote DMA complete are *NOT* enabled. */ outb(sc.nic_addr + ED_P0_IMR, ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE); /* Set page 1 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STP); /* Copy out our station address */ for (i = 0; i < ETHER_ADDR_LEN; i++) outb(sc.nic_addr + ED_P1_PAR0 + i, sc.hwaddr[i]); /* Set current page pointer to next_packet (initialized above) */ outb(sc.nic_addr + ED_P1_CURR, sc.next_packet); /* * Initialize multicast address hashing registers to not accept * multicasts */ for (i = 0; i < 8; ++i) outb(sc.nic_addr + ED_P1_MAR0 + i, 0); /* Set page 0 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STP); /* Accept broadcast packets */ outb(sc.nic_addr + ED_P0_RCR, ED_RCR_AB); /* Start NIC */ outb(sc.nic_addr + ED_P0_CR, ED_CR_STA); /* Take NIC out of loopback */ outb(sc.nic_addr + ED_P0_TCR, 0); return 0;}intne_shut(){ return ENOSYS;}intne_ioctl(int cmd, void *args){ return ENOSYS;}intne_read(buf_t * b){ return ENOSYS;}static u_shortne_writebufs(buf_t b, u_short dst){ u_short len; u_char byte; int max_wait = 500; /* About 240us */ len = ALIGN(blen(b), 2); /* Set page 0 registers */ outb(sc.nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* Reset remote DMA complete flag */ outb(sc.nic_addr + ED_P0_ISR, ED_ISR_RDC); /* Set up DMA byte count */ outb(sc.nic_addr + ED_P0_RBCR0, (len < MIN_PKT_SIZE ? MIN_PKT_SIZE : len)); outb(sc.nic_addr + ED_P0_RBCR1, len >> 8); /* Set up destination address in NIC memory */ outb(sc.nic_addr + ED_P0_RSAR0, dst); outb(sc.nic_addr + ED_P0_RSAR1, dst >> 8); /* Set remote DMA write */ outb(sc.nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); /* Write packet data */ outsw(sc.asic_addr + ED_NOVELL_DATA, (u_short *) bstart(b), len >> 1); if (len < MIN_PKT_SIZE) outsw(sc.asic_addr + ED_NOVELL_DATA, pad, (MIN_PKT_SIZE - len) >> 1); /* * Wait for remote DMA complete. This is necessary because on the * transmit side, data is handled internally by the NIC in bursts and * we can't start another remote DMA until this one completes. Not * waiting causes really bad things to happen - like the NIC * irrecoverably jamming the ISA bus. */ for (;;) { byte = inb(sc.nic_addr + ED_P0_ISR) & ED_ISR_RDC; if ((byte & ED_ISR_RDC) == ED_ISR_RDC) break; if (--max_wait == 0) break; } return (blen(b) < MIN_PKT_SIZE ? MIN_PKT_SIZE : blen(b));}#define MAX(A, B) ((A) > (B) ? (A) : (B))static voidne_start(){ buf_t b; u_short mem_ptr, len; for (;;) { if (sc.txb_inuse == sc.txb_cnt) return; disable; b = bdeq(&(sc.sndq)); enable; if (b == NULL) return; mem_ptr = sc.mem_start + (sc.txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); len = ne_writebufs(b, mem_ptr); if (len == 0) continue; brel(b); sc.txb_len[sc.txb_new] = len; sc.txb_inuse++; /* Point to next buffer slot and wrap if necessary */ sc.txb_new++; if (sc.txb_new == sc.txb_cnt) sc.txb_new = 0; if (sc.tx_busy == 0) ne_transmit(); }}intne_write(buf_t * b){ disable; benq(*b, &(sc.sndq)); enable; ne_start(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -