📄 lnc.c
字号:
/*- * Copyright (c) 1994-1998 * Paul Richards. 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, * verbatim and that no modifications are made prior to this * point in the file. * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Richards. * 4. The name Paul Richards may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``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 PAUL RICHARDS 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_lnc.c,v 1.51.2.1 1999/01/31 00:57:46 paul Exp $ *//* * Ported to Roadrunner by Frank W. Miller */#include <bus/isa.h>#include <bus/pci.h>#include <dev/lnc.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/Am7990.h>#include <sys/i8259.h>#include <sys/intr.h>#include <sys/mem.h>/* XXX Need to get a few more of these in */#define PCI_DEVICE 0x2000/* Board types */#define UNKNOWN 0#define NE2100 1/* Chip types */#define LANCE 1 /* Am7990 */#define C_LANCE 2 /* Am79C90 */#define PCnet_ISA 3 /* Am79C960 */#define PCnet_ISAplus 4 /* Am79C961 */#define PCnet_ISA_II 5 /* Am79C961A */#define PCnet_32 6 /* Am79C965 */#define PCnet_PCI 7 /* Am79C970 */#define PCnet_PCI_II 8 /* Am79C970A */#define PCnet_FAST 9 /* Am79C971 */#define PCnet_FASTplus 10 /* Am79C972 *//* CSR88-89: Chip ID masks */#define AMD_MASK 0x003#define PART_MASK 0xffff#define Am79C960 0x0003#define Am79C961 0x2260#define Am79C961A 0x2261#define Am79C965 0x2430#define Am79C970 0x0242#define Am79C970A 0x2621#define Am79C971 0x2623#define Am79C972 0x2624/* NE2100 port addresses */#define NE2100_IOSIZE 24#define PCNET_RDP 0x10 /* Register data port */#define PCNET_RAP 0x12 /* Register address port */#define PCNET_RESET 0x14#define PCNET_BDP 0x16#define PCNET_VSW 0x18/* struct nic_info mem_mode values */#define DMA_FIXED 0x01#define DMA_MBUF 0x02#define SHMEM 0x04/* Descriptor ring entries */#define NRDRE 3#define NTDRE 3/* Packet buffer sizes (rounded to nearest dword boundary */#define RECVBUFSIZE 1518#define TRANSBUFSIZE 1518/* Number of descriptors */#define NDESC(N) (1 << (N))#define INC_MD_PTR(PTR, ENTRIES) \ if (++(PTR) >= NDESC(ENTRIES)) \ PTR = 0#define DEC_MD_PTR(PTR, ENTRIES) \ if (--(PTR) < 0) \ PTR = NDESC(ENTRIES) - 1;#define MULTICAST_FILTER_LEN 8#define FCS_LEN 4static char *ic_ident[] = { "Unknown", "LANCE", "C-LANCE", "PCnet-ISA", "PCnet-ISA+", "PCnet-ISA II", "PCnet-32 VL-Bus", "PCnet-PCI", "PCnet-PCI II", "PCnet-FAST", "PCnet-FAST+"};struct nic_info { int ident; /* Type of card */ int ic; /* Type of chip */ int mem_mode; int mode; /* Mode setting at initialization */};struct host_ring_entry { struct mds *md; union { buf_t b; char *data; } buff;};struct lnc_softc { struct isa_device isa_dev; /* ISA bus parameters */ etheraddr hwaddr; /* Ethernet MAC address */ struct nic_info nic; u_short rdp; /* Register data port */ u_short rap; /* Register address port */ int nrdre; int lnc_mem_size; struct host_ring_entry *recv_ring; int recv_next; int ntdre; struct host_ring_entry *trans_ring; int trans_next; int next_to_send; int pending_transmits; struct init_block *init_block; struct bufq sndq; /* Send queue */};static struct lnc_softc sc;static __inline u_shortread_csr(u_short port){ outw(sc.rap, port); return inw(sc.rdp);}static __inline voidwrite_csr(u_short port, u_short val){ outw(sc.rap, port); outw(sc.rdp, val);}static intlance_probe(){ write_csr(CSR0, STOP); if ((read_csr(CSR0) & STOP) && !(read_csr(CSR3))) { /* * Check to see if it's a C-LANCE. For the LANCE the INEA bit * cannot be set while the STOP bit is. This restriction is * removed for the C-LANCE. */ write_csr(CSR0, INEA); if (read_csr(CSR0) & INEA) return C_LANCE; else return LANCE; } else return UNKNOWN;}static intpcnet_probe(){ u_long chip_id; int type; /* * The PCnet family doesn't reset the RAP register on reset so we * have to write during the probe. It does have an ID register * though so the probe is just a matter of reading it. */ if ((type = lance_probe(sc))) { chip_id = (read_csr(CSR89) << 16) | read_csr(CSR88); if (chip_id & AMD_MASK) { chip_id >>= 12; switch (chip_id & PART_MASK) { case Am79C960: return PCnet_ISA; case Am79C961: return PCnet_ISAplus; case Am79C961A: return PCnet_ISA_II; case Am79C965: return PCnet_32; case Am79C970: return PCnet_PCI; case Am79C970A: return PCnet_PCI_II; case Am79C971: return PCnet_FAST; case Am79C972: return PCnet_FASTplus; default: break; } } } return type;}static intne2100_probe(){ int i; sc.rap = sc.isa_dev.id_iobase + PCNET_RAP; sc.rdp = sc.isa_dev.id_iobase + PCNET_RDP; sc.nic.ic = pcnet_probe(sc); if (sc.nic.ic > 0) { sc.nic.ident = NE2100; sc.nic.mem_mode = DMA_FIXED; sc.nic.mode = 0x8000; /* Promiscuous */ /* XXX - For now just use the defines */ sc.nrdre = NRDRE; sc.ntdre = NTDRE; /* Extract MAC address from PROM */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc.hwaddr[i] = inb(sc.isa_dev.id_iobase + i); return NE2100_IOSIZE; } return 0;}static intlnc_send_packet(buf_t b, char *buf){ bcopy(bstart(b), buf, blen(b)); return blen(b);}static voidlnc_start(){ buf_t b; struct host_ring_entry *desc; int len; do { disable; b = bdeq(&(sc.sndq)); enable; if (b == NULL) return; sc.pending_transmits++; desc = sc.trans_ring + sc.next_to_send; len = lnc_send_packet(b, desc->buff.data); desc->md->md3 = 0; desc->md->md2 = -len; desc->md->md1 |= OWN | STP | ENP; INC_MD_PTR(sc.next_to_send, sc.ntdre); /* Force an immediate poll of the transmit ring */ write_csr(CSR0, TDMD | INEA); brel(b); } while (sc.pending_transmits < NDESC(sc.ntdre));}static voidlnc_stop(){ write_csr(CSR0, STOP);}static voidlnc_reset(){ buf_t b; char *lnc_mem; u_long lnc_mem_val; int i; /* Stop the device */ lnc_stop(); /* Empty send queue */ disable; for (;;) { b = bdeq(&(sc.sndq)); if (b == NULL) break; _bfree(b); } enable; bzero(sc.recv_ring, sc.lnc_mem_size); /* Setup receive and transmit rings */ sc.recv_next = 0; sc.trans_ring = sc.recv_ring + NDESC(sc.nrdre); sc.trans_next = 0; lnc_mem = (char *) (sc.trans_ring + NDESC(sc.ntdre)); lnc_mem = (char *) (((int) lnc_mem + 1) & ~1); sc.init_block = (struct init_block *) ((int) lnc_mem & ~1); lnc_mem = (char *) (sc.init_block + 1); lnc_mem = (char *) (((int) lnc_mem + 7) & ~7); /* Initialize pointers to descriptor entries */ for (i = 0; i < NDESC(sc.nrdre); i++) { (sc.recv_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } for (i = 0; i < NDESC(sc.ntdre); i++) { (sc.trans_ring + i)->md = (struct mds *) lnc_mem; lnc_mem += sizeof(struct mds); } /* Initialize descriptor ring entries */ for (i = 0; i < NDESC(sc.nrdre); i++) { lnc_mem_val = (u_long) lnc_mem; (sc.recv_ring + i)->md->md0 = (u_short) lnc_mem_val; (sc.recv_ring + i)->md->md1 = ((lnc_mem_val >> 16) & 0xff) | OWN; (sc.recv_ring + i)->md->md2 = -RECVBUFSIZE; (sc.recv_ring + i)->md->md3 = 0; (sc.recv_ring + i)->buff.data = lnc_mem; lnc_mem += RECVBUFSIZE; } for (i = 0; i < NDESC(sc.ntdre); i++) { lnc_mem_val = (u_long) lnc_mem; (sc.trans_ring + i)->md->md0 = lnc_mem_val; (sc.trans_ring + i)->md->md1 = ((lnc_mem_val >> 16) & 0xff); (sc.trans_ring + i)->md->md2 = 0; (sc.trans_ring + i)->md->md3 = 0; (sc.trans_ring + i)->buff.data = lnc_mem; lnc_mem += TRANSBUFSIZE; } sc.next_to_send = 0; /* Setup initialization block */ sc.init_block->mode = sc.nic.mode; for (i = 0; i < ETHER_ADDR_LEN; i++) sc.init_block->padr[i] = sc.hwaddr[i]; for (i = 0; i < MULTICAST_FILTER_LEN; i++) sc.init_block->ladrf[i] = 0xff; sc.init_block->rdra = (u_short) ((u_long) sc.recv_ring->md); sc.init_block->rlen = (u_short) (((((u_long) sc.recv_ring->md) >> 16) & 0xff) | (sc.nrdre << 13)); sc.init_block->tdra = (u_short) ((u_long) sc.trans_ring->md); sc.init_block->tlen = (u_short) (((((u_long) sc.trans_ring->md) >> 16) & 0xff) | (sc.ntdre << 13)); /* Give the initialization block to the LANCE */ write_csr(CSR1, (u_short) ((u_long) sc.init_block)); write_csr(CSR2, (((u_long) sc.init_block) >> 16) & 0xff); /* * Depending on which controller this is, CSR3 has different meanings. * For the Am7990 it controls DMA operations, for the Am79C960 it * controls interrupt masks and transmitter algorithms. In either * case, no flags are set. */ write_csr(CSR3, 0); /* Let's see if it starts... */ write_csr(CSR0, INIT); for (i = 0; i < 1000; i++) if (read_csr(CSR0) & IDON) break; if (read_csr(CSR0) & IDON) { /* Enable interrupts and start the LANCE transmitter */ write_csr(CSR0, STRT | INEA); lnc_start(); } else {#if _DEBUG kprintf("lnc_init: initialization failed\n");#endif }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -