singrdk/base/Drivers/Network/Intel/Intel.sg

920 lines
32 KiB
Plaintext

//////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Notes:
//
// Simple Driver for Intel 8254x PCI Ethernet Cards.
//
// Useful reference URLs:
// http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
//
// We use standard IP/TCP checksum offloading.
//
// The driver currently runs in interrupt driven mode using hardware based
// interrupt throttling.
//
// Phy handling is automatic and relies on attached phy supporting
// auto-negotiation. The phy update interrupt is used to keep track
// of phy state.
//
// TODO:
//
// - Flow Control Support
// - Support for packet fragments (all packets must have a single fragment
// currently)
// - Jumbo Packets
// - Transmit TCP/IP checksum offloading
//
//#define DEBUG_INTEL
using System;
using System.Threading;
using System.Diagnostics;
using System.Collections;
using Microsoft.Contracts;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Configuration;
using Microsoft.Singularity.Io.Net;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.Drivers;
using Microsoft.SingSharp;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Extending;
using Drivers.Net;
namespace Microsoft.Singularity.Drivers.Network.Intel
{
[CLSCompliant(false)]
public class Intel
{
internal const uint MaxTxFragmentsPerPacket = 1; // Todo: allow frags
internal const uint MaxRxFragmentsPerPacket = 1; // Todo: allow frags
internal const uint MaxRxPackets = 1023; // one less than full buffer
internal const uint MaxTxPackets = 2047;
// Fragments must be power of two.
internal const uint MaxTxFragmentsInRing = ((MaxTxPackets + 1) *
MaxTxFragmentsPerPacket) ;
internal const uint MaxRxFragmentsInRing = ((MaxRxPackets + 1) *
MaxRxFragmentsPerPacket);
internal const ChecksumSupport ChecksumSupport =
ChecksumSupport.AllIp4Recieve | ChecksumSupport.AllIp6Recieve;
internal const uint IEEE8023FrameBytes = 1518;
internal const uint MtuBytes = 1514;
internal const uint PhyAddress = 1;
private string! cardName;
private CardType cardType;
private PciDeviceConfig! pciConfig;
private IoMemory! ioMemory;
private IoIrq! irq;
private Thread irqWorkerThread;
private bool irqWorkerStop;
private bool ioRunning;
private IntelEventRelay eventRelay;
private EerdRegister! eerdReg;
private EthernetAddress macAddress;
private IntelRxRingBuffer! rxRingBuffer;
private IntelTxRingBuffer! txRingBuffer;
[NotDelayed]
internal Intel(IntelResources! res)
{
IoMemoryRange imr = (!)res.imr;
ioMemory = (!) imr.MemoryAtOffset(0, 0x20000, Access.ReadWrite);
IoIrqRange! iir = (!)res.irq;
irq = (!) iir.IrqAtOffset(0);
rxRingBuffer = new IntelRxRingBuffer(MaxRxFragmentsInRing);
txRingBuffer = new IntelTxRingBuffer(MaxTxFragmentsInRing);
this.cardName = res.CardName;
this.cardType = res.CardType;
PciDeviceConfig! config = (PciDeviceConfig!)IoConfig.GetConfig();
this.pciConfig = config;
DebugWriteLine("PCI Control {0:x8} Status {1:x8}",
__arglist(config.Control, config.Status));
byte cap = config.Capabilities;
while (cap != 0 && cap < 0xff) {
DebugWriteLine("Capability at: {0:x2}", __arglist(cap));
byte id = config.Read8(cap);
if (id == 7) {
DebugWriteLine("PCI-X Command {0:x4} Status {1:x8}",
__arglist(
config.Read16(cap + 2) & 0x3f,
config.Read32(cap + 4)
)
);
}
else if (id == 5) {
// MSI capability - only reached if enabled,
// but usually present.
DebugWriteLine("MSI Control {0:x4} Address {1:x8}.{2:x8} Data {3:x4}",
__arglist(config.Read16(cap + 2),
config.Read32(cap + 8),
config.Read32(cap + 4),
config.Read16(cap + 10)
)
);
}
else {
DebugWriteLine("Unknown capability {0:x2}", __arglist(id));
}
cap = config.Read8(cap + 1);
}
eerdReg = new EerdRegister(config.DeviceId);
base();
DebugWriteLine("Irq {0}", __arglist(irq.Irq));
Debug.Assert((config.Control & PciConfig.PCI_ENABLE_BUS_MASTER) != 0);
Debug.Assert((config.Control & PciConfig.PCI_ENABLE_MEMORY_SPACE) != 0);
DebugStub.Assert(config.InterruptsEnabled);
}
~Intel()
{
ReleaseResources();
}
///////////////////////////////////////////////////////////////////////
//
// Initialisation and finalisation code
//
internal void Initialize()
{
DebugPrint("Initialising " + DriverName + " Version " + DriverVersion);
if (!irq.RegisterInterrupt()) {
DebugStub.Break();
}
ResetDevice();
SetupPhys();
SetupMac();
}
void ReleaseResources()
{
DisableInterrupts();
StopIo();
irq.ReleaseInterrupt();
irqWorkerThread = null;
}
internal void Shutdown()
{
if (irqWorkerThread != null) {
// XXX This is not the right way to do this.
DisableInterrupts();
StopIo();
}
}
internal void StartIo()
{
irqWorkerStop = false;
irqWorkerThread = new Thread(new ThreadStart(this.IrqWorkerMain));
irqWorkerThread.Start();
SetupMac();
EnableInterrupts();
StartReceiver();
StartTransmitter();
this.ioRunning = true;
}
internal void StopIo()
{
StopTransmitter();
StopReceiver();
if (irqWorkerThread != null) {
// Set stop flag, wake-up irqWorker thread, then wait.
irqWorkerStop = true;
irq.Pulse();
irqWorkerThread.Join();
irqWorkerThread = null;
}
this.ioRunning = false;
}
internal void SetEventRelay(IntelEventRelay intelEventRelay)
{
eventRelay = intelEventRelay;
}
///////////////////////////////////////////////////////////////////////
//
// Setup functions
//
private void ResetDevice()
{
// Disable all interrupts
DisableInterrupts();
DebugWriteLine("CTRL pre-device-reset : {0:x8}\n",
__arglist(Read32(Register.CTRL)));
Write32(Register.RECV_CTRL, 0);
Write32(Register.TSMT_CTRL, TsmtCtrlBits.PAD_SHORT_PACKETS);
Read32(Register.STATUS);
// Allow pending PCI transactions to complete
Delay(10);
// Reset the device
RegSetBits(Register.CTRL, CtrlBits.RST | CtrlBits.PHY_RST);
// Wait for 3us before board is really reset
Delay(3);
Delay(100);
Read32(Register.CTRL);
// Set the control register to the proper initial values,
// clearing RST and PHY_RST as a side-effect.
Write32(Register.CTRL, CtrlBits.FD | CtrlBits.ASDE | CtrlBits.SLU);
while ((Read32(Register.CTRL) & CtrlBits.RST) != 0) {
DebugWriteLine(".");
}
DebugWriteLine("Autonegotiation complete");
}
private void SetupPhys()
{
if (this.cardType == CardType.I82545GM ) {
Write32(Register.MDIC, Mdic.MdiRead);
while ((Read32(Register.MDIC) & Mdic.Ready) == 0);
uint mdic = Read32(Register.MDIC) & Mdic.DataMask;
mdic |= Mdic.MdiWrite;
mdic &= Mdic.PowerMask;
Write32(Register.MDIC, mdic);
while ((Read32(Register.MDIC) & Mdic.Ready) == 0);
DumpPhy();
uint ctrl = Read32(Register.CTRL);
ctrl |= CtrlBits.SLU | CtrlBits.ASDE;
ctrl &= ~(CtrlBits.ILOS | CtrlBits.FRCSPD | CtrlBits.FRCDPLX);
Write32(Register.CTRL, ctrl);
Delay(20);
// Wait for link to come up
int attempts = 5000;
while ((MiiRead(PhyAddress, 1) & 0x4) == 0) {
Delay(1000);
if (0 == attempts--) {
DumpPhy();
DebugStub.Break();
}
}
DumpPhy();
uint phyCtrl = MiiRead(PhyAddress, 0);
phyCtrl |= 0x1200; // Enable-AutoNeg + Restart-AutoNeg
MiiWrite(PhyAddress, 0, phyCtrl);
uint phyStat;
attempts = 5000;
do {
Delay(1000);
phyStat = MiiRead(PhyAddress, 1);
phyStat = MiiRead(PhyAddress, 1);
} while ((phyStat & 0x20) == 0 && --attempts > 0); // wait for autoneg complete
DebugStub.Assert((phyStat & 0x20) != 0);
// Transfer settings from phy to ctrl
uint phyPssr = MiiRead(PhyAddress, 17);
ctrl = Read32(Register.CTRL);
ctrl &= ~(CtrlBits.SPEED | CtrlBits.FD);
if ((phyPssr & (1 << 13)) != 0) {
ctrl |= CtrlBits.FD;
}
ctrl |= ((phyPssr >> 14) & 3) << 8;
ctrl |= CtrlBits.FRCSPD | CtrlBits.FRCDPLX;
Write32(Register.CTRL, ctrl);
Delay(1000000);
DumpPhy();
}
else if (this.cardType == CardType.I82541PI) {
// Use Internal Phys mode (SerDes is not possible for some 8254x cards)
Write32BitRange(Register.CTRL_EXT,
CtrlExtBits.LINK_MODE_PHYS,
CtrlExtBits.LINK_MODE_LO_BIT,
CtrlExtBits.LINK_MODE_HI_BIT);
}
// We don't want flow control
Write32(Register.FCAL, 0x0);
Write32(Register.FCAH, 0x0);
Write32(Register.FCT, 0x0);
Write32(Register.FCTTV, 0x0);
}
private void SetupMac()
{
uint ral, rah;
// Setup our Ethernet address
macAddress = GetMacFromEeprom();
DebugPrint("Setting Ethernet Mac Address to {0:s}\n",
__arglist(macAddress.ToString()));
GetMacHiLow(out ral, out rah, macAddress);
rah = rah | RahRegister.ADDRESS_VALID;
Write32(Register.RAL0, ral);
Write32(Register.RAH0, rah);
// Clear the mutlicast table array
for (uint i = 0; i < MtaRegister.MTA_LENGTH; i++)
{
Write32(Register.MTA_START + (4*i), 0);
}
// Setup Descriptor buffers for rx
ResetRxRingBuffer();
// Setup Receiever Control flags
Write32(Register.RECV_CTRL, (RecvCtrlBits.BROADCAST_ACCEPT |
RecvCtrlBits.STRIP_CRC |
RecvCtrlBits.LOOPBACK_MODE_DISABLE |
RecvCtrlBits.MULTICAST_OFFSET_47_36 |
RecvCtrlBits.BUFFER_SIZE_2KB |
RecvCtrlBits.RECV_DESC_THRESHOLD_QUARTER));
// Note: If MTU ever changes (e.g. for jumbo frames), the
// recv buffer size will need to be increased.
// Setup the rx interrupt delay
Write32(Register.RECV_DELAY_TIMER, RxDelayTimers.RECV_DELAY_TIMER);
Write32(Register.RECV_INT_ABS_TIMER, RxDelayTimers.RECV_ABSOLUTE_TIMER);
// Enable IP and TCP checksum calculation offloading
Write32(Register.RECV_CHECKSUM, (RecvChecksumBits.IP_CHECKSUM_ENABLE |
RecvChecksumBits.TCP_CHECKSUM_ENABLE |
RecvChecksumBits.IP6_CHECKSUM_ENABLE));
// Setup Descriptor buffers for tx
ResetTxRingBuffer();
// Setup Transmit Control flags
Write32(Register.TSMT_CTRL,
TsmtCtrlBits.PAD_SHORT_PACKETS |
TsmtCtrlBits.COLL_THRESHOLD_DEFAULT |
TsmtCtrlBits.COLL_DISTANCE_DEFAULT);
// Setup Transmit Inter Frame Gap
Write32(Register.TSMT_IPG, TsmtIpg.DEFAULT_IPG);
// TODO enable transmit checksum offloading
}
///////////////////////////////////////////////////////////////////////
//
// Helper functions
//
internal void GetMacHiLow(out uint addr_low,
out uint addr_hi,
EthernetAddress mac)
{
byte[]! macBytes = mac.GetAddressBytes();
addr_hi = (uint) ((macBytes[5] << 8) |
(macBytes[4]));
addr_low = (uint) ((macBytes[3] << 24) |
(macBytes[2] << 16) |
(macBytes[1] << 8) |
(macBytes[0]));
}
internal EthernetAddress GetMacFromEeprom()
{
ushort eepromData;
byte[] macBytes = new byte[6];
// Mac address is in reverse byte order in the EEPROM
eepromData = ReadEepromWord(0);
macBytes[0] = (byte) (eepromData & 0xff);
macBytes[1] = (byte) (eepromData >> 8);
eepromData = ReadEepromWord(1);
macBytes[2] = (byte) (eepromData & 0xff);
macBytes[3] = (byte) (eepromData >> 8);
eepromData = ReadEepromWord(2);
macBytes[4] = (byte) (eepromData & 0xff);
macBytes[5] = (byte) (eepromData >> 8);
return new EthernetAddress(macBytes);
}
private ushort ReadEepromWord(ushort eepromAddress)
{
uint eepromRead;
// Write address required
uint writeVal = (EerdRegister.Start |
((uint) eepromAddress << eerdReg.AddressShift));
Write32(Register.EERD, writeVal);
// wait until read has completed
do {
eepromRead = Read32(Register.EERD);
} while ((eepromRead & eerdReg.Done) == 0);
// return data value
return (ushort) (eepromRead >> EerdRegister.DataShift);
}
///////////////////////////////////////////////////////////////////////
//
// Interrupt Handling
//
internal void EnableInterrupts()
{
// Clear existing interrupts
Write32(Register.IMC, 0xffffffff);
Read32(Register.ICR);
// Set interrupts we are interested in
RegSetBits(Register.IMS, (InterruptMasks.RXT0 | InterruptMasks.RXO |
InterruptMasks.RXDMT0 |
InterruptMasks.LSC | InterruptMasks.TXQE |
InterruptMasks.TXDW));
}
internal void DisableInterrupts()
{
// Clear existing interrupts
Write32(Register.IMC, 0xffffffff);
Read32(Register.ICR);
}
private bool HandleInterrupts(uint intrCause)
{
if (intrCause == 0) {
return false;
}
NicEventType ev = NicEventType.NoEvent;
if ((intrCause & InterruptMasks.LSC) != 0) {
DebugPrint("Link Status Change\n");
ev |= NicEventType.LinkEvent;
}
if ((intrCause & InterruptMasks.RXSEQ) != 0) {
DebugPrint("Sequence Error\n");
ev |= NicEventType.LinkEvent;
}
if (((InterruptMasks.RXT0 |
InterruptMasks.RXO |
InterruptMasks.RXDMT0) & intrCause) != 0) {
ev |= NicEventType.ReceiveEvent;
}
// no transmit interupts are set, check rxbuffer to see
// if any packets have been sent since last time
if (txRingBuffer.NewTransmitEvent()) {
ev |= NicEventType.TransmitEvent;
}
if (eventRelay != null) {
eventRelay.ForwardEvent(ev);
}
return true;
}
private void IrqWorkerMain()
{
DebugPrint(
"Intel {0} Ethernet Driver irq worker thread started.\n",
__arglist(this.cardName)
);
while (irqWorkerStop == false) {
irq.WaitForInterrupt();
uint icr = Read32(Register.ICR);
HandleInterrupts(icr);
irq.AckInterrupt();
}
DisableInterrupts();
DebugPrint(
"Intel {0} Ethernet Driver irq worker thread stopped.\n",
__arglist(this.cardName)
);
}
///////////////////////////////////////////////////////////////////////
//
// Rx buffer operations
//
internal void PopulateRecvBuffer(PacketFifo*! in ExHeap fromUser)
{
lock (rxRingBuffer) {
while (fromUser->Count > 0) {
rxRingBuffer.LockedPushRecvBuffer(fromUser->Pop());
}
Write32(Register.RECV_DESC_TAIL, rxRingBuffer.Head);
}
}
internal void DrainRecvBuffer(PacketFifo*! in ExHeap toUser)
{
lock (rxRingBuffer) {
rxRingBuffer.LockedDrainRecvBuffer(toUser);
}
}
private void ResetRxRingBuffer()
{
ulong descBase;
uint descBaseLo, descBaseHi;
rxRingBuffer.Reset();
descBase = rxRingBuffer.BaseAddress.ToUInt64();
descBaseLo = (uint)(0xffffffff & descBase);
descBaseHi = (uint) (0xffffffff & (descBase >> 32));
Write32(Register.RECV_DESC_BASE_LO, ByteOrder.HostToLittleEndian(descBaseLo));
Write32(Register.RECV_DESC_BASE_HI, ByteOrder.HostToLittleEndian(descBaseHi));
Write32(Register.RECV_DESC_LENGTH, ByteOrder.HostToLittleEndian(rxRingBuffer.DescLength));
Write32(Register.RECV_DESC_HEAD, ByteOrder.HostToLittleEndian(rxRingBuffer.Head));
Write32(Register.RECV_DESC_TAIL, ByteOrder.HostToLittleEndian(rxRingBuffer.Tail));
}
private void StartReceiver()
{
ResetRxRingBuffer();
RegSetBits(Register.RECV_CTRL, RecvCtrlBits.RECV_ENABLE);
DebugPrint("Receiver Enabled.\n");
}
private void StopReceiver()
{
RegClrBits(Register.RECV_CTRL, RecvCtrlBits.RECV_ENABLE);
}
///////////////////////////////////////////////////////////////////////
//
// Tx buffer operations
//
internal void PopulateTsmtBuffer(PacketFifo*! in ExHeap fromUser)
{
DebugStub.Assert(this.ioRunning);
// since no transmit interrupts are sent, we must check for
// transmission events here
if (txRingBuffer.NewTransmitEvent()) {
NicEventType ev = NicEventType.TransmitEvent;
if (eventRelay != null) {
eventRelay.ForwardEvent(ev);
}
}
lock (txRingBuffer) {
while (fromUser->Count > 0) {
Packet*! in ExHeap packet = fromUser->Pop();
txRingBuffer.LockedPushTsmtBuffer(packet);
}
// update hardware tail pointer
// so that hardware knows it has new packets to transmit
Write32(Register.TSMT_DESC_TAIL, txRingBuffer.Head); // sw head is hw tail
}
}
internal void DrainTsmtBuffer(PacketFifo*! in ExHeap toUser)
{
lock (txRingBuffer) {
txRingBuffer.LockedDrainTsmtBuffer(toUser);
}
}
private void ResetTxRingBuffer()
{
ulong descBase;
uint descBaseLo, descBaseHi;
txRingBuffer.Reset();
descBase = txRingBuffer.BaseAddress.ToUInt64();
descBaseLo = (uint)(0xffffffff & descBase);
descBaseHi = (uint) (0xffffffff & (descBase >> 32));
Write32(Register.TSMT_DESC_BASE_LO, ByteOrder.HostToLittleEndian(descBaseLo));
Write32(Register.TSMT_DESC_BASE_HI, ByteOrder.HostToLittleEndian(descBaseHi));
Write32(Register.TSMT_DESC_LENGTH, ByteOrder.HostToLittleEndian(txRingBuffer.DescLength));
Write32(Register.TSMT_DESC_HEAD, ByteOrder.HostToLittleEndian(txRingBuffer.Head));
Write32(Register.TSMT_DESC_TAIL, ByteOrder.HostToLittleEndian(txRingBuffer.Tail));
}
private void StartTransmitter()
{
ResetTxRingBuffer();
RegSetBits(Register.TSMT_CTRL, TsmtCtrlBits.TSMT_ENABLE);
DebugPrint("Transmitter Enabled.\n");
}
private void StopTransmitter()
{
RegClrBits(Register.TSMT_CTRL, TsmtCtrlBits.TSMT_ENABLE);
}
///////////////////////////////////////////////////////////////////////
//
// Driver Details
//
internal string! DriverName
{
get { return string.Format("Intel {0} Ethernet Driver", this.cardName); }
}
internal string! DriverVersion
{
get { return "0.1"; }
}
internal EthernetAddress getMacAddress
{
get { return macAddress; }
}
///////////////////////////////////////////////////////////////////////
//
// MII
//
private uint MiiRead(uint phy, uint register)
{
uint mdic = (((phy & Mdic.PhyMask) << Mdic.PhyRoll) |
((register & Mdic.RegMask) << Mdic.RegRoll) |
Mdic.MdiRead);
Write32(Register.MDIC, mdic);
do {
mdic = Read32(Register.MDIC);
DebugStub.Assert((mdic & Mdic.Error) == 0);
} while ((mdic & Mdic.Ready) == 0);
return mdic & Mdic.DataMask;
}
private uint MiiWrite(uint phy, uint register, uint value)
{
DebugStub.Assert((value & ~Mdic.DataMask) == 0);
uint mdic = (((phy & Mdic.PhyMask) << Mdic.PhyRoll) |
((register & Mdic.RegMask) << Mdic.RegRoll) |
Mdic.MdiWrite);
mdic |= (uint)value;
Write32(Register.MDIC, mdic);
do {
mdic = Read32(Register.MDIC);
DebugStub.Assert((mdic & Mdic.Error) == 0);
} while ((mdic & Mdic.Ready) == 0);
return mdic & Mdic.DataMask;
}
///////////////////////////////////////////////////////////////////////
//
// Register accessors / modifiers / utilities
//
private uint Read32(uint offset)
{
return ioMemory.Read32((int) offset);
}
private void Write32(uint offset, uint value)
{
ioMemory.Write32((int) offset, value);
}
private void Write32BitRange(uint offset,
uint new_val,
int hiBit,
int loBit)
{
uint write_val = SetValueBits(Read32(offset), new_val, hiBit, loBit);
Write32(offset, write_val);
}
private void RegSetBits(int offset, uint bits)
{
ioMemory.Write32(offset, ioMemory.Read32(offset) | bits);
}
private void RegClrBits(int offset, uint bits)
{
ioMemory.Write32(offset, ioMemory.Read32(offset) & ~bits);
}
private uint SetValueBits(uint value,
uint bits,
int hiBit,
int loBit)
{
int width = (hiBit - loBit) + 1;
uint mask = (1u << width) - 1u;
bits = (bits & mask);
value &= ~(mask << loBit);
value |= bits << loBit;
return value;
}
private void SetBit(ref uint value, int bit)
{
value = value | (1u << bit);
}
private void ClearBit(ref uint value, int bit)
{
value = value & ~(1u << bit);
}
private static void Delay(int us)
{
long expiry = ProcessService.GetUpTime().Ticks + (us * 10);
while (ProcessService.GetUpTime().Ticks < expiry);
}
///////////////////////////////////////////////////////////////////////
//
// Debug Helper Functions
//
[Conditional("DEBUG_INTEL")]
internal static void DebugPrint(string format, __arglist)
{
DebugStub.Print(format, new ArgIterator(__arglist));
}
[Conditional("DEBUG_INTEL")]
internal static void DebugPrint(string format)
{
DebugStub.Print(format);
}
[Conditional("DEBUG_INTEL")]
internal static void DebugWriteLine(string format, __arglist)
{
DebugStub.WriteLine(format, new ArgIterator(__arglist));
}
[Conditional("DEBUG_INTEL")]
internal static void DebugWriteLine(string format)
{
DebugStub.WriteLine(format);
}
///////////////////////////////////////////////////////////////////////
//
// Debugging methods
//
[Conditional("DEBUG_INTEL")]
private void DumpBufferDebugRegisters()
{
// TODO, uses Tracing log
DebugPrint("Device Control {0:x8} Device Status {1:x8}\n",
__arglist(Read32(Register.CTRL),
Read32(Register.STATUS)));
DebugPrint("PCI Status {0:x4}", __arglist(this.pciConfig.Status));
DebugPrint("Recv Control {0:x8} Tsmt Control {1:x8}\n",
__arglist(Read32(Register.RECV_CTRL),
Read32(Register.TSMT_CTRL)));
DebugPrint("RDTR {0:x8} RADV {1:x8}\n",
__arglist(Read32(0x2820), Read32(0x282c)));
DebugPrint("Total Transmit {0:x8} Total Received {1:x8}\n",
__arglist(Read32(Register.TOTAL_TSMT_PACKETS),
Read32(Register.TOTAL_RECV_PACKETS)));
DebugPrint("Interrupt Mask {0:x8} Rx Error Count {1:x8}\n",
__arglist(Read32(Register.IMS),
Read32(Register.RX_ERR_COUNT)));
DebugPrint("Recv Addr High {0:x8} Recv Addr Low {1:x8}\n",
__arglist(Read32(Register.RAH0),
Read32(Register.RAL0)));
DebugPrint("Recv Desc Head {0:x8} Recv Desc Tail {1:x8}\n",
__arglist(Read32(Register.RECV_DESC_HEAD),
Read32(Register.RECV_DESC_TAIL)));
DebugPrint("Tsmt Desc Head {0:x8} Tsmt Desc Tail {1:x8}\n",
__arglist(Read32(Register.TSMT_DESC_HEAD),
Read32(Register.TSMT_DESC_TAIL)));
}
[Conditional("DEBUG_INTEL")]
private void DumpPhy()
{
for (uint i = 0; i < 32; i += 8) {
DebugWriteLine("PHY {0:x4} : {1:x4} {2:x4} {3:x4} {4:x4} {5:x4} {6:x4} {7:x4} {8:x4}",
__arglist(i,
MiiRead(PhyAddress, i + 0),
MiiRead(PhyAddress, i + 1),
MiiRead(PhyAddress, i + 2),
MiiRead(PhyAddress, i + 3),
MiiRead(PhyAddress, i + 4),
MiiRead(PhyAddress, i + 5),
MiiRead(PhyAddress, i + 6),
MiiRead(PhyAddress, i + 7))
);
}
}
#if DEBUG_INTEL
// Occasionally helpful when debugging.
Thread statsThread = null;
internal void StartStatistics()
{
DebugStub.Assert(statsThread == null);
statsThread = new Thread(new ThreadStart(this.StatisticsMain));
statsThread.Start();
}
internal void ReportChanges(uint[]! now)
{
DebugWriteLine("Changes.");
for (int i = 0; i < now.Length; i++) {
if (now[i] != 0) {
DebugWriteLine("{0} [0x40{1:x1}] -> {2}",
__arglist(i, i * 4, now[i]));
}
}
rxRingBuffer.Dump();
DebugWriteLine("Rx head {0:x8} tail {1:x8}",
__arglist(Read32(Register.RECV_DESC_HEAD),
Read32(Register.RECV_DESC_TAIL)));
DebugWriteLine("ICS = {0:x8} ICR = {1:x8}",
__arglist(Read32(Register.ICS),
Read32(Register.ICR)));
}
internal void StatisticsMain()
{
uint []! counters1 = new uint[64];
for (;;) {
Thread.Sleep(TimeSpan.FromSeconds(10));
GetStatistics(counters1);
ReportChanges(counters1);
}
}
internal void GetStatistics(uint []! counters)
{
for (uint i = 0; i < counters.Length; i++) {
counters[i] = Read32(0x4000 + i * 4);
}
}
#endif
}
}