///////////////////////////////////////////////////////////////////////////////
//
// 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(IoPortRange! csrRange,
IoMemoryRange! memRange,
IoIrqRange! irqRange)
{
TulipDebug.Print("Tulip: Initialize() called\n");
csr0 = (!)csrRange.PortAtOffset(0x00, 4, Access.ReadWrite);
csr1 = (!)csrRange.PortAtOffset(0x08, 4, Access.Write);
csr2 = (!)csrRange.PortAtOffset(0x10, 4, Access.Write);
csr3 = (!)csrRange.PortAtOffset(0x18, 4, Access.ReadWrite);
csr4 = (!)csrRange.PortAtOffset(0x20, 4, Access.ReadWrite);
csr5 = (!)csrRange.PortAtOffset(0x28, 4, Access.ReadWrite);
csr6 = (!)csrRange.PortAtOffset(0x30, 4, Access.ReadWrite);
csr7 = (!)csrRange.PortAtOffset(0x38, 4, Access.ReadWrite);
csr8 = (!)csrRange.PortAtOffset(0x40, 4, Access.Read);
csr9 = (!)csrRange.PortAtOffset(0x48, 4, Access.ReadWrite);
csr10 = (!)csrRange.PortAtOffset(0x50, 4, Access.Read);
csr11 = (!)csrRange.PortAtOffset(0x58, 4, Access.ReadWrite);
csr12 = (!)csrRange.PortAtOffset(0x60, 4, Access.ReadWrite);
csr15 = (!)csrRange.PortAtOffset(0x78, 4, Access.ReadWrite);
irq = (!)irqRange.IrqAtOffset(0);
TulipDebug.Print("Tulip IRQ {0}\n", __arglist(irqRange.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();
}
// Configure bus mode register
uint cls = config.CacheLineSize; // # of DWORDS
uint cacheBits = CSR0.WIE | CSR0.RLE | CSR0.RME;
uint cal = 0; // cache alignment
uint pbl = 32; // # of DWORDS to burst (>= cal)
switch (cls) {
case 8:
cal = 1;
break;
case 16:
cal = 2;
break;
case 32:
cal = 3;
break;
default:
cacheBits = 0;
cal = 0;
pbl = 0;
break;
}
InitChains();
// Write CSR0 to set global host bus operating parameters
csr0.Write32(cacheBits | CSR0.BAR |
(cal << CSR0.CAL_ROLL) |
(pbl << 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;
}
}
}