920 lines
32 KiB
Plaintext
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
|
|
}
|
|
}
|