901 lines
33 KiB
Plaintext
901 lines
33 KiB
Plaintext
|
///////////////////////////////////////////////////////////////////////////////
|
|||
|
//
|
|||
|
// 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
|
|||
|
//
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructs a perfect filtering setup frame buffer.
|
|||
|
/// </summary>
|
|||
|
///
|
|||
|
/// <param name="addresses">The addresses to be present
|
|||
|
/// in the perfect filter up to a maximum of 16.</param>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|