📄 net_3c509.cxx
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* References: * * 1. EtherLink(r) III Parallel Tasking(tm) ISA, EISA, Micro * Channel(r), and PCMCIA Adapter Drivers Technical Reference * * The current version of the driver has been tested only on 3c509 and * 3c509b. If anyone has a PCMCIA card out there to test with, I'll * be glad to help debug it. We do not support MCA bus at * the moment, though the probes are stubbed properly. The EISA bus * code is untested, but should work okay. */#include <kerninc/kernel.hxx>#include <kerninc/Process.hxx>#include <kerninc/Invocation.hxx>#include <kerninc/AutoConf.hxx>#include <kerninc/IntAction.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Machine.hxx>#include <kerninc/Thread.hxx>#include <kerninc/NetInterface.hxx>#include <kerninc/IoRegion.hxx>#include <eros/Device.h>#include <io.h>#include <eros/Invoke.h>#include <eros/memory.h>#include <kerninc/MsgLog.hxx>#include <kerninc/util.hxx>/* for fine-grain timing: */#include <../kernel/lostart.hxx>/* #define ENET_DEBUG * #define VERBOSE *//* The min/max packet sizes at the ethernet level are dictated by wire * timing, and are entirely independent of the encapsulation (RFC984 * v/s IEEE 802.2/802.3. Both sizes exclude the CRC field, which is * automatically generated by the hardware (or if necessary by the * driver). * * The true min packet size for ethernet, excluding CRC field, is 60 bytes * The true max packet size for ethernet, excluding CRC field, is 1514 bytes. * * In general, we wish to copy the data directly from/to the * user-supplied buffer. One problem with this is that the hardware * requires the packet to be a multiple of 4 bytes (zero padded if * necessary), while the enet spec allows the packet to be any desired * length between min and max. * * On the receive side, we therefore require that the user-supplied * buffer by aligned to a 32 bit boundary and ALSO multiple of 32 * bits. This allows us to accept the padding bytes from the card * directly into the buffer and then report the true length to the * caller. * * On the send side, we need to cope with short packets. The options * would appear to be one of the following: * * 1. Require the sent packet to be 32-bit aligned, handle the * trailing bytes specially by copying if necessary. * * 2. Require the sent packet buffer to be BOTH 32-bit aligned and a * multiple of 32-bits, obtaining the true packet length by * parsing the outbound packet. Unfortunately, this * requires the driver to do some parsing to determine if this is * an RFC894 packet or an IEEE 802.2/802.3 packet, so that is * not a preferred solution. * * 3. Require the sent packet buffer to be BOTH 32-bit aligned and a * 32-bit multiple, with the length prepended to the buffer. * * NEWS FLASH * * sjmuir points out that the important issue from the software side * is not whether the ethernet PACKET is aligned, but whether the * ethernet PAYLOAD is aligned. In particular, the ethernet packet * header is 14 bytes, which leaves the payload at a 16-bit boundary. * Given this, we decided that the interface should be prepared to * hand back packets in the following form: * * <- 16 -> * +--------+ * | | * +-----+ | * | | * ~ ~ * | | * +--------------+ * <- 32 -> * * The major gotcha with this is that one must be careful about * trailing packet bytes. If the end of the packet falls across a * page boundary, one cannot safely read beyond the end of the packet * to obtain pad bytes. * * After some cursing and muttering about this I resolved that the * damn thing should just be alignment insensitive, and should do the * right thing to ensure that it does NOT violate a page boundary. * */#define MIN_ENET_PACKET_SZ 0X3C#define MAX_ENET_PACKET_SZ 0x05ea#define TX_FIFO_THRESH MAX_ENET_PACKET_SZ + 4 + 2DEFMODULE(3c509, Probe, AutoAttach, "3c509 Interrupt");/* THEORY OF OPERATION: * * The 3c509 is capable of some fairly serious autoconfiguration, even * in the absence of Plug And Play. [Note: The current driver assumes * that no Plug and Play support exists in the kernel -- it will need * to be (slightly) revised when Plug and Play becomes a factor]. * Unlike most ISA devices, it is capable of being left in a * "detached" state. * * The probe phase of this driver simply builds the equipment list. * It inquires of each card what it's desired io port address, ROM * address, and IRQ line are. It tags each card so that it can be * activated later, but leaves the card UNACTIVATED. In the * unactivated state, the card can pretty much be relied on to leave * us alone. * * * For reasons of (SHORT LIVED) compatibility with existing code that * assumes the old driver design, this driver has an auto-activate * phase. The auto-activate phase simply configures each card at it's * default configured address. Conceptually, the card attachment * should be done from user level using a device key. * * * Contention resolution for the 3c509 is done using a single global * ID port. [Note: there is a disagreement in the documentation * concerning the ID PORT. In one statement, it says that the ID PORT * is remembered by the card. In the other, it says that cards in the * ID_WAIT state remember the *last* port in the range 0x1?0 to which * a zero has been written. If this is the case, the ID PORT need not * be a global variable at all. Until I have an opportunity to test * it, I don't intend to make assumptions.] * * The 3c509 ID PORT is (regrettably) global. It is set during the * early part of the probe phase. * * * Under normal operating conditions, the cards are left in register * window 1. This window holds all of the registers that are critical * to normal operation. Some supporting routines adjust the * registers, but these are always careful to switch them back. * * There is a (necessary) race condition between the read/write packet * routines and the interrupt handler. This is resolved by disabling * the etherlink interrupt during read/write. */static uint16_t id_port = 0x100;#define MANU_3COM 0x6d50#define tagValue devInfo[0]#define rxFilter devInfo[1]#define curInts devInfo[2]/* PnP Ports/Values of interest: */enum { PnpAddress = 0x279, PnpWrite = 0xa79, PnpSelConfigCtrl = 0x2, /* select configuration control register */ ElinkSetContention = 0x0, /* set card to run contention-based */ /* configuration */};/* ID Port access values */enum { IdRdEEPROM = 0x80, IdGlobalReset = 0xc0, IdSetTagReg = 0xd0, IdTestAdapter = 0xd8, IdActivate = 0xe0, IdActivatePreconf = 0xff,} ;#if 0#define ID_PORT 0X0100#define ID_PORT_GOTO_ID_WAIT 0X00#define ID_PORT_RD_EEPROM 0X80#define ID_PORT_GLOBAL_RESET 0XC0#define ID_PORT_SET_TAG_REG 0XD0#define ID_PORT_TEST_ADAPTER 0XD8#define ID_PORT_ACTIVATE_IO_PORT 0XE0#define ID_PORT_ACTIVATE_PRECONFIG 0XFF#endif/* Register window offsets */enum { El3Cmd = 0xe, /* common to all windows */ El3Status = 0xe, /* BYTE value */ El3Window = 0xf, /* BYTE value */ El3EepromData = 0xc, El3EepromCmd = 0xa, El3RsrcConfig = 0x8, El3AddrConfig = 0x6, El3ConfigCtrl = 0x4, El3ProductId = 0x2, El3ManufacturerID = 0x0,} ;/* Window 0 offsets: */enum { Wn0Irq = 0x08, Wn0ConfigCtrl = 0x04,};/* Window 1 offsets: */enum { Wn1RxStatus = 0x08, Wn1TxStatus = 0x0B, Wn1Rxuint8_tFifo = 0x00, Wn1RxFifo = 0x00, Wn1Txuint8_tFifo = 0x00, Wn1TxFifo = 0x00, Wn1TxFree = 0x0c, Wn1Timer = 0x0a,};/* Window 4 offsets: */enum { Wn4MediaType = 0x0a,} ;/* Window 5 offsets: */enum { Wn5IntMask = 0x0a, Wn5StatusMask = 0x0c,} ;/* Media status bits: */enum { MediaStatusLK = 0x80, /* link beat enable (TP) */ MediaStatusJE = 0x40, /* Jabber Enable */} ;#define EISA_WINREG(base, x) ((base) + 0xc80 + (x))/* Commands: */enum { GlobalReset = 0u << 11, SelectWindow = 1u << 11, StartCoax = 2u << 11, RxDisable = 3u << 11, RxEnable = 4u << 11, RxReset = 5u << 11, RxDiscard = 8u << 11, TxEnable = 9u << 11, TxDisable = 10u << 11, TxReset = 11u << 11, ReqIntr = 12u << 11, AckIntr = 13u << 11, SetIntrMask = 14u << 11, SetStatusMask = 15u << 11, SetRxFilter = 16u << 11, SetRxThresh = 17u << 11, /* RX Early Threshold */ SetTxAvailThresh = 18u << 11, /* TX Available Threshold */ SetTxStartThresh = 19u << 11, StatsEnable = 21u << 11, StatsDisable = 22u << 11, StopCoax = 23u << 11, SetTxReclaimThresh = 24u << 11, PowerUp = 27u << 11, /* 3c509b, 3c589b */ PowerDownFull = 28u << 11, /* 3c509b, 3c589b */ PowerAuto = 29u << 11, /* 3c509b, 3c589b */};/* Set RX filter command args: */enum { RxIndividual = 0x1, RxMulticast = 0x2, RxBroadcast = 0x4, RxPromisc = 0x8,};/* Status register bits: */enum { CurWindow = 0xE000, /* current window number */ /* Intervening bits are reserved */ InProgress = 0x1000, UpdateStats = 0x0080, /* generates interrupt when set if not masked */ IntRequested = 0x0040, /* generates interrupt when set if not masked */ RxEarly = 0x0020, /* generates interrupt when set if not masked */ RxComplete = 0x0010, /* generates interrupt when set if not masked */ TxAvail = 0x0008, /* generates interrupt when set if not masked */ TxComplete = 0x0004, /* generates interrupt when set if not masked */ AdapterFail = 0x0002, /* generates interrupt when set if not masked */ IntLatch = 0x0001,};/* RX Status register values: (16-bit register) */enum { RxsIncomplete = 0x8000u, /* incomplete RX packet */ RxsError = 0x4000, /* error in RX packet */ RxsErrType = 0x3800, /* mask to fetch error type */ Rxsuint8_ts = 0x07ff, /* RX packet len, excluding padding */ /* RX Error Type values: */ RxsOverrun = 0x0u << 11, RxsOversize = 0x1u << 11, /* oversize packet (> 1514 bytes) */ RxsDribble = 0x2u << 11, /* The trouble with dribbles... */ RxsRunt = 0x3u << 11, /* small packet */ RxsAlignment = 0x4u << 11, /* framing error */ RxsCRC = 0x5u << 11,};/* TX Status register values: (8-bit register) */enum { TxsComplete = 0x80, TxsIntReq = 0x40, /* interrupt on completion requested */ TxsJabber = 0x20, /* jabber error -- TP only; TX Reset required */ TxsUnderrun = 0x10, /* TX Reset required */ TxsCollisions = 0x08, /* Maximum collisions */ TxsStatusOverflow = 0x04, TxsReclaim = 0x02, /* MCA only; else undefined */ /* bit 0 undefined */};/* Bit values for the internal configuration register: */enum { IsaActivateMask = 0x3u << 18, IsaActivatePnP = 0x2u << 18, IsaActivateContention = 0x1u << 18, IsaActivateBoth = 0x0u << 18, /* Following describe how the FIFO ram should be divvied up between * TX and RX FIFO -- ratios are TX::RX. Defaults to 1:1 */ Ram_3to5 = 0x0u << 16,/* ram size must be 000b */ Ram_1to3 = 0x1u << 16,/* ram size must be 000b */ Ram_1to1 = 0x2u << 16, RamSpeed2 = 0x0u << 4, /* don't mess with this! */ RamSize8k = 0x0u, RamSize32k = 0x2u, AddrCfgXcvr = 0xc000, XcvrTwistedPair = 0x0, XcvrAUI = 0x1, XcvrReserved = 0x2, XcvrBNC = 0x3, RsrcCfgIrq = 0xf000, AddrCfgIoBase = 0x001f,} ;static void Invoke(NetInterface *, Invocation& inv); /* FORWARD */#if 0static void TxPacket(NetInterface *); /* FORWARD */#endifstatic void RxPacket(NetInterface *); /* FORWARD */static void ReadPacket(NetInterface *ni, Invocation& inv); /* FORWARD */static void WritePacket(NetInterface *ni, Invocation& inv); /* FORWARD */static void MultiReadPacket(NetInterface *ni, Invocation& inv); /* FORWARD */#if 0static void MultiWritePacket(NetInterface *ni, Invocation& inv); /* FORWARD */#endifstatic void Enable(NetInterface *); /* FORWARD */static void UpdateStatistics(NetInterface *); /* FORWARD */static void HandleInterrupt(NetInterface *); /* FORWARD */static void HandleInterrupt(IntAction *); /* FORWARD */inline voidOutCmd(NetInterface *ni, uint16_t cmd, bool andWait = false){ out16(cmd, ni->ioAddr + El3Cmd); if (andWait) while (in16(ni->ioAddr + El3Status) & InProgress) ;}inline voidSetWindow(NetInterface* ni, int whichWindow){ OutCmd(ni, SelectWindow | whichWindow);}#ifdef CONFIG_EISA/* Assumes we are in window 0; */static uint16_tReadEEPROM(uint32_t iobase, uint32_t offset){ out16(IdRdEEPROM + offset, iobase + El3EepromCmd); /* EEPROM requires 162 usec stall: */ Machine::SpinWaitMs(1); return inh(iobase + El3EepromData);}#endif/* Read a word from the EEPROM via the ID port. */static uint16_tIsaIdRdEEPROM(uint16_t id_port, uint32_t offset){ /* Issue read command, and pause for at least 162 us. for it to complete. Assume extra-fast 16Mhz bus. */ out8(IdRdEEPROM + offset, id_port); /* Pause for at least 162 us. for the read to take place. */ Machine::SpinWaitMs(1); /* let things settle */ /* Read 16-bit value from ID Port. */ uint16_t temp = 0; for (uint8_t loop = 0; loop < 16; loop++) temp = (temp << 1) + (inb(id_port) & 0x1); return temp;}inline voidEISA_Probe(AutoConf * ac){#ifdef CONFIG_EISA /* Check all slots of the EISA bus. */ for (uint32_t ioaddr = 0x1000; ioaddr < 0x9000; ioaddr++) { if (inh(EISA_WINREG(ioaddr, El3ManufacturerID)) != MANU_3COM) continue; ac->present = true; /* Switch to Window 0 -- this might be a reboot, so the state * is not necessarily known. */ outh(EISA_WINREG(ioaddr, El3Cmd), SelectWindow); uint16_t irq = inh(EISA_WINREG(ioaddr, Wn0Irq)) >> 12; NetInterface *ni = NetInterface::Alloc(); ni->irq = irq; ni->ioAddr = ioaddr; ni->ioLen = 0x0f; ni->addr = 0; ni->len = 0; ni->xcvr = (inh(ioaddr + 6) & AddrCfgXcvr) >> 14; ni->name = "EtherLink III (EISA)"; ni->devClass = DEV_NET_ENET; ni->updateStats = UpdateStatistics; for (int i = 0; i < 3; i++) { uint16_t hw = ReadEEPROM(ioaddr, i); ni->linkAddr[i*2] = (hw >> 8) & 0xffu; ni->linkAddr[ i*2 + 1 ] = hw & 0xffu; } /* Drop the product ID back in the EEPROM register: */ ReadEEPROM(ioaddr, 3); }#else /* This will peephole out -- it suppresses a compiler warning. */ ac->present = ac->present;#endif}inline voidMCA_Probe(AutoConf * ac){#ifdef CONFIG_MCA#else /* This will peephole out -- it suppresses a compiler warning. */ ac->present = ac->present;#endif}#define ioport_isavail(x) truestatic void IdentifySequence(){ /* Lock down the ID PORT, in case that was reset - harmless if the * card is in the ID_WAIT state. */ out8(0x0, id_port); Machine::SpinWaitMs(1); /* let things settle */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -