📄 lance.c
字号:
/**************************************************************************Etherboot - BOOTP/TFTP Bootstrap ProgramLANCE NIC driver for EtherbootLarge portions borrowed from the Linux LANCE driver by Donald BeckerKen Yap, July 1997***************************************************************************//* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. *//* to get some global routines like printf */#include "etherboot.h"/* to get the interface to the body of the program */#include "nic.h"#ifdef INCLUDE_LANCE#include "pci.h"#endif#include "cards.h"/* Offsets from base I/O address */#if defined(INCLUDE_NE2100) || defined(INCLUDE_LANCE)#define LANCE_ETH_ADDR 0x0#define LANCE_DATA 0x10#define LANCE_ADDR 0x12#define LANCE_RESET 0x14#define LANCE_BUS_IF 0x16#define LANCE_TOTAL_SIZE 0x18#endif#ifdef INCLUDE_NI6510#define LANCE_ETH_ADDR 0x8#define LANCE_DATA 0x0#define LANCE_ADDR 0x2#define LANCE_RESET 0x4#define LANCE_BUS_IF 0x6#define LANCE_TOTAL_SIZE 0x10#endif/* lance_poll() now can use multiple Rx buffers to prevent packet loss. Set * Set LANCE_LOG_RX_BUFFERS to 0..7 for 1, 2, 4, 8, 16, 32, 64 or 128 Rx * buffers. Usually 4 (=16 Rx buffers) is a good value. (Andreas Neuhaus) * Decreased to 2 (=4 Rx buffers) (Ken Yap, 20010305) */#define LANCE_LOG_RX_BUFFERS 2 /* Use 2^2=4 Rx buffers */#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)struct lance_init_block{ unsigned short mode; unsigned char phys_addr[ETH_ALEN]; unsigned long filter[2]; Address rx_ring; Address tx_ring;};struct lance_rx_head{ union { Address base; unsigned char addr[4]; } u; short buf_length; /* 2s complement */ short msg_length;};struct lance_tx_head{ union { Address base; unsigned char addr[4]; } u; short buf_length; /* 2s complement */ short misc;};struct lance_interface{ struct lance_init_block init_block; struct lance_rx_head rx_ring[RX_RING_SIZE]; struct lance_tx_head tx_ring; unsigned char rbuf[RX_RING_SIZE][ETH_FRAME_LEN+4]; unsigned char tbuf[ETH_FRAME_LEN]; /* * Do not alter the order of the struct members above; * the hardware depends on the correct alignment. */ int rx_idx;};#define LANCE_MUST_PAD 0x00000001#define LANCE_ENABLE_AUTOSELECT 0x00000002#define LANCE_SELECT_PHONELINE 0x00000004#define LANCE_MUST_UNRESET 0x00000008/* A mapping from the chip ID number to the part number and features. These are from the datasheets -- in real life the '970 version reportedly has the same ID as the '965. */static const struct lance_chip_type{ int id_number; const char *name; int flags;} chip_table[] = { {0x0000, "LANCE 7990", /* Ancient lance chip. */ LANCE_MUST_PAD + LANCE_MUST_UNRESET}, {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */ LANCE_ENABLE_AUTOSELECT}, {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */ LANCE_ENABLE_AUTOSELECT}, {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */ LANCE_ENABLE_AUTOSELECT}, /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call it the PCnet32. */ {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */ LANCE_ENABLE_AUTOSELECT}, {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */ LANCE_ENABLE_AUTOSELECT}, {0x2625, "PCnet-FAST III 79C973", /* 79C973 PCInet-FAST III. */ LANCE_ENABLE_AUTOSELECT}, {0x2626, "PCnet/HomePNA 79C978", LANCE_ENABLE_AUTOSELECT|LANCE_SELECT_PHONELINE}, {0x0, "PCnet (unknown)", LANCE_ENABLE_AUTOSELECT},};/* Define a macro for converting program addresses to real addresses */#undef virt_to_bus#define virt_to_bus(x) ((unsigned long)x)static int chip_version;static int lance_version;static unsigned short ioaddr;#ifndef INCLUDE_LANCEstatic int dma;#endifstatic struct lance_interface *lp;/* additional 8 bytes for 8-byte alignment space */#ifdef USE_LOWMEM_BUFFER#define lance ((char *)0x10000 - (sizeof(struct lance_interface)+8))#elsestatic char lance[sizeof(struct lance_interface)+8];#endif#ifndef INCLUDE_LANCE/* DMA defines and helper routines *//* DMA controller registers */#define DMA1_CMD_REG 0x08 /* command register (w) */#define DMA1_STAT_REG 0x08 /* status register (r) */#define DMA1_REQ_REG 0x09 /* request register (w) */#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */#define DMA1_MODE_REG 0x0B /* mode register (w) */#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */#define DMA1_RESET_REG 0x0D /* Master Clear (w) */#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */#define DMA2_CMD_REG 0xD0 /* command register (w) */#define DMA2_STAT_REG 0xD0 /* status register (r) */#define DMA2_REQ_REG 0xD2 /* request register (w) */#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */#define DMA2_MODE_REG 0xD6 /* mode register (w) */#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */#define DMA2_RESET_REG 0xDA /* Master Clear (w) */#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only *//* enable/disable a specific DMA channel */static void enable_dma(unsigned int dmanr){ if (dmanr <= 3) outb_p(dmanr, DMA1_MASK_REG); else outb_p(dmanr & 3, DMA2_MASK_REG);}static void disable_dma(unsigned int dmanr){ if (dmanr <= 3) outb_p(dmanr | 4, DMA1_MASK_REG); else outb_p((dmanr & 3) | 4, DMA2_MASK_REG);}/* set mode (above) for a specific DMA channel */static void set_dma_mode(unsigned int dmanr, char mode){ if (dmanr <= 3) outb_p(mode | dmanr, DMA1_MODE_REG); else outb_p(mode | (dmanr&3), DMA2_MODE_REG);}#endif /* !INCLUDE_LANCE *//**************************************************************************RESET - Reset adapter***************************************************************************/static void lance_reset(struct nic *nic){ int i; Address l; /* Reset the LANCE */ (void)inw(ioaddr+LANCE_RESET); /* Un-Reset the LANCE, needed only for the NE2100 */ if (chip_table[lance_version].flags & LANCE_MUST_UNRESET) outw(0, ioaddr+LANCE_RESET); if (chip_table[lance_version].flags & LANCE_ENABLE_AUTOSELECT) { /* This is 79C960 specific; Turn on auto-select of media (AUI, BNC). */ outw(0x2, ioaddr+LANCE_ADDR); /* Don't touch 10base2 power bit. */ outw(inw(ioaddr+LANCE_BUS_IF) | 0x2, ioaddr+LANCE_BUS_IF); } /* HomePNA cards need to explicitly pick the phoneline interface. * Some of these cards have ethernet interfaces as well, this * code might require some modification for those. */ if (chip_table[lance_version].flags & LANCE_SELECT_PHONELINE) { short media, check ; /* this is specific to HomePNA cards... */ outw(49, ioaddr+0x12) ; media = inw(ioaddr+0x16) ;#ifdef DEBUG printf("media was %d\n", media) ;#endif media &= ~3 ; media |= 1 ;#ifdef DEBUG printf("media changed to %d\n", media) ;#endif media &= ~3 ; media |= 1 ; outw(49, ioaddr+0x12) ; outw(media, ioaddr+0x16) ; outw(49, ioaddr+0x12) ; check = inw(ioaddr+0x16) ;#ifdef DEBUG printf("check %s, media was set properly\n", check == media ? "passed" : "FAILED" ) ; #endif } /* Re-initialise the LANCE, and start it when done. */ /* Set station address */ for (i = 0; i < ETH_ALEN; ++i) lp->init_block.phys_addr[i] = nic->node_addr[i]; /* Preset the receive ring headers */ for (i=0; i<RX_RING_SIZE; i++) { lp->rx_ring[i].buf_length = -ETH_FRAME_LEN-4; /* OWN */ lp->rx_ring[i].u.base = virt_to_bus(lp->rbuf[i]) & 0xffffff; /* we set the top byte as the very last thing */ lp->rx_ring[i].u.addr[3] = 0x80; } lp->rx_idx = 0; lp->init_block.mode = 0x0; /* enable Rx and Tx */ l = (Address)virt_to_bus(&lp->init_block); outw(0x1, ioaddr+LANCE_ADDR); (void)inw(ioaddr+LANCE_ADDR); outw((short)l, ioaddr+LANCE_DATA); outw(0x2, ioaddr+LANCE_ADDR); (void)inw(ioaddr+LANCE_ADDR); outw((short)(l >> 16), ioaddr+LANCE_DATA); outw(0x4, ioaddr+LANCE_ADDR); (void)inw(ioaddr+LANCE_ADDR); outw(0x915, ioaddr+LANCE_DATA); outw(0x0, ioaddr+LANCE_ADDR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -