if_tl.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,302 行 · 第 1/5 页
C
2,302 行
/* * Copyright (c) 1997, 1998 * Bill Paul <wpaul@ctr.columbia.edu>. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD * 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_tl.c,v 1.24.2.5 1999/05/06 15:39:38 wpaul Exp $ *//* * Texas Instruments ThunderLAN driver for FreeBSD 2.2.6 and 3.x. * Supports many Compaq PCI NICs based on the ThunderLAN ethernet controller, * the National Semiconductor DP83840A physical interface and the * Microchip Technology 24Cxx series serial EEPROM. * * Written using the following four documents: * * Texas Instruments ThunderLAN Programmer's Guide (www.ti.com) * National Semiconductor DP83840A data sheet (www.national.com) * Microchip Technology 24C02C data sheet (www.microchip.com) * Micro Linear ML6692 100BaseTX only PHY data sheet (www.microlinear.com) * * Written by Bill Paul <wpaul@ctr.columbia.edu> * Electrical Engineering Department * Columbia University, New York City *//* * Some notes about the ThunderLAN: * * The ThunderLAN controller is a single chip containing PCI controller * logic, approximately 3K of on-board SRAM, a LAN controller, and media * independent interface (MII) bus. The MII allows the ThunderLAN chip to * control up to 32 different physical interfaces (PHYs). The ThunderLAN * also has a built-in 10baseT PHY, allowing a single ThunderLAN controller * to act as a complete ethernet interface. * * Other PHYs may be attached to the ThunderLAN; the Compaq 10/100 cards * use a National Semiconductor DP83840A PHY that supports 10 or 100Mb/sec * in full or half duplex. Some of the Compaq Deskpro machines use a * Level 1 LXT970 PHY with the same capabilities. Certain Olicom adapters * use a Micro Linear ML6692 100BaseTX only PHY, which can be used in * concert with the ThunderLAN's internal PHY to provide full 10/100 * support. This is cheaper than using a standalone external PHY for both * 10/100 modes and letting the ThunderLAN's internal PHY go to waste. * A serial EEPROM is also attached to the ThunderLAN chip to provide * power-up default register settings and for storing the adapter's * station address. Although not supported by this driver, the ThunderLAN * chip can also be connected to token ring PHYs. * * The ThunderLAN has a set of registers which can be used to issue * commands, acknowledge interrupts, and to manipulate other internal * registers on its DIO bus. The primary registers can be accessed * using either programmed I/O (inb/outb) or via PCI memory mapping, * depending on how the card is configured during the PCI probing * phase. It is even possible to have both PIO and memory mapped * access turned on at the same time. * * Frame reception and transmission with the ThunderLAN chip is done * using frame 'lists.' A list structure looks more or less like this: * * struct tl_frag { * u_int32_t fragment_address; * u_int32_t fragment_size; * }; * struct tl_list { * u_int32_t forward_pointer; * u_int16_t cstat; * u_int16_t frame_size; * struct tl_frag fragments[10]; * }; * * The forward pointer in the list header can be either a 0 or the address * of another list, which allows several lists to be linked together. Each * list contains up to 10 fragment descriptors. This means the chip allows * ethernet frames to be broken up into up to 10 chunks for transfer to * and from the SRAM. Note that the forward pointer and fragment buffer * addresses are physical memory addresses, not virtual. Note also that * a single ethernet frame can not span lists: if the host wants to * transmit a frame and the frame data is split up over more than 10 * buffers, the frame has to collapsed before it can be transmitted. * * To receive frames, the driver sets up a number of lists and populates * the fragment descriptors, then it sends an RX GO command to the chip. * When a frame is received, the chip will DMA it into the memory regions * specified by the fragment descriptors and then trigger an RX 'end of * frame interrupt' when done. The driver may choose to use only one * fragment per list; this may result is slighltly less efficient use * of memory in exchange for improving performance. * * To transmit frames, the driver again sets up lists and fragment * descriptors, only this time the buffers contain frame data that * is to be DMA'ed into the chip instead of out of it. Once the chip * has transfered the data into its on-board SRAM, it will trigger a * TX 'end of frame' interrupt. It will also generate an 'end of channel' * interrupt when it reaches the end of the list. *//* * Some notes about this driver: * * The ThunderLAN chip provides a couple of different ways to organize * reception, transmission and interrupt handling. The simplest approach * is to use one list each for transmission and reception. In this mode, * the ThunderLAN will generate two interrupts for every received frame * (one RX EOF and one RX EOC) and two for each transmitted frame (one * TX EOF and one TX EOC). This may make the driver simpler but it hurts * performance to have to handle so many interrupts. * * Initially I wanted to create a circular list of receive buffers so * that the ThunderLAN chip would think there was an infinitely long * receive channel and never deliver an RXEOC interrupt. However this * doesn't work correctly under heavy load: while the manual says the * chip will trigger an RXEOF interrupt each time a frame is copied into * memory, you can't count on the chip waiting around for you to acknowledge * the interrupt before it starts trying to DMA the next frame. The result * is that the chip might traverse the entire circular list and then wrap * around before you have a chance to do anything about it. Consequently, * the receive list is terminated (with a 0 in the forward pointer in the * last element). Each time an RXEOF interrupt arrives, the used list * is shifted to the end of the list. This gives the appearance of an * infinitely large RX chain so long as the driver doesn't fall behind * the chip and allow all of the lists to be filled up. * * If all the lists are filled, the adapter will deliver an RX 'end of * channel' interrupt when it hits the 0 forward pointer at the end of * the chain. The RXEOC handler then cleans out the RX chain and resets * the list head pointer in the ch_parm register and restarts the receiver. * * For frame transmission, it is possible to program the ThunderLAN's * transmit interrupt threshold so that the chip can acknowledge multiple * lists with only a single TX EOF interrupt. This allows the driver to * queue several frames in one shot, and only have to handle a total * two interrupts (one TX EOF and one TX EOC) no matter how many frames * are transmitted. Frame transmission is done directly out of the * mbufs passed to the tl_start() routine via the interface send queue. * The driver simply sets up the fragment descriptors in the transmit * lists to point to the mbuf data regions and sends a TX GO command. * * Note that since the RX and TX lists themselves are always used * only by the driver, the are malloc()ed once at driver initialization * time and never free()ed. * * Also, in order to remain as platform independent as possible, this * driver uses memory mapped register access to manipulate the card * as opposed to programmed I/O. This avoids the use of the inb/outb * (and related) instructions which are specific to the i386 platform. * * Using these techniques, this driver achieves very high performance * by minimizing the amount of interrupts generated during large * transfers and by completely avoiding buffer copies. Frame transfer * to and from the ThunderLAN chip is performed entirely by the chip * itself thereby reducing the load on the host CPU. */#include "bpfilter.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/sockio.h>#include <sys/mbuf.h>#include <sys/malloc.h>#include <sys/kernel.h>#include <sys/socket.h>#include <net/if.h>#include <net/if_arp.h>#include <net/ethernet.h>#include <net/if_dl.h>#include <net/if_media.h>#if NBPFILTER > 0#include <net/bpf.h>#endif#include <vm/vm.h> /* for vtophys */#include <vm/pmap.h> /* for vtophys */#include <machine/clock.h> /* for DELAY */#include <machine/bus_memio.h>#include <machine/bus_pio.h>#include <machine/bus.h>#include <pci/pcireg.h>#include <pci/pcivar.h>/* * Default to using PIO register access mode to pacify certain * laptop docking stations with built-in ThunderLAN chips that * don't seem to handle memory mapped mode properly. */#define TL_USEIOSPACE/* #define TL_BACKGROUND_AUTONEG */#include <pci/if_tlreg.h>#if !defined(lint)static const char rcsid[] = "$Id: if_tl.c,v 1.24.2.5 1999/05/06 15:39:38 wpaul Exp $";#endif/* * Various supported device vendors/types and their names. */static struct tl_type tl_devs[] = { { TI_VENDORID, TI_DEVICEID_THUNDERLAN, "Texas Instruments ThunderLAN" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10, "Compaq Netelligent 10" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100, "Compaq Netelligent 10/100" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_PROLIANT, "Compaq Netelligent 10/100 Proliant" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_DUAL, "Compaq Netelligent 10/100 Dual Port" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_INTEGRATED, "Compaq NetFlex-3/P Integrated" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P, "Compaq NetFlex-3/P" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETFLEX_3P_BNC, "Compaq NetFlex 3/P w/ BNC" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_EMBEDDED, "Compaq Netelligent 10/100 TX Embedded UTP" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_T2_UTP_COAX, "Compaq Netelligent 10 T/2 PCI UTP/Coax" }, { COMPAQ_VENDORID, COMPAQ_DEVICEID_NETEL_10_100_TX_UTP, "Compaq Netelligent 10/100 TX UTP" }, { OLICOM_VENDORID, OLICOM_DEVICEID_OC2183, "Olicom OC-2183/2185" }, { OLICOM_VENDORID, OLICOM_DEVICEID_OC2325, "Olicom OC-2325" }, { OLICOM_VENDORID, OLICOM_DEVICEID_OC2326, "Olicom OC-2326 10/100 TX UTP" }, { 0, 0, NULL }};/* * Various supported PHY vendors/types and their names. Note that * this driver will work with pretty much any MII-compliant PHY, * so failure to positively identify the chip is not a fatal error. */static struct tl_type tl_phys[] = { { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, { 0, 0, "<MII-compliant physical interface>" }};static unsigned long tl_count;static const char *tl_probe __P((pcici_t, pcidi_t));static void tl_attach __P((pcici_t, int));static int tl_attach_phy __P((struct tl_softc *));static int tl_intvec_rxeoc __P((void *, u_int32_t));static int tl_intvec_txeoc __P((void *, u_int32_t));static int tl_intvec_txeof __P((void *, u_int32_t));static int tl_intvec_rxeof __P((void *, u_int32_t));static int tl_intvec_adchk __P((void *, u_int32_t));static int tl_intvec_netsts __P((void *, u_int32_t));static int tl_newbuf __P((struct tl_softc *, struct tl_chain_onefrag *));static void tl_stats_update __P((void *));static int tl_encap __P((struct tl_softc *, struct tl_chain *, struct mbuf *));static void tl_intr __P((void *));static void tl_start __P((struct ifnet *));static int tl_ioctl __P((struct ifnet *, u_long, caddr_t));static void tl_init __P((void *));static void tl_stop __P((struct tl_softc *));static void tl_watchdog __P((struct ifnet *));static void tl_shutdown __P((int, void *));static int tl_ifmedia_upd __P((struct ifnet *));static void tl_ifmedia_sts __P((struct ifnet *, struct ifmediareq *));static u_int8_t tl_eeprom_putbyte __P((struct tl_softc *, int));static u_int8_t tl_eeprom_getbyte __P((struct tl_softc *, int, u_int8_t *));static int tl_read_eeprom __P((struct tl_softc *, caddr_t, int, int));static void tl_mii_sync __P((struct tl_softc *));static void tl_mii_send __P((struct tl_softc *, u_int32_t, int));static int tl_mii_readreg __P((struct tl_softc *, struct tl_mii_frame *));static int tl_mii_writereg __P((struct tl_softc *, struct tl_mii_frame *));static u_int16_t tl_phy_readreg __P((struct tl_softc *, int));static void tl_phy_writereg __P((struct tl_softc *, int, int));static void tl_autoneg __P((struct tl_softc *, int, int));static void tl_setmode __P((struct tl_softc *, int));static int tl_calchash __P((caddr_t));static void tl_setmulti __P((struct tl_softc *));static void tl_setfilt __P((struct tl_softc *, caddr_t, int));static void tl_softreset __P((struct tl_softc *, int));static void tl_hardreset __P((struct tl_softc *));static int tl_list_rx_init __P((struct tl_softc *));static int tl_list_tx_init __P((struct tl_softc *));static u_int8_t tl_dio_read8 __P((struct tl_softc *, int));static u_int16_t tl_dio_read16 __P((struct tl_softc *, int));static u_int32_t tl_dio_read32 __P((struct tl_softc *, int));static void tl_dio_write8 __P((struct tl_softc *, int, int));static void tl_dio_write16 __P((struct tl_softc *, int, int));static void tl_dio_write32 __P((struct tl_softc *, int, int));static void tl_dio_setbit __P((struct tl_softc *, int, int));static void tl_dio_clrbit __P((struct tl_softc *, int, int));static void tl_dio_setbit16 __P((struct tl_softc *, int, int));static void tl_dio_clrbit16 __P((struct tl_softc *, int, int));static u_int8_t tl_dio_read8(sc, reg) struct tl_softc *sc; int reg;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); return(CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)));}static u_int16_t tl_dio_read16(sc, reg) struct tl_softc *sc; int reg;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); return(CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)));}static u_int32_t tl_dio_read32(sc, reg) struct tl_softc *sc; int reg;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); return(CSR_READ_4(sc, TL_DIO_DATA + (reg & 3)));}static void tl_dio_write8(sc, reg, val) struct tl_softc *sc; int reg; int val;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), val); return;}static void tl_dio_write16(sc, reg, val) struct tl_softc *sc; int reg; int val;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), val); return;}static void tl_dio_write32(sc, reg, val) struct tl_softc *sc; int reg; int val;{ CSR_WRITE_2(sc, TL_DIO_ADDR, reg); CSR_WRITE_4(sc, TL_DIO_DATA + (reg & 3), val); return;}static void tl_dio_setbit(sc, reg, bit) struct tl_softc *sc; int reg; int bit;{ u_int8_t f; CSR_WRITE_2(sc, TL_DIO_ADDR, reg); f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); f |= bit; CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); return;}static void tl_dio_clrbit(sc, reg, bit) struct tl_softc *sc; int reg; int bit;{ u_int8_t f; CSR_WRITE_2(sc, TL_DIO_ADDR, reg); f = CSR_READ_1(sc, TL_DIO_DATA + (reg & 3)); f &= ~bit; CSR_WRITE_1(sc, TL_DIO_DATA + (reg & 3), f); return;}static void tl_dio_setbit16(sc, reg, bit) struct tl_softc *sc; int reg; int bit;{ u_int16_t f; CSR_WRITE_2(sc, TL_DIO_ADDR, reg); f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); f |= bit; CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); return;}static void tl_dio_clrbit16(sc, reg, bit) struct tl_softc *sc; int reg; int bit;{ u_int16_t f; CSR_WRITE_2(sc, TL_DIO_ADDR, reg); f = CSR_READ_2(sc, TL_DIO_DATA + (reg & 3)); f &= ~bit; CSR_WRITE_2(sc, TL_DIO_DATA + (reg & 3), f); return;}/* * Send an instruction or address to the EEPROM, check for ACK. */static u_int8_t tl_eeprom_putbyte(sc, byte) struct tl_softc *sc; int byte;{ register int i, ack = 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?