/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Tulip.cs // // Simple Driver for DEC 21140a PCI Ethernet card. (as used in Virtual PC) // // Useful reference URLs: // http://www.intel.com/design/network/manuals/21140ahm.pdf // // TODO: // - MII support // - Loss of Carrier / No Carrier Handling // - Promiscuous mode switching // - Filtering // - Receive Watchdog / Jabber Handling // - Event Counters // - Review why is this driver using I/O ports. // // NB: This code is only tested on Virtual PC 2007 and would probably have // error handling issues on real hardware. using System; using System.Threading; using System.Diagnostics; using System.Collections; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using Microsoft.Singularity.Io.Net; using Microsoft.Singularity.V1.Services; using Drivers.Net; using Microsoft.Singularity.Drivers; namespace Microsoft.Singularity.Drivers.Network.Tulip { internal class Tulip { internal const uint MaxRxPackets = 512; internal const uint MaxTxPackets = 512; internal const uint MaxRxFragmentsPerPacket = 1; internal const uint MaxTxFragmentsPerPacket = 8; internal const uint MaxRxFragmentsInRing = MaxRxPackets * MaxRxFragmentsPerPacket; internal const uint MaxTxFragmentsInRing = MaxTxPackets * MaxTxFragmentsPerPacket; internal const uint MtuBytes = 1514; internal const uint SetupFrameBytes = 192; private const uint BytesPerDescriptor = 16; // MAC address private EthernetAddress macAddress; private uint unit; // State for tracking controllers sharing serial rom private static EthernetAddress macBase; private static uint lastUnit = 0; // PCI stuff private PciDeviceConfig! config; // CSRs private IoPort! csr0; private IoPort! csr1; private IoPort! csr2; private IoPort! csr3; private IoPort! csr4; private IoPort! csr5; private IoPort! csr6; private IoPort! csr7; private IoPort! csr8; private IoPort! csr9; private IoPort! csr10; private IoPort! csr11; private IoPort! csr12; private IoPort! csr15; private IoIrq! irq; private TulipTxRingBuffer! txRing; private TulipRxRingBuffer! rxRing; private EthernetAddress ethernetAddress; private TulipEventRelay eventRelay; private Thread irqWorker; private volatile uint irqLastStatus; private volatile bool irqWorkerStop; private volatile bool isShutdown = false; // Constructor internal Tulip(TulipResources! res) { TulipDebug.Print("Tulip: Initialize() called\n"); assume res.csr != null; assume res.irq != null; csr0 = (!)res.csr.PortAtOffset(0x00, 4, Access.ReadWrite); csr1 = (!)res.csr.PortAtOffset(0x08, 4, Access.Write); csr2 = (!)res.csr.PortAtOffset(0x10, 4, Access.Write); csr3 = (!)res.csr.PortAtOffset(0x18, 4, Access.ReadWrite); csr4 = (!)res.csr.PortAtOffset(0x20, 4, Access.ReadWrite); csr5 = (!)res.csr.PortAtOffset(0x28, 4, Access.ReadWrite); csr6 = (!)res.csr.PortAtOffset(0x30, 4, Access.ReadWrite); csr7 = (!)res.csr.PortAtOffset(0x38, 4, Access.ReadWrite); csr8 = (!)res.csr.PortAtOffset(0x40, 4, Access.Read); csr9 = (!)res.csr.PortAtOffset(0x48, 4, Access.ReadWrite); csr10 = (!)res.csr.PortAtOffset(0x50, 4, Access.Read); csr11 = (!)res.csr.PortAtOffset(0x58, 4, Access.ReadWrite); csr12 = (!)res.csr.PortAtOffset(0x60, 4, Access.ReadWrite); csr15 = (!)res.csr.PortAtOffset(0x78, 4, Access.ReadWrite); irq = (!)res.irq.IrqAtOffset(0); TulipDebug.Print("Tulip IRQ {0}\n", __arglist(res.irq.ToString())); PciDeviceConfig! config = this.config = (PciDeviceConfig!)IoConfig.GetConfig(); Debug.Assert((config.InterruptsEnabled)); this.txRing = new TulipTxRingBuffer(MaxTxPackets, MaxTxFragmentsInRing); this.rxRing = new TulipRxRingBuffer(MaxRxFragmentsInRing); } internal void StartIo() { Debug.Assert(irqWorker == null); TulipDebug.Print("StartIo\n"); if (isShutdown) { return; } csr6.Write32(CSR6.MBO | CSR6.HBD | CSR6.PS | (3u << CSR6.TR_ROLL) | CSR6.ST); WriteStandardSetupFrame(); // Write CSR6 to start receive and transmit processes // // This is taken from the state remaining after pxeboot. // == 100Mb/s MII/SYM (table 3-43 entry 1000) csr6.Write32(CSR6.MBO | CSR6.HBD | CSR6.PS | (3u << CSR6.TR_ROLL) | CSR6.ST | CSR6.SR); TulipDebug.Print("Programmed CSR values...\n"); TulipDebug.PrintCsrs(csr0, csr1, csr2, csr3, csr4, csr5, csr6, csr7, csr8, csr9, csr10, csr11, csr12, csr15); TulipDebug.Print("CFCS: {0:x4} {1:x4}\n", __arglist(config.Control, config.Status)); irqWorkerStop = false; irqWorker = new Thread(new ThreadStart(this.IrqWorkerMain)); irqWorker.Start(); } internal void StopIo() { if (irqWorker != null) { // Set stop flag, wake-up irqWorker thread, then wait. irqWorkerStop = true; irq.Pulse(); irqWorker.Join(); irqWorker = null; } } internal void Shutdown() { isShutdown = true; StopIo(); } public bool IsShutdown { get { return isShutdown; } } private void InitChains() { TulipDebug.Print("InitChains(rx = {0}, tx = {1})\n", __arglist(MaxRxFragmentsInRing, MaxTxFragmentsInRing)); // Turn off receive and transmit uint mode = csr6.Read32(); mode &= ~(CSR6.SR | CSR6.ST); csr6.Write32(mode); this.rxRing.Reset(); csr3.Write32(this.rxRing.BaseAddress.ToUInt32()); this.txRing.Reset(); csr4.Write32(this.txRing.BaseAddress.ToUInt32()); } // Device methods internal void Initialize() { TulipDebug.Print("Tulip: Initialize() called\n"); // Dump config registers TulipDebug.Print("Initial CSR values...\n"); TulipDebug.PrintCsrs(csr0, csr1, csr2, csr3, csr4, csr5, csr6, csr7, csr8, csr9, csr10, csr11, csr12, csr15); // Perform reset to clear out any state we may have left over // from pxeboot. csr0.Write32(CSR0.SWR); TulipDebug.Print("Reset and new CSR values...\n"); TulipDebug.PrintCsrs(csr0, csr1, csr2, csr3, csr4, csr5, csr6, csr7, csr8, csr9, csr10, csr11, csr12, csr15); // Pull Serial Rom and parse data byte[]! srom = PullSRom(); TulipDebug.PrintBytes("SROM", srom); // Assign mac address. Note we may have multiple // phy's sharing the same rom and multiple cards and // the below is not foolproof. EthernetAddress macRom = EthernetAddress.ParseBytes(srom, 20); if (macBase != macRom) { macBase = macRom; unit = 0; } else { unit = lastUnit + 1; } macAddress = macRom; for (int i = 1; i < unit; i++) { macAddress++; } lastUnit = unit; // Register Interrupt if (irq.RegisterInterrupt() == false) { DebugStub.Break(); } // Figure out PCI cache bits for csr0 // Not sure this is sane. uint cls = config.CacheLineSize; uint cacheBits = CSR0.WIE | CSR0.RLE | CSR0.RME; if (cls < 8 || cls > 32) { cls = 0; cacheBits = 0; } InitChains(); // Write CSR0 to set global host bus operating parameters csr0.Write32(cacheBits | CSR0.BAR | ((cls / 8u) << CSR0.CAL_ROLL) | (cls << CSR0.PBL_ROLL)); // This is a debug helper and could be used to switch to // polling mode. // StartPeriodicTimer(TimeSpan.FromMilliseconds(1000)); // And remember to enable the timer interrupt below CSR7.GPT // Write CSR7 to mask unnecessary interrupts csr7.Write32(CSR7.AI | CSR7.RI | CSR7.TI | CSR7.GPT); } internal void Finalize() { irq.ReleaseInterrupt(); // Get worker thread to end main loop. When it terminates irqWorkerStop = true; TulipDebug.Print("Tulip: Finalize() called\n"); } internal string! Name { get { return "DE2114x Ethernet"; } } internal string! Version { get { return "0.1"; } } internal EthernetAddress HardwareAddress { get { return macAddress; } } internal byte[]! HardwareAddressBytes { get { byte [] tmp = macAddress.GetAddressBytes(); if (tmp != null) return tmp; return new byte[6] { 0, 0, 0, 0, 0, 0 }; } } internal uint LinkSpeed { get { return 10 * 1000 * 1000; } } internal EthernetAddress BssId { get { return EthernetAddress.Zero; } } private void ReceivePollDemand() { csr2.Write32(1); } internal void GetReceivedPackets(PacketFifo*! in ExHeap fifo) { rxRing.Pop(fifo); // ReceivePollDemand(); } internal void ReceivePackets(PacketFifo*! in ExHeap fifo) ensures fifo->Count == 0; { rxRing.Push(fifo); ReceivePollDemand(); } private void TransmitPollDemand() { csr1.Write32(1); } internal void GetTransmittedPackets(PacketFifo*! in ExHeap fifo) { txRing.Pop(fifo); TransmitPollDemand(); } internal void TransmitPackets(PacketFifo*! in ExHeap fifo) ensures fifo->Count == 0; { txRing.Push(fifo); TransmitPollDemand(); } // // IrqWorker thread and related event notification setter // internal void SetEventRelay(TulipEventRelay theEventRelay) { eventRelay = theEventRelay; } private void StartPeriodicTimer(TimeSpan period) { long ticksPerBit; uint mode = csr6.Read32(); if ((mode & (CSR6.PS | CSR6.TTM)) != 0) { // 10 MB port ticksPerBit = 2048; } else if ((mode & (CSR6.PS | CSR6.TTM | CSR6.PCS | CSR6.SCR)) == (CSR6.PS | CSR6.TTM)) { // 10 MB MII/SYM ticksPerBit = 8192; } else { // 100 MB ticksPerBit = 819; } long bv = period.Ticks / ticksPerBit; if (bv > 0xffff) bv = 0xffff; csr11.Write32(0x10000 | (uint)bv); } private void IrqWorkerMain() { Tracing.Log(Tracing.Debug, "Start worker thread."); // Grab IoPorts used locally from instance so other threads // can not use them. while (irqWorkerStop == false) { uint status = 0; while (status == 0) { irq.WaitForInterrupt(); status = csr5.Read32() & CSR5.VALID; } DumpCsr5(status); rxRing.DumpOwnership("Interrupt"); uint tulipEvent = TulipEvent.NoEvent; if ((status & (CSR5.EB|CSR5.FBE)) != 0) { TulipDebug.Print("Error: csr5.EB = {0} csr5.FBE = {1}\n", __arglist(status & CSR5.EB, status & CSR5.FBE)); DebugStub.Break(); } if ((status & CSR5.RWT) != 0) { } if ((status & CSR5.TJT) != 0) { } if ((status & CSR5.AIS) != 0) { txRing.ClearTransmitError(); TransmitPollDemand(); } if ((status & (CSR5.RI | CSR5.RPS)) != 0) { tulipEvent |= TulipEvent.ReceiveEvent; } if ((status & CSR5.TI) != 0) { tulipEvent |= TulipEvent.TransmitEvent; } // Clear status bits csr5.Write32(status); // Enable interrupts // csr7.Write32(CSR7.AI | CSR7.RI | CSR7.TI ); // Acknowledge Interrupt irq.AckInterrupt(); // Announce events if (eventRelay != null) { eventRelay.ForwardEvent(tulipEvent); } } // Perform software reset to stop processing csr0.Write32(CSR0.SWR); TulipDebug.Print("Tulip Stopped."); } [ System.Diagnostics.Conditional("DEBUG_TULIP") ] private static void DumpCsr5(uint v) { v &= CSR5.VALID; TulipDebug.Print("CSR5 : {0:x8} ", __arglist(v)); uint eb = (v >> CSR5.EB_ROLL) & CSR5.EB_MASK; if ((v & CSR5.FBE) != 0) { TulipDebug.Print("Fatal bus error: {0}\n", __arglist(eb)); return; } uint ts = (v >> CSR5.TS_ROLL) & CSR5.TS_MASK; switch (ts) { case 0: TulipDebug.Print("TS - STOPPED "); break; case 1: TulipDebug.Print("TS - RUN (Fetch) "); break; case 2: TulipDebug.Print("TS - RUN (Wait) "); break; case 3: TulipDebug.Print("TS - RUN (Fifo) "); break; case 4: TulipDebug.Print("TS - RESERVED "); break; case 5: TulipDebug.Print("TS - RUN (Setup) "); break; case 6: TulipDebug.Print("TS - Suspended "); break; case 7: TulipDebug.Print("TS - RUN (Close) "); break; } uint rs = (v >> CSR5.RS_ROLL) & CSR5.RS_MASK; switch (rs) { case 0: TulipDebug.Print("RS - STOPPED "); break; case 1: TulipDebug.Print("RS - RUN (Fetch) "); break; case 2: TulipDebug.Print("RS - RUN (Check) "); break; case 3: TulipDebug.Print("RS - RUN (Wait) "); break; case 4: TulipDebug.Print("RS - Suspended ) "); break; case 5: TulipDebug.Print("RS - RUN (Close) "); break; case 6: TulipDebug.Print("RS - RUN (Flush) "); break; case 7: TulipDebug.Print("RS - RUN (Queue) "); break; } if ((v & CSR5.NIS) != 0) TulipDebug.Print("NIS "); if ((v & CSR5.AIS) != 0) TulipDebug.Print("AIS "); if ((v & CSR5.ERI) != 0) TulipDebug.Print("ERI "); if ((v & CSR5.FBE) != 0) TulipDebug.Print("FBE "); if ((v & CSR5.GTE) != 0) TulipDebug.Print("GTE "); if ((v & CSR5.ETI) != 0) TulipDebug.Print("ETI "); if ((v & CSR5.RWT) != 0) TulipDebug.Print("RWT "); if ((v & CSR5.RPS) != 0) TulipDebug.Print("RPS "); if ((v & CSR5.RU) != 0) TulipDebug.Print("RU "); if ((v & CSR5.RI) != 0) TulipDebug.Print("RI "); if ((v & CSR5.UNF) != 0) TulipDebug.Print("UNF "); if ((v & CSR5.TJT) != 0) TulipDebug.Print("TJT "); if ((v & CSR5.TU) != 0) TulipDebug.Print("TU "); if ((v & CSR5.TPS) != 0) TulipDebug.Print("TPS "); if ((v & CSR5.TI) != 0) TulipDebug.Print("TI "); TulipDebug.Print("\n"); } // // SROM related methods // private uint GetSRomAddressWidth() { for (uint i = 6; i < 13; i++) { ushort w = SRomRead16(18u, i); if (w == 0) { return i - 1; } } return 6; } private byte[]! PullSRom() { uint sromWidth = GetSRomAddressWidth(); byte[] b = new byte[2 << (int)sromWidth]; for (uint i = 0; i < b.Length; i += 2) { ushort w = SRomRead16(i, sromWidth); b[i] = (byte) (w >> 8); b[i + 1] = (byte) (w & 0xff); } return b; } [System.Diagnostics.Conditional("HAVE_DELAY")] private static void Delay(int kernelTicks) { long expiry = ProcessService.GetUpTime().Ticks + kernelTicks; while (ProcessService.GetUpTime().Ticks < expiry) { Thread.Yield(); } } // -------------------------------------------------------------------- // MII (Based on IEEE802.3-2002 and unreviewed *and* untested). private int MiiReadBit() { int result = (csr9.Read32() & CSR9.MDI) != 0 ? 1 : 0; csr9.Write32(CSR9.MII); Delay(1); csr9.Write32(CSR9.MII | CSR9.MDC); Delay(1); csr9.Write32(CSR9.MII); Delay(1); return result; } private int MiiReadBits(int count) { int result = 0; while (count-- > 0) { result = result << 1; result |= MiiReadBit(); } return result; } private int MiiRead(int phy, int register) requires phy <= 31; requires register <= 31; { MiiWriteBits(-1, 32); // Preamble // 14 bit write, 18bit read MiiWriteBits((6 << 10) | // Start read transaction (2 bits) (phy << 5) | // Phy Address (5 bits) register, 14); // Register (5 bits) int value = MiiReadBits(18); MiiWriteBits(0, 32); // Idle return value & 0xffff; } private void MiiWriteBit(int bit) requires bit == 0 || bit == 1; { uint data = (uint)(bit * CSR9.MDO); csr9.Write32(data); Delay(1); csr9.Write32(data | CSR9.MDC); Delay(1); csr9.Write32(data); Delay(1); } // Write count bits from vector to MII private void MiiWriteBits(int vector, int count) requires count >= 0 && count <= 32; { while (count-- > 0) { MiiWriteBit(vector & 1); vector >>= 1; } } private void MiiWrite(int phy, int register, int value) requires phy <= 31; requires register <= 31; requires value <= 0xffff; { MiiWriteBits(-1, 32); // Preamble MiiWriteBits((0x5 << 28) | // Start write transaction (4 bits) (phy << 23) | // Phy Address (5 bits) (register << 18) | // Register (5 bits) (2 << 16) | // Turnaround 2 cycles (2 bits) value, 32); MiiWriteBits(0, 32); // Idle } // -------------------------------------------------------------------- // SROM private static ushort CalculateSromCrc(byte[]! srom) requires srom.Length > 126; { // Cut-paste-and-converted directly from SROM 4.09 spec const uint POLY = 0x04C11DB6; const uint DATA_LEN = 126; uint crc = 0xffffffff; for (uint index = 0; index < DATA_LEN; index++) { byte currentByte = srom[index]; for (uint bit = 0; bit < 8; bit++) { uint Msb = (crc >> 31) & 1; crc <<= 1; if ((Msb ^ (currentByte & 1)) != 0) { crc ^= POLY; crc |= 0x00000001; } currentByte >>= 1; } } uint flippedCrc = 0; for (uint i = 0; i < 32; i++) { flippedCrc <<= 1; uint bit = crc & 1; crc >>= 1; flippedCrc += bit; } crc = flippedCrc ^ 0xffffffff; return (ushort)(crc & 0xffff); } private static int DumpLeafMedia(byte[]! srom, int offset) { if ((0x80 & (int)srom[offset]) == 0) { TulipDebug.Print("-- Compact format (non-MII)"); TulipDebug.Print(" Media code {0:x2} GPR {1:x2} Command {2:x2}{3:x2}\n", __arglist(0x3f & (int)srom[offset], srom[offset + 1], srom[offset + 3], srom[offset + 2])); return 4; } else { int length = (0x7f & (int)srom[offset]) + 1; int type = (int)srom[offset + 1]; TulipDebug.Print("--- Extended format (Type {0} Length {1})\n", __arglist(type, length)); if (type == 0x01) { int gprLength = (int)srom[offset + 3]; int resetLength = (int)srom[offset + 4 + gprLength]; TulipDebug.Print(" MII PHY id = {0:x2} SeqLen {1} ResetLen {2} ", __arglist(srom[offset + 2], gprLength, resetLength)); int mediaCaps = ((int)srom[offset + 6 + gprLength + resetLength]) * 256 + (int)srom[offset + 5 + gprLength + resetLength]; int nWay = ((int)srom[offset + 8 + gprLength + resetLength]) * 256 + (int)srom[offset + 7 + gprLength + resetLength]; int fdx = ((int)srom[offset + 10 + gprLength + resetLength]) * 256 + (int)srom[offset + 9 + gprLength + resetLength]; int ttm = ((int)srom[offset + 12 + gprLength + resetLength]) * 256 + (int)srom[offset + 11 + gprLength + resetLength]; TulipDebug.Print("mediaCaps {0:x4} nWay {1:x4} fdx {2:x4} ttm {3:x4}\n", __arglist(mediaCaps, nWay, fdx, ttm)); } return length; } } private static void DumpLeafNode(byte[]! srom, int offset) { TulipDebug.Print("Leaf:\n"); TulipDebug.Print("- ConnectionType: {0:x2}{1:x2} GPR: {2:x2} InfoBlocks: {3}\n", __arglist(srom[offset + 1], srom[offset], srom[offset + 2], srom[offset + 3])); int infoBlocks = (int)srom[offset + 3]; offset += 4; for (int i = 0; i < infoBlocks; i++) { offset += DumpLeafMedia(srom, offset); } } private static void DecodeSRom(byte[]! srom) { TulipDebug.Print("SROM ID Block: Vendor {0:x2}{1:x2} Subsystem {2:x2}{3:x2} HwOpts {4:x2} Func0Opts {5:x2} CRC {6:x2}", __arglist(srom[1], srom[0], srom[3], srom[4], srom[15], srom[17], srom[16])); TulipDebug.Print("\nSROM Format: {0}\nController Count: {1}", __arglist(srom[18], srom[19])); int controllers = (int)srom[19]; TulipDebug.Print("\nMAC address: {0:x2}:{1:x2}:{2:x2}:{3:x2}:{4:x2}:{5:x2}", __arglist(srom[20], srom[21], srom[22], srom[23], srom[24], srom[25]) ); for (int i = 0; i < controllers; i++) { int offset = 27 + i * 2; TulipDebug.Print("\nController {0} leaf offset {1:x2}{2:x2}\n", __arglist(i, srom[offset + 1], srom[offset]) ); DumpLeafNode(srom, ((int)(srom[offset + 1]) << 8) | (int)(srom[offset])); } } private static byte [] SRomReadPreamble = { 0x01, 0x31, 0x57, 0x57, 0x51, 0x31 }; private ushort SRomRead16(uint addr, uint addrBits) { // This is taken from section 7-8 in 21140A reference manual. We // deliberately make all delays as 3 ticks since we don't have // sufficient resolution and don't care about 300ns vs 150ns for // the SROM accesses. const uint srom_base_cmd = 0x4800; // Starting byte address to starting word address addr >>= 1; foreach (byte b in SRomReadPreamble) { csr9.Write32(srom_base_cmd | (uint)(b >> 4)); Delay(3); csr9.Write32(srom_base_cmd | (uint)(b & 0x0f)); Delay(3); } // Write address to be read for (int i = (int)addrBits - 1; i >= 0; --i) { uint bit = ((uint)addr >> i) & 0x01; bit <<= 2; csr9.Write32(srom_base_cmd | bit | 0x01); Delay(3); csr9.Write32(srom_base_cmd | bit | 0x03); Delay(3); csr9.Write32(srom_base_cmd | bit | 0x01); Delay(3); } // Get lsb uint r = 0; for (int i = 7; i >= 0; --i) { csr9.Write32(srom_base_cmd | 0x03); Delay(3); r |= ((csr9.Read32() & 0x08) >> 3) << i; Delay(3); csr9.Write32(srom_base_cmd | 0x01); Delay(3); } // Get msb for (int i = 15; i >= 8; --i) { csr9.Write32(srom_base_cmd | 0x03); Delay(3); r |= ((csr9.Read32() & 0x08) >> 3) << i; Delay(3); csr9.Write32(srom_base_cmd | 0x01); Delay(3); } // Finish up csr9.Write32(srom_base_cmd); Delay(3); return (ushort)r; } private String ConnectionTypeName(uint connectionType) { switch (connectionType) { case 0x0000: return "10BaseT"; case 0x0100: return "10BaseT Nway"; case 0x0204: return "10BaseT Full Duplex"; case 0x0001: return "BNC"; case 0x0003: return "SYM_SRC (100BaseTx)"; case 0x0205: return "SYM_SCR"; case 0x0006: return "100Base-T4"; case 0x0007: return "100Base-FX"; case 0x0208: return "100Base-FXFD"; case 0x0009: return "MII TP"; case 0x020a: return "MII TP (full-dulex)"; case 0x000d: return "MII (100Base-Tx)"; case 0x020e: return "MII (100Base-Tx full duplex)"; case 0x000f: return "MII (100Base-T4"; case 0x0010: return "MII (100Base-FX 100Mb/s fiber)"; case 0x0211: return "MII (100Base-FX 100Mb/s fiber full duplex)"; case 0x0800: return "Power up AutoSense (dynamic)"; case 0x8800: return "Power up AutoSense"; case 0xffff: return "No selected media interface"; default: return String.Format("Unknown ({0})", connectionType); } } private String MediaCodeName(uint mediaCode) { switch (mediaCode) { case 0: return "TP (10MB/s)"; case 1: return "BNC (10MB/s)"; case 3: return "SYM_SCR (100Base-Tx)"; case 4: return "TP full-duplex"; case 5: return "SYM_SCR full-duplex (100Base-Tx)"; case 6: return "100Base-T4"; case 7: return "100Base-FX"; case 8: return "100Base-FXFD"; default: return String.Format("Unknown ({1})", mediaCode); } } private byte[]! GetEthernetAddressBytes(EthernetAddress address) { byte []! data = (!)address.GetAddressBytes(); return data; } // // Setup Frame related methods // /// /// Constructs a perfect filtering setup frame buffer. /// /// /// The addresses to be present /// in the perfect filter up to a maximum of 16. private byte[]! MakePerfectFilteringFrame(EthernetAddress[]! addresses) requires addresses.Length <= 16; { // // Ref: pages 4.20-4.23 // // Frame has 192 bytes and is laid out thus: // // <3:0> XX XX a0[1] a0[0] XX = don't care // <7:4> XX XX a0[3] a0[2] aN = ethernet address N // <11:8> XX XX a0[5] a0[4] aN[i] = i'th byte of address N // <15:12> XX XX a1[1] a1[0] // <19:16> XX XX a1[3] a1[2] // <23:20> XX XX a1[5] a1[4] // ... // <183:180> XX XX a15[1] a15[0] // <187:184> XX XX a15[3] a15[2] // <191:188> XX XX a15[5] a15[4] // // If there are less than 16 addresses to go in the frame, // addresses should be repeated to fill all 16 slots. // // Frame is generated in little endian ordering. // const int MaxOutIndex = 16; int addressIndex = 0; int outIndex = 0; byte []! frame = new byte [SetupFrameBytes]; // 192 bytes while (outIndex != MaxOutIndex) { int start = outIndex * 6 * 2; byte []! address = GetEthernetAddressBytes( addresses[addressIndex]); frame[start + 0] = address[0]; frame[start + 1] = address[1]; frame[start + 4] = address[2]; frame[start + 5] = address[3]; frame[start + 8] = address[4]; frame[start + 9] = address[5]; addressIndex = (addressIndex + 1) % addresses.Length; outIndex ++; } return frame; } private bool WriteStandardSetupFrame() { txRing.WriteSetupFrame( MakePerfectFilteringFrame( new EthernetAddress [] { macAddress, EthernetAddress.Broadcast } ), FilteringType.Perfect ); TransmitPollDemand(); for (int i = 0; i < 100; i++) { if (txRing.PollSetupFrameCompleted()) { return true; } Thread.Sleep(TimeSpan.FromMilliseconds(5)); } DebugStub.Break(); return false; } } }