diff --git a/PUP/Conf/ifs.cfg b/PUP/Conf/ifs.cfg
index a890586..5395c09 100644
--- a/PUP/Conf/ifs.cfg
+++ b/PUP/Conf/ifs.cfg
@@ -10,11 +10,9 @@ CopyDiskRoot = c:\ifs\copydisk
BootRoot = c:\ifs\boot
MailRoot = c:\ifs\mail
-# InterfaceType defines the type of interface for local networking,
-# (either RAW or UDP)
-# and must be the same as other devices or software on the network that
-# wishes to talk to this server.
-InterfaceType = raw
+# InterfaceType defines the type of interface(s) to use for local networking,
+# (one or more of RAW, UDP, or 3MBIT)
+InterfaceTypes = raw
# The name of the network interface to use for local networking.
# This is the name reported by "ipconfig"
@@ -25,6 +23,9 @@ InterfaceName = Ethernet
# (gateway ports are specified in networks.txt)
UDPPort = 42424
+# Whether to run IFS services or just bridge interfaces
+RunIFSServices = true
+
# Defines the address for this IFS server.
# An entry for this IFS server's network must be
# present in networks.txt
diff --git a/PUP/Configuration.cs b/PUP/Configuration.cs
index 5507be8..ab4bf2e 100644
--- a/PUP/Configuration.cs
+++ b/PUP/Configuration.cs
@@ -83,9 +83,9 @@ namespace IFS
}
///
- /// The type of interface (UDP or RAW) to communicate over
+ /// The type of interface(s) (UDP, RAW, or 3mbit) to communicate over
///
- public static readonly string InterfaceType;
+ public static readonly string InterfaceTypes;
///
/// The name of the network interface to use
@@ -97,6 +97,11 @@ namespace IFS
///
public static readonly int UDPPort;
+ ///
+ /// Whether to run IFS Services or just bridge interfaces.
+ ///
+ public static readonly bool RunIFSServices;
+
///
/// The network that this server lives on
///
@@ -208,6 +213,13 @@ namespace IFS
{
switch (field.FieldType.Name)
{
+ case "Boolean":
+ {
+ bool b = bool.Parse(value);
+ field.SetValue(null, b);
+ }
+ break;
+
case "Int32":
{
int v = int.Parse(value);
@@ -248,7 +260,7 @@ namespace IFS
"ifs.cfg line {0}: Unknown configuration parameter '{1}'.", lineNumber, parameter);
}
}
- }
+ }
}
}
}
diff --git a/PUP/Entrypoint.cs b/PUP/Entrypoint.cs
index 76abeaf..92acde4 100644
--- a/PUP/Entrypoint.cs
+++ b/PUP/Entrypoint.cs
@@ -15,20 +15,12 @@
along with IFS. If not, see .
*/
-using IFS.Boot;
-using IFS.CopyDisk;
-using IFS.FTP;
using IFS.Gateway;
using IFS.IfsConsole;
-using IFS.Transport;
using PcapDotNet.Core;
using PcapDotNet.Core.Extensions;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.NetworkInformation;
-using System.Text;
-using System.Threading.Tasks;
namespace IFS
{
@@ -36,9 +28,9 @@ namespace IFS
{
static void Main(string[] args)
{
- PrintHerald();
+ PrintHerald();
- RegisterInterface();
+ RegisterInterfaces();
// This runs forever, or until the user tells us to exit.
RunCommandPrompt();
@@ -51,61 +43,69 @@ namespace IFS
private static void PrintHerald()
{
- Console.WriteLine("LCM+L IFS v0.3, 11/17/2020.");
- Console.WriteLine("(c) 2015-2020 Living Computers: Museum+Labs.");
- Console.WriteLine("Bug reports to joshd@livingcomputers.org");
+ Console.WriteLine($"LCM+L IFS {typeof(Entrypoint).Assembly.GetName().Version}, 9/30/2023");
+ Console.WriteLine("(c) 2015-2020 Living Computers: Museum+Labs, 2020-2023 Josh Dersch");
Console.WriteLine();
Console.WriteLine();
- }
+ }
- private static void RegisterInterface()
+ private static void RegisterInterfaces()
{
bool bFound = false;
- switch (Configuration.InterfaceType.ToLowerInvariant())
+ string[] selectedInterfaces = Configuration.InterfaceTypes.ToLowerInvariant().Split(new char[] { ' ', '\t', ',' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string ifaceName in selectedInterfaces)
{
- case "udp":
- // Find matching network interface
- {
- NetworkInterface[] ifaces = NetworkInterface.GetAllNetworkInterfaces();
- foreach(NetworkInterface iface in ifaces)
+ switch (ifaceName)
+ {
+ case "udp":
+ // Find matching network interface
{
- if (iface.Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
+ NetworkInterface[] ifaces = NetworkInterface.GetAllNetworkInterfaces();
+ foreach (NetworkInterface iface in ifaces)
{
- Router.Instance.RegisterUDPInterface(iface);
- bFound = true;
- break;
+ if (iface.Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
+ {
+ Router.Instance.RegisterUDPInterface(iface);
+ bFound = true;
+ break;
+ }
}
}
- }
- break;
+ break;
- case "raw":
- // Find matching RAW interface
- {
- foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
+ case "raw":
+ // Find matching RAW interface
{
- if (device.GetNetworkInterface() != null &&
- device.GetNetworkInterface().Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
+ foreach (LivePacketDevice device in LivePacketDevice.AllLocalMachine)
{
- Router.Instance.RegisterRAWInterface(device);
- bFound = true;
- break;
+ if (device.GetNetworkInterface() != null &&
+ device.GetNetworkInterface().Name.ToLowerInvariant() == Configuration.InterfaceName.ToLowerInvariant())
+ {
+ Router.Instance.RegisterRAWInterface(device);
+ bFound = true;
+ break;
+ }
}
- }
- }
- break;
+ }
+ break;
- default:
+ case "3mbit":
+ Router.Instance.RegisterBeagleBoneInterface();
+ break;
+
+ default:
+ throw new InvalidConfigurationException(
+ String.Format("The specified interface type ({0}) is invalid.", Configuration.InterfaceTypes));
+ }
+
+ // Not found.
+ if (!bFound)
+ {
throw new InvalidConfigurationException(
- String.Format("The specified interface type ({0}) is invalid.", Configuration.InterfaceType));
- }
-
- // Not found.
- if (!bFound)
- {
- throw new InvalidConfigurationException(
- String.Format("The specified network interface ({0}) is invalid or unusable by IFS.", Configuration.InterfaceName));
+ String.Format("The specified network interface ({0}) is invalid or unusable by IFS.", Configuration.InterfaceName));
+ }
}
}
diff --git a/PUP/Gateway/Router.cs b/PUP/Gateway/Router.cs
index cf7133d..a56d3ed 100644
--- a/PUP/Gateway/Router.cs
+++ b/PUP/Gateway/Router.cs
@@ -28,7 +28,7 @@ using System.Threading;
namespace IFS.Gateway
{
- public delegate void RoutePupCallback(PUP pup, bool route);
+ public delegate void ReceivedPacketCallback(MemoryStream packetStream, IPacketInterface receivingInterface);
///
/// Implements gateway services, routing PUPs intended for other networks to
@@ -50,6 +50,7 @@ namespace IFS.Gateway
{
_localProtocolDispatcher = new PUPProtocolDispatcher();
_routingTable = new RoutingTable();
+ _packetInterfaces = new List();
//
// Look up our own network in the table and get our port.
@@ -96,7 +97,11 @@ namespace IFS.Gateway
public void Shutdown()
{
_localProtocolDispatcher.Shutdown();
- _pupPacketInterface.Shutdown();
+
+ foreach (IPacketInterface iface in _packetInterfaces)
+ {
+ iface.Shutdown();
+ }
if (_gatewayUdpClient != null)
{
@@ -109,19 +114,22 @@ namespace IFS.Gateway
public void RegisterRAWInterface(LivePacketDevice iface)
{
Ethernet enet = new Ethernet(iface);
-
- _pupPacketInterface = enet;
- _rawPacketInterface = enet;
- _pupPacketInterface.RegisterRouterCallback(RouteIncomingLocalPacket);
+ _packetInterfaces.Add(enet);
+ enet.RegisterRouterCallback(HandleIncomingPacket);
}
public void RegisterUDPInterface(NetworkInterface iface)
{
UDPEncapsulation udp = new UDPEncapsulation(iface);
+ _packetInterfaces.Add(udp);
+ udp.RegisterRouterCallback(HandleIncomingPacket);
+ }
- _pupPacketInterface = udp;
- _rawPacketInterface = udp;
- _pupPacketInterface.RegisterRouterCallback(RouteIncomingLocalPacket);
+ public void RegisterBeagleBoneInterface()
+ {
+ Ether3MbitInterface bbInterface = new Ether3MbitInterface();
+ _packetInterfaces.Add(bbInterface);
+ bbInterface.RegisterRouterCallback(HandleIncomingPacket);
}
///
@@ -142,9 +150,8 @@ namespace IFS.Gateway
///
public void Send(byte[] data, byte source, byte destination, ushort frameType)
{
- if (_rawPacketInterface != null)
- {
- _rawPacketInterface.Send(data, source, destination, frameType);
+ foreach(IPacketInterface iface in _packetInterfaces) {
+ iface.Send(data, source, destination, frameType);
}
}
@@ -163,7 +170,10 @@ namespace IFS.Gateway
if (p.DestinationPort.Network == 0 ||
p.DestinationPort.Network == DirectoryServices.Instance.LocalNetwork)
{
- _pupPacketInterface.Send(p);
+ foreach (IPacketInterface iface in _packetInterfaces)
+ {
+ iface.Send(p);
+ }
}
else
{
@@ -204,6 +214,57 @@ namespace IFS.Gateway
}
}
+ ///
+ /// Handles an encapsulated 3mbit frame incoming from the receiver.
+ ///
+ ///
+ private void HandleIncomingPacket(MemoryStream packetStream, IPacketInterface receivingInterface)
+ {
+ // Read the length prefix (in words), convert to bytes.
+ // Subtract off 2 words for the ethernet header
+ int length = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte())) * 2 - 4;
+
+ // Read the address (1st word of 3mbit packet)
+ byte destination = (byte)packetStream.ReadByte();
+ byte source = (byte)packetStream.ReadByte();
+
+ // Read the type and switch on it
+ int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
+
+ //
+ // Ensure this is a packet we're interested in.
+ //
+ if (Configuration.RunIFSServices && // We're servicing packets
+ etherType3mbit == PupPacketBuilder.PupFrameType && // it's a PUP
+ (destination == DirectoryServices.Instance.LocalHost || // for us, or...
+ destination == 0)) // broadcast
+ {
+ try
+ {
+ PUP pup = new PUP(packetStream, length);
+ RouteIncomingLocalPacket(pup, destination != 0);
+ }
+ catch (Exception e)
+ {
+ // An error occurred, log it.
+ Log.Write(LogType.Error, LogComponent.PUP, "Error handling PUP: {0}", e.Message);
+ }
+ }
+ else if (!Configuration.RunIFSServices)
+ {
+ // Bridge the packet through all registered interfaces other than the one it came in on
+ foreach (IPacketInterface iface in _packetInterfaces)
+ {
+ if (iface != receivingInterface)
+ {
+ packetStream.Seek(0, SeekOrigin.Begin);
+ Console.WriteLine("Sending to {0}", iface);
+ iface.Send(packetStream);
+ }
+ }
+ }
+ }
+
private void RoutePacketExternally(PUP p)
{
RoutingTableEntry destinationNetworkEntry = _routingTable.GetAddressForNetworkNumber(p.DestinationPort.Network);
@@ -270,7 +331,10 @@ namespace IFS.Gateway
if (p.DestinationPort.Host == DirectoryServices.Instance.LocalHostAddress.Host || // us specifically
p.DestinationPort.Host == 0) // broadcast
{
- _localProtocolDispatcher.ReceivePUP(p);
+ if (Configuration.RunIFSServices)
+ {
+ _localProtocolDispatcher.ReceivePUP(p);
+ }
}
//
@@ -279,7 +343,10 @@ namespace IFS.Gateway
if (p.DestinationPort.Host != DirectoryServices.Instance.LocalHostAddress.Host || // not us
p.DestinationPort.Host == 0) // broadcast
{
- _pupPacketInterface.Send(p);
+ foreach (IPacketInterface iface in _packetInterfaces)
+ {
+ iface.Send(p);
+ }
}
}
else
@@ -395,14 +462,9 @@ namespace IFS.Gateway
}
///
- /// Our interface to a facility that can transmit/receive PUPs
+ /// The various interfaces we use to send and receive 3mbit ethernet packets, encapsulated or otherwise.
///
- private IPupPacketInterface _pupPacketInterface;
-
- ///
- /// Our interface to a facility that can transmit raw Ethernet frames
- ///
- private IRawPacketInterface _rawPacketInterface;
+ private List _packetInterfaces;
///
/// Our UdpClient for sending PUPs to external networks.
diff --git a/PUP/IFS.csproj b/PUP/IFS.csproj
index 6918367..9ecae83 100644
--- a/PUP/IFS.csproj
+++ b/PUP/IFS.csproj
@@ -124,11 +124,13 @@
+
+
@@ -151,6 +153,12 @@
PreserveNewest
+
+ Always
+
+
+ Always
+
diff --git a/PUP/Logging/Log.cs b/PUP/Logging/Log.cs
index ea44625..c4e4606 100644
--- a/PUP/Logging/Log.cs
+++ b/PUP/Logging/Log.cs
@@ -43,8 +43,9 @@ namespace IFS.Logging
UDP = 0x800,
Mail = 0x1000,
Routing = 0x2000,
+ E3Mbit = 0x4000,
- Configuration = 0x4000,
+ Configuration = 0x8000,
All = 0x7fffffff
}
diff --git a/PUP/MiscServicesProtocol.cs b/PUP/MiscServicesProtocol.cs
index 5698802..3621a73 100644
--- a/PUP/MiscServicesProtocol.cs
+++ b/PUP/MiscServicesProtocol.cs
@@ -24,8 +24,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Threading;
namespace IFS
{
@@ -71,7 +70,7 @@ namespace IFS
{
public MiscServicesProtocol()
{
-
+
}
///
@@ -139,8 +138,8 @@ namespace IFS
//
DateTime currentTime = DateTime.Now;
- byte[] timeString = Helpers.StringToArray(currentTime.ToString("dd-MMM-yy HH:mm:ss"));
-
+ byte[] timeString = Helpers.StringToArray(currentTime.ToString("dd-MMM-yy HH:mm:ss"));
+
PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.SourcePort.Socket);
PUP response = new PUP(PupType.StringTimeReply, p.ID, p.SourcePort, localPort, timeString);
@@ -148,7 +147,7 @@ namespace IFS
}
private void SendAltoTimeReply(PUP p)
- {
+ {
// So the Alto epoch is 1/1/1901. For the time being to keep things simple we're assuming
// GMT and no DST at all. TODO: make this take into account our TZ, etc.
//
@@ -167,8 +166,8 @@ namespace IFS
// The epoch for .NET is 1/1/0001 at 12 midnight and is counted in 100-ns intervals.
// Some conversion is needed, is what I'm saying.
- DateTime altoEpoch = new DateTime(1901, 1, 1);
-
+ DateTime altoEpoch = new DateTime(1901, 1, 1);
+
TimeSpan timeSinceAltoEpoch = new TimeSpan(currentTime.Ticks - altoEpoch.Ticks);
UInt32 altoTime = (UInt32)timeSinceAltoEpoch.TotalSeconds;
@@ -178,9 +177,9 @@ namespace IFS
time.DateTime = altoTime;
time.TimeZone = 0; // Hardcoded to GMT
time.DSTStart = 366; // DST not specified yet
- time.DSTEnd = 366;
+ time.DSTEnd = 366;
- PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
+ PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
// Response must contain our network number; this is used to tell clients what network they're on if they don't already know.
PUPPort remotePort = new PUPPort(DirectoryServices.Instance.LocalNetwork, p.SourcePort.Host, p.SourcePort.Socket);
@@ -218,7 +217,7 @@ namespace IFS
// We have a result, pack the name into the response.
// NOTE: This is *not* a BCPL string, just the raw characters.
byte[] interNetworkName = Helpers.StringToArray(hostName);
-
+
PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, p.DestinationPort.Socket);
PUP lookupReply = new PUP(PupType.AddressLookupResponse, p.ID, p.SourcePort, localPort, interNetworkName);
@@ -239,8 +238,8 @@ namespace IFS
{
//
// For the request PUP:
- // A string consisting of an inter-network name expression.
- // NOTE: This is *not* a BCPL string, just the raw characters.
+ // A string consisting of an inter-network name expression.
+ // NOTE: This is *not* a BCPL string, just the raw characters.
//
// Response:
// One or more 6-byte blocks containing the address(es) corresponding to the
@@ -315,7 +314,7 @@ namespace IFS
List bootFiles = BootServer.EnumerateBootFiles();
- foreach(BootFileEntry entry in bootFiles)
+ foreach (BootFileEntry entry in bootFiles)
{
BootDirectoryBlock block;
block.FileNumber = entry.BootNumber;
@@ -323,7 +322,7 @@ namespace IFS
block.FileName = new BCPLString(entry.Filename);
byte[] serialized = Serializer.Serialize(block);
-
+
//
// If this block fits into the current PUP, add it to the stream, otherwise send off the current PUP
// and start a new one.
@@ -375,7 +374,7 @@ namespace IFS
int passwordOffset = (userName.Length % 2) == 0 ? userName.Length : userName.Length + 1;
string password = Helpers.MesaArrayToString(p.Contents, passwordOffset + 4);
-
+
UserToken token = Authentication.Authenticate(userName, password);
if (token == null)
@@ -411,7 +410,7 @@ namespace IFS
// If mailbox name has a host/registry appended, we will strip it off.
// TODO: probably should validate host...
//
- mailboxName = Authentication.GetUserNameFromFullName(mailboxName);
+ mailboxName = Authentication.GetUserNameFromFullName(mailboxName);
IEnumerable mailList = MailManager.EnumerateMail(mailboxName);
@@ -433,31 +432,39 @@ namespace IFS
private void SendMicrocodeResponse(PUP p)
{
+ //
+ // TODO; validate that this is a request for V1 of the protocol (I don't think there was ever another version...)
+ //
+
//
// The request PUP contains the file number in the lower-order 16-bits of the pup ID.
// Assuming the number is a valid bootfile, we start sending it to the client's port via EFTP.
//
ushort fileNumber = (ushort)p.ID;
+ ushort version = (ushort)(p.ID >> 16);
- Log.Write(LogType.Verbose, LogComponent.MiscServices, "Microcode request is for file {0}.", fileNumber);
+ Log.Write(LogType.Verbose, LogComponent.MiscServices, "Microcode request (version {0}) is for file {1}.", version, Helpers.ToOctal(fileNumber));
FileStream microcodeFile = BootServer.GetStreamForNumber(fileNumber);
if (microcodeFile == null)
{
- Log.Write(LogType.Warning, LogComponent.MiscServices, "Microcode file {0} does not exist or could not be opened.", fileNumber);
+ Log.Write(LogType.Warning, LogComponent.MiscServices, "Microcode file {0} does not exist or could not be opened.", Helpers.ToOctal(fileNumber));
}
else
{
- // Send the file. The MicrocodeReply protocol is extremely simple:
+ // Send the file asynchronously. The MicrocodeReply protocol is extremely simple:
// Just send a sequence of MicrocodeReply PUPs containing the microcode data,
// there are no acks or flow control of any kind.
- Log.Write(LogType.Warning, LogComponent.MiscServices, "Sending microcode file {0}.", fileNumber);
- SendMicrocodeFile(p.SourcePort, microcodeFile);
+ ThreadPool.QueueUserWorkItem((ctx) =>
+ {
+ Log.Write(LogType.Warning, LogComponent.MiscServices, "Sending microcode file {0} ('{1}').", Helpers.ToOctal(fileNumber), microcodeFile.Name);
+ SendMicrocodeFile(p.SourcePort, microcodeFile, fileNumber == 0x100 /* test for Initial.eb */);
+ }, null);
}
}
- private void SendMicrocodeFile(PUPPort sourcePort, Stream microcodeFile)
+ private void SendMicrocodeFile(PUPPort sourcePort, Stream microcodeFile, bool sendEmptyPacket)
{
//
// "For version 1 of the protocol, a server willing to supply the data simply sends a sequence of packets
@@ -467,25 +474,48 @@ namespace IFS
// acknowledgments. This protocol is used by Dolphins and Dorados.
// Currently, the version 1 servers send packets containing 3 * n words of data. This constraint is imposed by the
// Rev L Dolphin EPROM microcode. I’d like to remove this restriction if I get a chance, so please don’t take
- // advantage of it unless you need to.The Rev L Dolphin EPROM also requires the second word of the source
+ // advantage of it unless you need to. The Rev L Dolphin EPROM also requires the second word of the source
// socket to be 4. / HGM May - 80."
//
- // TODO: this should happen in a worker thread.
- //
+ // Skip the first 256 header words in the microcode file.
+ microcodeFile.Seek(512, SeekOrigin.Begin);
//
- // We send 192 words of data per PUP (3 * 64) in an attempt to make the Dolphin happy.
+ // We send 258 words of data per PUP (3 * 86) in an attempt to make the Dolphin happy.
+ // This is what the original Xerox IFS code did.
// We space these out a bit to give the D-machine time to keep up, we're much much faster than they are.
//
- PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, 4);
+ PUPPort localPort = new PUPPort(DirectoryServices.Instance.LocalHostAddress, SocketIDGenerator.GetNextSocketID() << 16 | 0x4);
- byte[] buffer = new byte[384];
bool done = false;
uint id = 0;
+ //
+ // Send an empty packet to start the transfer. The prom boot microcode will explicitly ignore this.
+ // Note that this is not documented in the (meager) protocol docs, nor does the BCPL IFS code
+ // appear to actually send such a packet, at least not explicitly.
+ //
+ // Further:
+ // D0 Initial's E3Boot doesn't like the empty packet, and assumes it means the end of the microcode reply; it then
+ // tries to load a 0-length microcode file into CS and falls over.
+ // The below hacks around it (it only sends the empty packet when the Initial microcode file is requested).
+ // I'm unsure if there's a subtle bug in our IFS code here or elsewhere or a subtle bug in PARC's IFS code; it does kind of seem
+ // like the microcode is working around a weird issue but those folks were a lot smarter than I.
+ // Addendum 7/28/23:
+ // After reset, The real D0 seems to occasionally complete the first-stage (Initial) boot without the extra empty packet being sent.
+ // I wonder if there's a hardware glitch the boot microcode is working around.
+ // Additionally: the Dorado boot ucode source makes no mention of ignoring an empty packet, nor does the code implement such behavior.
+ //
+ if (sendEmptyPacket)
+ {
+ Router.Instance.SendPup(new PUP(PupType.MicrocodeReply, 0x10000, sourcePort, localPort, new byte[] { }));
+ }
+
+ uint checksum = 0;
while (!done)
{
+ byte[] buffer = new byte[258 * 2]; // 258 words, as the original IFS did
int read = microcodeFile.Read(buffer, 0, buffer.Length);
if (read < buffer.Length)
@@ -495,12 +525,23 @@ namespace IFS
if (read > 0)
{
- PUP microcodeReply = new PUP(PupType.MicrocodeReply, (id | 0x00010000), sourcePort, localPort, buffer);
+ // Send ONLY the bytes we read.
+ byte[] packetBuffer = new byte[read];
+ Array.Copy(buffer, packetBuffer, read);
+ PUP microcodeReply = new PUP(PupType.MicrocodeReply, (id | 0x10000), sourcePort, localPort, buffer);
Router.Instance.SendPup(microcodeReply);
+
+ Log.Write(LogType.Warning, LogComponent.MiscServices, "Sequence {0} Sent {1} bytes of microcode file", id, read);
+
+ for (int i = 0; i < read; i += 2)
+ {
+ checksum += (uint)(packetBuffer[i + 1] | (packetBuffer[i] << 8));
+ }
}
// Pause a bit to give the D0 time to breathe.
- System.Threading.Thread.Sleep(5);
+ // TODO: make this configurable?
+ System.Threading.Thread.Sleep(10);
id++;
}
@@ -508,10 +549,9 @@ namespace IFS
//
// Send an empty packet to conclude the transfer.
//
- PUP endReply = new PUP(PupType.MicrocodeReply, (id | 0x00010000), sourcePort, localPort, new byte[] { });
- Router.Instance.SendPup(endReply);
+ Router.Instance.SendPup(new PUP(PupType.MicrocodeReply, (id | 0x10000), sourcePort, localPort, new byte[] { }));
- Log.Write(LogType.Warning, LogComponent.MiscServices, "Microcode file sent.");
+ Log.Write(LogType.Warning, LogComponent.MiscServices, "Microcode file sent. Checksum {0:x4}", (checksum & 0xffff));
}
diff --git a/PUP/Properties/AssemblyInfo.cs b/PUP/Properties/AssemblyInfo.cs
index b9cca8a..2963d6f 100644
--- a/PUP/Properties/AssemblyInfo.cs
+++ b/PUP/Properties/AssemblyInfo.cs
@@ -27,7 +27,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Living Computers: Museum+Labs")]
[assembly: AssemblyProduct("IFS")]
-[assembly: AssemblyCopyright("Copyright © LCM+L 2015-2020")]
+[assembly: AssemblyCopyright("Copyright © LCM+L 2015-2020, Josh Dersch 2020-2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -49,5 +49,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.3.0.0")]
-[assembly: AssemblyFileVersion("1.3.0.0")]
+[assembly: AssemblyVersion("1.4.0.0")]
+[assembly: AssemblyFileVersion("1.4.0.0")]
diff --git a/PUP/Transport/3MbitAdapter.cs b/PUP/Transport/3MbitAdapter.cs
new file mode 100644
index 0000000..2a6304b
--- /dev/null
+++ b/PUP/Transport/3MbitAdapter.cs
@@ -0,0 +1,655 @@
+/*
+ This file is part of IFS.
+
+ IFS is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ IFS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with IFS. If not, see .
+*/
+
+
+using IFS.Gateway;
+using IFS.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace IFS.Transport
+{
+ ///
+ /// This provides a packet interface implementation that talks to Ken Shirriff's 3mbit interface on the BeagleBone.
+ /// See https://github.com/shirriff/alto-ethernet-interface for the original code. This class effectively replaces
+ /// the "gateway" C program and talks directly to the PRUs on the beaglebone to exchange packets with the hardware.
+ /// The PRU data and code files etherdata.bin and ethertext.bin are used to load the PRU with the appropriate
+ /// 3mbit driver code; these are included with this project and must be placed alongside IFS.exe in order to be
+ /// found and loaded.
+ ///
+ /// This code is more or less a direct port of Ken's code over to C#, with a bit of cleanup to make it more palatable
+ /// for C# coding styles. Though it's still pretty rough.
+ ///
+ public class Ether3MbitInterface : IPacketInterface
+ {
+ public Ether3MbitInterface()
+ {
+ try
+ {
+ InitializePRU();
+ StartReceiver();
+ }
+ catch (Exception e)
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, "Failed to initialize the BeagleBone 3Mbit Interface. Error: {0}", e.Message);
+ return;
+ }
+ }
+
+ public void RegisterRouterCallback(ReceivedPacketCallback callback)
+ {
+ _routerCallback = callback;
+ }
+
+ public void Send(PUP p)
+ {
+ byte[] frameData = PupPacketBuilder.BuildEthernetFrameFromPup(p);
+ SendToNetworkInterface(frameData);
+ }
+
+ public void Send(byte[] data, byte source, byte destination, ushort frameType)
+ {
+ byte[] frameData = PupPacketBuilder.BuildEthernetFrameFromRawData(data, source, destination, frameType);
+ SendToNetworkInterface(frameData);
+ }
+
+ public void Send(MemoryStream encapsulatedPacketStream)
+ {
+ byte[] encapsulatedFrameData = encapsulatedPacketStream.ToArray();
+ // Skip the first two bytes (encapsulated length info). This is annoying.
+ byte[] frameData = new byte[encapsulatedFrameData.Length - 2];
+ Array.Copy(encapsulatedFrameData, 2, frameData, 0, frameData.Length);
+
+ SendToNetworkInterface(frameData);
+ }
+
+ public void Shutdown()
+ {
+
+ }
+
+ private void InitializePRU()
+ {
+ Log.Write(LogType.Normal, LogComponent.E3Mbit, "PRU Initialization started.");
+
+ PRU.prussdrv_init();
+
+ if (PRU.prussdrv_open(PRU.PRU_EVTOUT_0) == -1)
+ {
+ throw new InvalidOperationException("Unable to open PRU.");
+ }
+
+ PRU.tpruss_intc_initdata initData;
+ initData.sysevts_enabled = new byte[]{ PRU.PRU0_PRU1_INTERRUPT, PRU.PRU1_PRU0_INTERRUPT, PRU.PRU0_ARM_INTERRUPT, PRU.PRU1_ARM_INTERRUPT, PRU.ARM_PRU0_INTERRUPT, PRU.ARM_PRU1_INTERRUPT, 15, 0xff };
+ initData.sysevt_to_channel_map = new PRU.tsysevt_to_channel_map[]
+ {
+ new PRU.tsysevt_to_channel_map(PRU.PRU0_PRU1_INTERRUPT, PRU.CHANNEL1),
+ new PRU.tsysevt_to_channel_map(PRU.PRU1_PRU0_INTERRUPT, PRU.CHANNEL0),
+ new PRU.tsysevt_to_channel_map(PRU.PRU0_ARM_INTERRUPT, PRU.CHANNEL2),
+ new PRU.tsysevt_to_channel_map(PRU.PRU1_ARM_INTERRUPT, PRU.CHANNEL3),
+ new PRU.tsysevt_to_channel_map(PRU.ARM_PRU0_INTERRUPT, PRU.CHANNEL0),
+ new PRU.tsysevt_to_channel_map(PRU.ARM_PRU1_INTERRUPT, PRU.CHANNEL1),
+ new PRU.tsysevt_to_channel_map(15, PRU.CHANNEL0),
+ new PRU.tsysevt_to_channel_map(-1, -1),
+ };
+
+ initData.channel_to_host_map = new PRU.tchannel_to_host_map[]
+ {
+ new PRU.tchannel_to_host_map(PRU.CHANNEL0, PRU.PRU0),
+ new PRU.tchannel_to_host_map(PRU.CHANNEL1, PRU.PRU1),
+ new PRU.tchannel_to_host_map(PRU.CHANNEL2, PRU.PRU_EVTOUT0),
+ new PRU.tchannel_to_host_map(PRU.CHANNEL3, PRU.PRU_EVTOUT1),
+ new PRU.tchannel_to_host_map(-1, -1),
+ };
+
+ initData.host_enable_bitmask = PRU.PRU0_HOSTEN_MASK | PRU.PRU1_HOSTEN_MASK | PRU.PRU_EVTOUT0_HOSTEN_MASK | PRU.PRU_EVTOUT1_HOSTEN_MASK;
+
+ PRU.prussdrv_pruintc_init(ref initData);
+
+ if (PRU.prussdrv_load_datafile(0, "etherdata.bin") < 0)
+ {
+ throw new InvalidOperationException("Unable to load PRU data file 'etherdata.bin'.");
+ }
+
+ if (PRU.prussdrv_exec_program(0, "ethertext.bin") < 0)
+ {
+ throw new InvalidOperationException("Unable to load and exec PRU program file 'ethertext.bin'.");
+ }
+
+ if (PRU.prussdrv_map_prumem(PRU.PRUSS0_PRU0_DATARAM, out _sharedPruMemory) < 0)
+ {
+ throw new InvalidOperationException("Unable to map PRU shared memory.");
+ }
+
+ Log.Write(LogType.Verbose, LogComponent.E3Mbit, "Shared PRU memory at 0x{0:x}", _sharedPruMemory.ToInt64());
+
+ // Initialize PRU control block:
+ PruInterfaceControlBlock cb;
+ cb.r_owner = OWNER_PRU;
+ cb.r_buf = R_PTR_OFFSET;
+ cb.r_max_length = MAX_SIZE;
+ cb.r_received_length = 0;
+ cb.r_status = 0;
+ cb.r_truncated = 0;
+ cb.w_owner = OWNER_ARM;
+ cb.w_buf = W_PTR_OFFSET;
+ cb.w_length = 0;
+ cb.w_status = 0;
+
+ SetInterfaceControlBlock(cb);
+
+ Log.Write(LogType.Normal, LogComponent.E3Mbit, "PRU Initialization completed.");
+ }
+
+ private void StartReceiver()
+ {
+ ThreadPool.QueueUserWorkItem((ctx) =>
+ {
+ Log.Write(LogType.Normal, LogComponent.E3Mbit, "Starting receiver thread.");
+ ReceiveWorker();
+ }, null);
+ }
+
+ ///
+ /// Worker thread function. Waits for incoming packets on the 3mbit network and handles them
+ /// when they arrive.
+ ///
+ private void ReceiveWorker()
+ {
+ while(true)
+ {
+ // Wait for the next wakeup from the PRU
+ PRU.prussdrv_pru_wait_event(PRU.PRU_EVTOUT_0);
+
+ // Clear it
+ PRU.prussdrv_pru_clear_event(PRU.PRU_EVTOUT_0, PRU.PRU0_ARM_INTERRUPT);
+
+ if (HostOwnsReadBuffer())
+ {
+ // PRU gave us a read packet from the 3mbit Ether, handle it.
+ ReceiveFromNetworkInterface();
+ }
+ }
+ }
+
+ //
+ // The following functions read and write the control block located in Host/PRU shared memory.
+ //
+
+ private void SetInterfaceControlBlock(PruInterfaceControlBlock controlBlock)
+ {
+ Marshal.StructureToPtr(controlBlock, _sharedPruMemory, false);
+ }
+
+ private PruInterfaceControlBlock GetInterfaceControlBlock()
+ {
+ return (PruInterfaceControlBlock)Marshal.PtrToStructure(_sharedPruMemory, typeof(PruInterfaceControlBlock));
+ }
+
+ private bool HostOwnsReadBuffer()
+ {
+ // r_owner is at offset + 0
+ return Marshal.ReadInt32(_sharedPruMemory) == OWNER_ARM;
+ }
+
+ private bool HostOwnsWriteBuffer()
+ {
+ // w_owner is at offset + 24
+ return Marshal.ReadInt32(new IntPtr(_sharedPruMemory.ToInt64() + 24)) == OWNER_ARM;
+ }
+
+ private void SetReadBufferOwner(UInt32 owner)
+ {
+ // r_owner is at offset + 0
+ Marshal.WriteInt32(_sharedPruMemory, (int)owner);
+ }
+
+ private void SetWriteBufferOwner(UInt32 owner)
+ {
+ // w_owner is at offset + 24
+ Marshal.WriteInt32(new IntPtr(_sharedPruMemory.ToInt64() + 24), (int)owner);
+ }
+
+ private void SetWriteBufferLength(UInt32 length)
+ {
+ // w_length is at offset + 28
+ Marshal.WriteInt32(new IntPtr(_sharedPruMemory.ToInt64() + 28), (int)length);
+ }
+
+ ///
+ /// Pulls data received from the 3mbit interface and passes it to the router.
+ ///
+ private void ReceiveFromNetworkInterface()
+ {
+ PruInterfaceControlBlock cb = GetInterfaceControlBlock();
+
+ if (cb.r_truncated != 0)
+ {
+ Log.Write(LogType.Warning, LogComponent.E3Mbit, "Truncated packet recieved.");
+ cb.r_truncated = 0;
+ SetInterfaceControlBlock(cb);
+ SetReadBufferOwner(OWNER_PRU);
+ return;
+ }
+
+ if (cb.r_status != STATUS_INPUT_COMPLETE)
+ {
+ Log.Write(LogType.Warning, LogComponent.E3Mbit, "Bad PRU status 0x{0:x}", cb.r_status);
+ SetReadBufferOwner(OWNER_PRU);
+ return;
+ }
+
+ int receivedDataLength = (int)cb.r_received_length;
+ if (receivedDataLength > MAX_SIZE)
+ {
+ Log.Write(LogType.Warning, LogComponent.E3Mbit, "Received data too long (0x{0:x} bytes)", receivedDataLength);
+ SetReadBufferOwner(OWNER_PRU);
+ return;
+ }
+
+ if (receivedDataLength == 0)
+ {
+ Log.Write(LogType.Warning, LogComponent.E3Mbit, "Received 0 bytes of duration data. Ignoring packet.");
+ SetReadBufferOwner(OWNER_PRU);
+ return;
+ }
+
+ // Grab the received data from the shared PRU memory:
+ byte[] durationBuffer = new byte[receivedDataLength];
+ Marshal.Copy(new IntPtr(_sharedPruMemory.ToInt64() + R_PTR_OFFSET), durationBuffer, 0, receivedDataLength);
+
+ // Ready for next packet
+ SetReadBufferOwner(OWNER_PRU);
+
+ byte[] decodedPacket = DecodeDurationBuffer(durationBuffer);
+ if (decodedPacket == null)
+ {
+ Log.Write(LogType.Warning, LogComponent.E3Mbit, "Received bad packet.");
+ return;
+ }
+
+ // Prepend packet length for our internal encapsulation (annoying since we're just going to strip it off again...)
+ byte[] encapsulatedPacket = new byte[decodedPacket.Length + 2];
+ Array.Copy(decodedPacket, 0, encapsulatedPacket, 2, decodedPacket.Length);
+
+ int encapsulatedLength = decodedPacket.Length / 2 + 2;
+ encapsulatedPacket[0] = (byte)(encapsulatedLength >> 8);
+ encapsulatedPacket[1] = (byte)encapsulatedLength;
+
+ MemoryStream packetStream = new MemoryStream(encapsulatedPacket);
+ _routerCallback(packetStream, this);
+
+ Log.Write(LogType.Verbose, LogComponent.E3Mbit, "Received packet (0x{0:x} bytes), sent to router.", receivedDataLength);
+ }
+
+ ///
+ /// Sends data to the 3mbit interface.
+ ///
+ private void SendToNetworkInterface(byte[] data)
+ {
+ if (!HostOwnsWriteBuffer())
+ {
+ // Shouldn't happen
+ Log.Write(LogType.Error, LogComponent.E3Mbit, "SendToNetworkInterface called when PRU is not ready.");
+ return;
+ }
+
+ ushort crcVal = CalculateCRC(data, data.Length);
+
+ // Construct a new buffer with space for the CRC
+ byte[] fullPacket = new byte[data.Length + 2];
+ Array.Copy(data, fullPacket, data.Length);
+
+ fullPacket[fullPacket.Length - 2] = (byte)(crcVal >> 8);
+ fullPacket[fullPacket.Length - 1] = (byte)(crcVal);
+
+ // Copy the buffer to the shared PRU memory.
+ Marshal.Copy(fullPacket, 0, new IntPtr(_sharedPruMemory.ToInt64() + W_PTR_OFFSET), fullPacket.Length);
+ SetWriteBufferLength((uint)fullPacket.Length);
+
+ // Signal PRU to send the data in the write buffer.
+ SetWriteBufferOwner(OWNER_PRU);
+
+ Log.Write(LogType.Verbose, LogComponent.E3Mbit, "Packet sent to 3mbit interface.");
+ }
+
+ ///
+ /// Decodes bit timings into packet data. Returns null if issues were found with the data.
+ ///
+ ///
+ ///
+ byte[] DecodeDurationBuffer(byte[] durationBuf) {
+
+ Log.Write(LogType.Verbose, LogComponent.E3Mbit, $"Decoding duration buffer of length {durationBuf.Length}.");
+
+ List byteBuffer = new List();
+ bool[] bitBuf = new bool[8 * PUP.MAX_PUP_SIZE];
+ const int RECV_WIDTH = 2; // Recv values are in units of 2 ns (to fit in byte)
+
+ // Convert timings in durationBuf into high/low vector in bitBuf
+ // bitBuf holds values like 1, 0, 0, 1, 0, 1, indicating if the input
+ // was high or low during that time interval.
+ // A Manchester-encoded data bit consists of two values in bitBuf.
+ int offset1; // Offset into timing vector
+ int offset2 = 0; // Offset into bit vector
+ bool value = true; // Current high/low value
+ for (offset1 = 0; offset1 < durationBuf.Length; offset1++)
+ {
+ int width = durationBuf[offset1] * RECV_WIDTH;
+ if (width < 120)
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, $"Bad width {width} at {offset1} of {durationBuf.Length}");
+ return null;
+ }
+ else if (width < 230)
+ {
+ value = !value;
+ bitBuf[offset2++] = value;
+ }
+ else if (width < 280)
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, $"Bad width {width} at {offset1} of {durationBuf.Length}");
+ return null;
+ }
+ else if (width < 400)
+ {
+ value = !value;
+ bitBuf[offset2++] = value;
+ bitBuf[offset2++] = value;
+ }
+ else
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, $"Bad width {width} at {offset1} of {durationBuf.Length}");
+ return null;
+ }
+ }
+
+ // Convert bit pairs in bitBuf to bytes in byteBuffer
+ byte b = 0;
+ int i;
+ if ((offset2 % 2) == 0)
+ {
+ // For a 0 bit, the last 1 signal gets combined with the no-signal state and lost.
+ // So add it back.
+ bitBuf[offset2] = true;
+ offset2 += 1;
+ }
+
+ Log.Write(LogType.Verbose, LogComponent.E3Mbit, $"Offset2 is {offset2}.");
+ // Start at 1 to skip sync
+ for (i = 1; i < offset2; i += 2)
+ {
+ if (bitBuf[i] == bitBuf[i + 1])
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, $"Bad bit sequence at {i} of {offset2}: {bitBuf[i]}, {bitBuf[i+1]}");
+ b = (byte)(b << 1);
+ }
+ else
+ {
+ b = (byte)((b << 1) | (bitBuf[i] ? 1 : 0));
+ }
+ if ((i % 16) == 15)
+ {
+ byteBuffer.Add(b);
+ b = 0;
+ }
+ }
+ if ((offset2 % 16) != 1)
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, $"Bad offset2: {offset2}");
+ return null;
+ }
+
+ // Check the Ethernet CRC
+ byte[] byteArray = byteBuffer.ToArray();
+ ushort crcVal = CalculateCRC(byteArray, byteArray.Length - 2);
+ ushort readCrcVal = (ushort)((byteBuffer[byteBuffer.Count - 2] << 8) | byteBuffer[byteBuffer.Count - 1]);
+ if (crcVal != readCrcVal)
+ {
+ Log.Write(LogType.Error, LogComponent.E3Mbit, "Bad CRC, {0:x} vs {1:x}", crcVal, readCrcVal);
+ return null;
+ }
+
+ return byteArray;
+ }
+
+ // Generate CRC-16 for 3Mbit Ethernet
+ // buf is sequence of words stored big-endian.
+ ushort CalculateCRC(byte[] buf, int lengthInBytes)
+ {
+ ushort crc = 0x8005; // Due to the sync bit
+ for (int index = 0; index < lengthInBytes; index++)
+ {
+ ushort data = (ushort)(buf[index] << 8);
+ for (int i = 0; i < 8; i++)
+ {
+ ushort xorFeedback = (ushort)((crc ^ data) & 0x8000); // Test upper bit
+ crc = (ushort)(crc << 1);
+ data = (ushort)(data << 1);
+ if (xorFeedback != 0)
+ {
+ crc ^= 0x8005; // CRC-16 polynomial constant
+ }
+ }
+ }
+
+ return crc;
+ }
+
+ IntPtr _sharedPruMemory;
+
+ private ReceivedPacketCallback _routerCallback;
+
+ // Interface between host and PRU
+ // The idea is there are two buffers: r_ and w_.
+ // Ownership is passed back and forth between the PRU and the ARM processor.
+ // The PRU sends a signal whenever it gives a buffer back to the ARM.
+ // "in" and "out" below are from the perspective of the PRU.
+ //
+ // This struct is here more for convenience of debugging than actual use in C#
+ // since it's not really possible to map a C# object directly to volatile memory
+ // in a way that I feel good about using.
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ struct PruInterfaceControlBlock
+ {
+ public UInt32 r_owner; // in
+ public UInt32 r_max_length; // in, bytes
+ public UInt32 r_received_length; // out, bytes
+ public UInt32 r_buf; // in (pointer offset)
+ public UInt32 r_truncated; // out, boolean
+ public UInt32 r_status; // out
+ public UInt32 w_owner; // in
+ public UInt32 w_length; // bytes, in (buffer length)
+ public UInt32 w_buf; // in (pointer offset)
+ public UInt32 w_status; // out
+ };
+
+ const uint STATUS_INPUT_COMPLETE = (0 << 8);
+ const uint STATUS_OUTPUT_COMPLETE = (1 << 8);
+ const uint STATUS_INPUT_OVERRUN = (2 << 8);
+ const uint STATUS_SOFTWARE_RESET = (5 << 8); // Internal only
+
+ const uint STATUS_TRUNCATED = 36; // Not part of real interface
+ const uint STATUS_TIMING_ERROR = 32; // Not part of real interface
+ const uint STATUS_BIT_COLLISION = 16;
+ const uint STATUS_BIT_CRC_BAD = 8; // unused
+ const uint STATUS_BIT_ICMD = 4; // unused
+ const uint STATUS_BIT_OCMD = 2; // unused
+ const uint STATUS_BIT_INCOMPLETE = 1; // Not byte boundary
+
+ const uint COMMAND_NONE = 0;
+ const uint COMMAND_SEND = 1;
+ const uint COMMAND_RECV = 2;
+ const uint COMMAND_HALT = 3;
+
+ const uint OWNER_ARM = 1;
+ const uint OWNER_PRU = 2;
+
+ const uint W_PTR_OFFSET = 0x400;
+ const uint R_PTR_OFFSET = 0x10000;
+ const uint MAX_SIZE = 12 * 1024;
+ }
+
+ ///
+ /// Provides constants, structs, and functions needed to P/Invoke into prussdrv lib calls.
+ ///
+ public static class PRU
+ {
+ public const int NUM_PRU_HOSTIRQS = 8;
+ public const int NUM_PRU_HOSTS = 10;
+ public const int NUM_PRU_CHANNELS = 10;
+ public const int NUM_PRU_SYS_EVTS = 64;
+
+ public const uint PRUSS0_PRU0_DATARAM = 0;
+ public const uint PRUSS0_PRU1_DATARAM = 1;
+ public const uint PRUSS0_PRU0_IRAM = 2;
+ public const uint PRUSS0_PRU1_IRAM = 3;
+
+ public const uint PRUSS_V1 = 1; // AM18XX
+ public const uint PRUSS_V2 = 2; // AM33XX
+
+ //Available in AM33xx series - begin
+ public const uint PRUSS0_SHARED_DATARAM = 4;
+ public const uint PRUSS0_CFG = 5;
+ public const uint PRUSS0_UART = 6;
+ public const uint PRUSS0_IEP = 7;
+ public const uint PRUSS0_ECAP = 8;
+ public const uint PRUSS0_MII_RT = 9;
+ public const uint PRUSS0_MDIO = 10;
+ //Available in AM33xx series - end
+
+ public const uint PRU_EVTOUT_0 = 0;
+ public const uint PRU_EVTOUT_1 = 1;
+ public const uint PRU_EVTOUT_2 = 2;
+ public const uint PRU_EVTOUT_3 = 3;
+ public const uint PRU_EVTOUT_4 = 4;
+ public const uint PRU_EVTOUT_5 = 5;
+ public const uint PRU_EVTOUT_6 = 6;
+ public const uint PRU_EVTOUT_7 = 7;
+
+ public const byte PRU0_PRU1_INTERRUPT = 17;
+ public const byte PRU1_PRU0_INTERRUPT = 18;
+ public const byte PRU0_ARM_INTERRUPT = 19;
+ public const byte PRU1_ARM_INTERRUPT = 20;
+ public const byte ARM_PRU0_INTERRUPT = 21;
+ public const byte ARM_PRU1_INTERRUPT = 22;
+
+ public const byte CHANNEL0 = 0;
+ public const byte CHANNEL1 = 1;
+ public const byte CHANNEL2 = 2;
+ public const byte CHANNEL3 = 3;
+ public const byte CHANNEL4 = 4;
+ public const byte CHANNEL5 = 5;
+ public const byte CHANNEL6 = 6;
+ public const byte CHANNEL7 = 7;
+ public const byte CHANNEL8 = 8;
+ public const byte CHANNEL9 = 9;
+
+ public const byte PRU0 = 0;
+ public const byte PRU1 = 1;
+ public const byte PRU_EVTOUT0 = 2;
+ public const byte PRU_EVTOUT1 = 3;
+ public const byte PRU_EVTOUT2 = 4;
+ public const byte PRU_EVTOUT3 = 5;
+ public const byte PRU_EVTOUT4 = 6;
+ public const byte PRU_EVTOUT5 = 7;
+ public const byte PRU_EVTOUT6 = 8;
+ public const byte PRU_EVTOUT7 = 9;
+
+ public const uint PRU0_HOSTEN_MASK = 0x0001;
+ public const uint PRU1_HOSTEN_MASK = 0x0002;
+ public const uint PRU_EVTOUT0_HOSTEN_MASK = 0x0004;
+ public const uint PRU_EVTOUT1_HOSTEN_MASK = 0x0008;
+ public const uint PRU_EVTOUT2_HOSTEN_MASK = 0x0010;
+ public const uint PRU_EVTOUT3_HOSTEN_MASK = 0x0020;
+ public const uint PRU_EVTOUT4_HOSTEN_MASK = 0x0040;
+ public const uint PRU_EVTOUT5_HOSTEN_MASK = 0x0080;
+ public const uint PRU_EVTOUT6_HOSTEN_MASK = 0x0100;
+ public const uint PRU_EVTOUT7_HOSTEN_MASK = 0x0200;
+
+ public struct tsysevt_to_channel_map
+ {
+ public tsysevt_to_channel_map(short s, short c)
+ {
+ sysevt = s;
+ channel = c;
+ }
+ public short sysevt;
+ public short channel;
+ }
+
+ public struct tchannel_to_host_map
+ {
+ public tchannel_to_host_map(short c, short h)
+ {
+ channel = c;
+ host = h;
+ }
+ public short channel;
+ public short host;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ public struct tpruss_intc_initdata
+ {
+ //Enabled SYSEVTs - Range:0..63
+ //{-1} indicates end of list
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_PRU_SYS_EVTS)]
+ public byte[] sysevts_enabled;
+
+ //SysEvt to Channel map. SYSEVTs - Range:0..63 Channels -Range: 0..9
+ //{-1, -1} indicates end of list
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_PRU_SYS_EVTS)]
+ public tsysevt_to_channel_map[] sysevt_to_channel_map;
+
+ //Channel to Host map.Channels -Range: 0..9 HOSTs - Range:0..9
+ //{-1, -1} indicates end of list
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_PRU_CHANNELS)]
+ public tchannel_to_host_map[] channel_to_host_map;
+
+ //10-bit mask - Enable Host0-Host9 {Host0/1:PRU0/1, Host2..9 : PRUEVT_OUT0..7}
+ public UInt32 host_enable_bitmask;
+ }
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_init();
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_open(UInt32 host_interrupt);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_pruintc_init(ref tpruss_intc_initdata prussintc_init_data);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_load_datafile(int prunum, [MarshalAs(UnmanagedType.LPStr)] string filename);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_exec_program(int prunum, [MarshalAs(UnmanagedType.LPStr)] string filename);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_map_prumem(UInt32 pru_ram_id, out IntPtr address);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_pru_wait_event(UInt32 host_interrupt);
+
+ [DllImport("prussdrv")]
+ public static extern int prussdrv_pru_clear_event(UInt32 host_interrupt, UInt32 sysevent);
+ }
+}
diff --git a/PUP/Transport/Ethernet.cs b/PUP/Transport/Ethernet.cs
index 074a4b8..63a2bc9 100644
--- a/PUP/Transport/Ethernet.cs
+++ b/PUP/Transport/Ethernet.cs
@@ -16,11 +16,6 @@
*/
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
using PcapDotNet.Base;
using PcapDotNet.Core;
using PcapDotNet.Core.Extensions;
@@ -28,7 +23,6 @@ using PcapDotNet.Packets;
using PcapDotNet.Packets.Ethernet;
using IFS.Logging;
using System.IO;
-using System.Net.NetworkInformation;
using System.Threading;
using IFS.Gateway;
@@ -41,14 +35,14 @@ namespace IFS.Transport
/// Ethernet packets are broadcast. See comments in UDP.cs for the reasoning behind this.
///
///
- public class Ethernet : IPupPacketInterface, IRawPacketInterface
+ public class Ethernet : IPacketInterface
{
public Ethernet(LivePacketDevice iface)
{
- _interface = iface;
+ _interface = iface;
}
- public void RegisterRouterCallback(RoutePupCallback callback)
+ public void RegisterRouterCallback(ReceivedPacketCallback callback)
{
_routerCallback = callback;
@@ -63,7 +57,7 @@ namespace IFS.Transport
public void Shutdown()
{
_routerCallback = null;
- _communicator.Break();
+ _communicator.Break();
}
public void Send(PUP p)
@@ -71,75 +65,26 @@ namespace IFS.Transport
//
// Write PUP to ethernet:
//
-
- // Build the outgoing data; this is:
- // 1st word: length of data following
- // 2nd word: 3mbit destination / source bytes
- // 3rd word: frame type (PUP)
- byte[] encapsulatedFrame = new byte[6 + p.RawData.Length];
-
- // 3mbit Packet length
- encapsulatedFrame[0] = (byte)((p.RawData.Length / 2 + 2) >> 8);
- encapsulatedFrame[1] = (byte)(p.RawData.Length / 2 + 2);
-
- // addressing
- encapsulatedFrame[2] = p.DestinationPort.Host;
- encapsulatedFrame[3] = p.SourcePort.Host;
-
- // frame type
- encapsulatedFrame[4] = (byte)(_pupFrameType >> 8);
- encapsulatedFrame[5] = (byte)_pupFrameType;
-
- // Actual data
- p.RawData.CopyTo(encapsulatedFrame, 6);
-
- MacAddress destinationMac = new MacAddress(_10mbitBroadcast);
-
- // Build the outgoing packet; place the source/dest addresses, type field and the PUP data.
- EthernetLayer ethernetLayer = new EthernetLayer
- {
- Source = _interface.GetMacAddress(),
- Destination = destinationMac,
- EtherType = (EthernetType)_3mbitFrameType,
- };
-
- PayloadLayer payloadLayer = new PayloadLayer
- {
- Data = new Datagram(encapsulatedFrame),
- };
-
- PacketBuilder builder = new PacketBuilder(ethernetLayer, payloadLayer);
-
- // Send it over the 'net!
- _communicator.SendPacket(builder.Build(DateTime.Now));
+ byte[] encapsulatedFrame = PupPacketBuilder.BuildEncapsulatedEthernetFrameFromPup(p);
+ SendFrame(encapsulatedFrame);
}
public void Send(byte[] data, byte source, byte destination, ushort frameType)
{
- // Build the outgoing data; this is:
- // 1st word: length of data following
- // 2nd word: 3mbit destination / source bytes
- // 3rd word: frame type (PUP)
- byte[] encapsulatedFrame = new byte[6 + data.Length];
+ byte[] encapsulatedFrame = PupPacketBuilder.BuildEncapsulatedEthernetFrameFromRawData(data, source, destination, frameType);
+ SendFrame(encapsulatedFrame);
+ }
- // 3mbit Packet length
- encapsulatedFrame[0] = (byte)((data.Length / 2 + 2) >> 8);
- encapsulatedFrame[1] = (byte)(data.Length / 2 + 2);
+ public void Send(MemoryStream encapsulatedFrameStream)
+ {
+ SendFrame(encapsulatedFrameStream.ToArray());
+ }
- // addressing
- encapsulatedFrame[2] = destination;
- encapsulatedFrame[3] = source;
+ private void SendFrame(byte[] encapsulatedFrame)
+ {
+ MacAddress destinationMac = new MacAddress(_10mbitBroadcast);
- // frame type
- encapsulatedFrame[4] = (byte)(frameType >> 8);
- encapsulatedFrame[5] = (byte)frameType;
-
- // Actual data
- data.CopyTo(encapsulatedFrame, 6);
-
- MacAddress destinationMac = new MacAddress(_10mbitBroadcast);
-
- // Build the outgoing packet; place the source/dest addresses, type field and the PUP data.
+ // Build the outgoing packet; place the source/dest addresses, type field and the PUP data.
EthernetLayer ethernetLayer = new EthernetLayer
{
Source = _interface.GetMacAddress(),
@@ -160,56 +105,19 @@ namespace IFS.Transport
private void ReceiveCallback(Packet p)
{
- //
- // Filter out encapsulated 3mbit frames and look for PUPs, forward them on.
- //
if ((int)p.Ethernet.EtherType == _3mbitFrameType)
{
Log.Write(LogType.Verbose, LogComponent.Ethernet, "3mbit pup received.");
MemoryStream packetStream = p.Ethernet.Payload.ToMemoryStream();
-
- // Read the length prefix (in words), convert to bytes.
- // Subtract off 2 words for the ethernet header
- int length = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte())) * 2 - 4;
-
- // Read the address (1st word of 3mbit packet)
- byte destination = (byte)packetStream.ReadByte();
- byte source = (byte)packetStream.ReadByte();
-
- // Read the type and switch on it
- int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
-
- //
- // Ensure this is a packet we're interested in.
- //
- if (etherType3mbit == _pupFrameType && // it's a PUP
- (destination == DirectoryServices.Instance.LocalHost || // for us, or...
- destination == 0)) // broadcast
- {
- try
- {
- PUP pup = new PUP(packetStream, length);
- _routerCallback(pup, destination != 0);
- }
- catch(Exception e)
- {
- // An error occurred, log it.
- Log.Write(LogType.Error, LogComponent.PUP, "Error handling PUP: {0}", e.Message);
- }
-
- }
- else
- {
- Log.Write(LogType.Warning, LogComponent.Ethernet, "3mbit packet is not a PUP, dropping");
- }
+ _routerCallback(packetStream, this);
}
else
{
- // Not a PUP, Discard the packet. We will not log this, so as to keep noise down.
+ // Not an encapsulated 3mbit frame, Discard the packet. We will not log this, so as to keep noise down.
// Log.Write(LogType.Verbose, LogComponent.Ethernet, "Not a PUP (type 0x{0:x}. Dropping.", p.Ethernet.EtherType);
}
- }
+ }
private void Open(bool promiscuous, int timeout)
{
@@ -231,13 +139,10 @@ namespace IFS.Transport
private LivePacketDevice _interface;
private PacketCommunicator _communicator;
- private RoutePupCallback _routerCallback;
+ private ReceivedPacketCallback _routerCallback;
// Constants
- // The ethertype used in the encapsulated 3mbit frame
- private readonly ushort _pupFrameType = 512;
-
// The type used for 3mbit frames encapsulated in 10mb frames
private readonly int _3mbitFrameType = 0xbeef; // easy to identify, ostensibly unused by anything of any import
diff --git a/PUP/Transport/PacketBuilder.cs b/PUP/Transport/PacketBuilder.cs
new file mode 100644
index 0000000..1944adb
--- /dev/null
+++ b/PUP/Transport/PacketBuilder.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IFS.Transport
+{
+ ///
+ /// Helper functions for building Ethernet frames in a variety of ways.
+ ///
+ public static class PupPacketBuilder
+ {
+ // The ethertype used in the encapsulated 3mbit frame
+ public static readonly ushort PupFrameType = 512;
+
+ public static byte[] BuildEncapsulatedEthernetFrameFromPup(PUP p)
+ {
+ return BuildEncapsulatedEthernetFrameFromRawData(p.RawData, p.SourcePort.Host, p.DestinationPort.Host, PupFrameType);
+ }
+
+ public static byte[] BuildEncapsulatedEthernetFrameFromRawData(byte[] data, byte source, byte destination, ushort frameType)
+ {
+ // Build the outgoing data; this is:
+ // 1st word: length of data following
+ // 2nd word: 3mbit destination / source bytes
+ // 3rd word: frame type
+ byte[] newFrame = new byte[6 + data.Length];
+
+ // 3mbit Packet length
+ newFrame[0] = (byte)((data.Length / 2 + 2) >> 8);
+ newFrame[1] = (byte)(data.Length / 2 + 2);
+
+ // addressing
+ newFrame[2] = destination;
+ newFrame[3] = source;
+
+ // frame type
+ newFrame[4] = (byte)(frameType >> 8);
+ newFrame[5] = (byte)frameType;
+
+ // Actual data
+ data.CopyTo(newFrame, 6);
+
+ return newFrame;
+ }
+
+ public static byte[] BuildEthernetFrameFromPup(PUP p)
+ {
+ return BuildEthernetFrameFromRawData(p.RawData, p.SourcePort.Host, p.DestinationPort.Host, PupFrameType);
+ }
+
+ public static byte[] BuildEthernetFrameFromRawData(byte[] data, byte source, byte destination, ushort frameType)
+ {
+ // Build the full raw frame data; this is:
+ // 2nd word: 3mbit destination / source bytes
+ // 3rd word: frame type
+ byte[] newFrame = new byte[4 + data.Length];
+
+ // addressing
+ newFrame[0] = destination;
+ newFrame[1] = source;
+
+ // frame type
+ newFrame[2] = (byte)(frameType >> 8);
+ newFrame[3] = (byte)frameType;
+
+ // Actual data
+ data.CopyTo(newFrame, 4);
+
+ return newFrame;
+ }
+ }
+}
diff --git a/PUP/Transport/PacketInterface.cs b/PUP/Transport/PacketInterface.cs
index 382ea1e..7489ba9 100644
--- a/PUP/Transport/PacketInterface.cs
+++ b/PUP/Transport/PacketInterface.cs
@@ -16,12 +16,7 @@
*/
using IFS.Gateway;
-using PcapDotNet.Packets;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.IO;
namespace IFS.Transport
{
@@ -41,7 +36,7 @@ namespace IFS.Transport
/// Registers a callback (into the router) to be invoked on receipt of a PUP.
///
///
- void RegisterRouterCallback(RoutePupCallback callback);
+ void RegisterRouterCallback(ReceivedPacketCallback callback);
///
/// Shuts down the interface.
@@ -69,5 +64,18 @@ namespace IFS.Transport
///
///
void Send(byte[] data, byte source, byte destination, ushort frameType);
+
+ ///
+ /// Sends the specified data over the transport.
+ ///
+ ///
+ void Send(MemoryStream encapsulatedFrameStream);
}
+
+ public interface IPacketInterface : IPupPacketInterface, IRawPacketInterface
+ {
+
+ }
+
+
}
diff --git a/PUP/Transport/UDP.cs b/PUP/Transport/UDP.cs
index 31a1a8e..25866db 100644
--- a/PUP/Transport/UDP.cs
+++ b/PUP/Transport/UDP.cs
@@ -62,7 +62,7 @@ namespace IFS.Transport
/// and go with the broadcast implementation.
///
///
- public class UDPEncapsulation : IPupPacketInterface, IRawPacketInterface
+ public class UDPEncapsulation : IPacketInterface
{
public UDPEncapsulation(NetworkInterface iface)
{
@@ -78,7 +78,7 @@ namespace IFS.Transport
// Grab the broadcast address for the interface so that we know what broadcast address to use
// for our UDP datagrams.
//
- IPInterfaceProperties props = iface.GetIPProperties();
+ IPInterfaceProperties props = iface.GetIPProperties();
foreach (UnicastIPAddressInformation unicast in props.UnicastAddresses)
{
@@ -117,11 +117,11 @@ namespace IFS.Transport
/// Registers a gateway to handle incoming PUPs.
///
///
- public void RegisterRouterCallback(RoutePupCallback callback)
+ public void RegisterRouterCallback(ReceivedPacketCallback callback)
{
_routerCallback = callback;
- // Now that we have a callback we can start receiving stuff.
+ // Now that we have a callback we can start receiving stuff.
BeginReceive();
}
@@ -136,37 +136,16 @@ namespace IFS.Transport
//
// Write PUP to UDP:
//
- // For now, no actual routing (Gateway not implemented yet), everything is on the same 'net.
// Just send a broadcast UDP with the encapsulated frame inside of it.
- //
+ //
+ byte[] encapsulatedFrame = PupPacketBuilder.BuildEncapsulatedEthernetFrameFromPup(p);
- // Build the outgoing data; this is:
- // 1st word: length of data following
- // 2nd word: 3mbit destination / source bytes
- // 3rd word: frame type (PUP)
- byte[] encapsulatedFrame = new byte[6 + p.RawData.Length];
-
- // 3mbit Packet length
- encapsulatedFrame[0] = (byte)((p.RawData.Length / 2 + 2) >> 8);
- encapsulatedFrame[1] = (byte)(p.RawData.Length / 2 + 2);
-
- // addressing
- encapsulatedFrame[2] = p.DestinationPort.Host;
- encapsulatedFrame[3] = p.SourcePort.Host;
-
- // frame type
- encapsulatedFrame[4] = (byte)(_pupFrameType >> 8);
- encapsulatedFrame[5] = (byte)_pupFrameType;
-
- // Actual data
- p.RawData.CopyTo(encapsulatedFrame, 6);
-
- // Send as UDP broadcast.
- _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
+ // Send as UDP broadcast.
+ _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
}
///
- /// Sends an array of bytes over the ethernet as a 3mbit packet encapsulated in a 10mbit packet.
+ /// Sends an array of bytes over the network as a 3mbit packet encapsulated in a UDP datagram.
///
///
///
@@ -176,66 +155,26 @@ namespace IFS.Transport
// 1st word: length of data following
// 2nd word: 3mbit destination / source bytes
// 3rd word: frame type (PUP)
- byte[] encapsulatedFrame = new byte[6 + data.Length];
+ byte[] encapsulatedFrame = PupPacketBuilder.BuildEncapsulatedEthernetFrameFromRawData(data, source, destination, frameType);
- // 3mbit Packet length
- encapsulatedFrame[0] = (byte)((data.Length / 2 + 2) >> 8);
- encapsulatedFrame[1] = (byte)(data.Length / 2 + 2);
+ // Send as UDP broadcast.
+ _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
+ }
- // addressing
- encapsulatedFrame[2] = destination;
- encapsulatedFrame[3] = source;
-
- // frame type
- encapsulatedFrame[4] = (byte)(frameType >> 8);
- encapsulatedFrame[5] = (byte)frameType;
-
- // Actual data
- data.CopyTo(encapsulatedFrame, 6);
-
- // Send as UDP broadcast.
- _udpClient.Send(encapsulatedFrame, encapsulatedFrame.Length, _broadcastEndpoint);
+ ///
+ /// Sends a stream of bytes over the network as a 3mbit packet encapsulated in a UDP datagram.
+ ///
+ ///
+ public void Send(MemoryStream encapsulatedPacketStream)
+ {
+ // Send as UDP broadcast.
+ byte[] buf = encapsulatedPacketStream.ToArray();
+ _udpClient.Send(buf, buf.Length, _broadcastEndpoint);
}
private void Receive(MemoryStream packetStream)
{
- //
- // Look for PUPs, forward them on.
- //
-
- // Read the length prefix (in words), convert to bytes.
- // Subtract off 2 words for the ethernet header
- int length = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte())) * 2 - 4;
-
- // Read the address (1st word of 3mbit packet)
- byte destination = (byte)packetStream.ReadByte();
- byte source = (byte)packetStream.ReadByte();
-
- // Read the type and switch on it
- int etherType3mbit = ((packetStream.ReadByte() << 8) | (packetStream.ReadByte()));
-
- //
- // Ensure this is a packet we're interested in.
- //
- if (etherType3mbit == _pupFrameType && // it's a PUP
- (destination == DirectoryServices.Instance.LocalHost || // for us, or...
- destination == 0)) // broadcast
- {
- try
- {
- PUP pup = new PUP(packetStream, length);
- _routerCallback(pup, destination != 0);
- }
- catch(Exception e)
- {
- // An error occurred, log it.
- Log.Write(LogType.Error, LogComponent.PUP, "Error handling PUP: {0}", e.Message);
- }
- }
- else
- {
- Log.Write(LogType.Warning, LogComponent.Ethernet, "UDP packet is not a PUP, dropping");
- }
+ _routerCallback(packetStream, this);
}
///
@@ -258,7 +197,7 @@ namespace IFS.Transport
// properly.)
Log.Write(LogComponent.UDP, "UDP Receiver thread started.");
- IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, Configuration.UDPPort);
+ IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Any, Configuration.UDPPort);
while (true)
{
@@ -285,12 +224,9 @@ namespace IFS.Transport
}
return new IPAddress(broadcastAddress);
- }
-
- // The ethertype used in the encapsulated 3mbit frame
- private readonly ushort _pupFrameType = 512;
+ }
- private RoutePupCallback _routerCallback;
+ private ReceivedPacketCallback _routerCallback;
// Thread used for receive
private Thread _receiveThread;
diff --git a/PUP/etherdata.bin b/PUP/etherdata.bin
new file mode 100644
index 0000000..6d17cf9
Binary files /dev/null and b/PUP/etherdata.bin differ
diff --git a/PUP/ethertext.bin b/PUP/ethertext.bin
new file mode 100644
index 0000000..f2fc4ee
Binary files /dev/null and b/PUP/ethertext.bin differ
diff --git a/PUP/readme.txt b/PUP/readme.txt
index 957f477..f3c4002 100644
--- a/PUP/readme.txt
+++ b/PUP/readme.txt
@@ -1,4 +1,4 @@
-Readme.txt for IFS v1.2:
+Readme.txt for IFS v1.4:
1. Introduction and Overview
============================
@@ -11,8 +11,10 @@ into being during the Alto's lifetime, so the IFS was a permanent fixture of
the network environment at PARC during the heyday of the Alto.
The LCM+L's IFS implementation is an implementation of this protocol suite
-that runs on a modern PC, and is designed to work with the ContrAlto Alto
-emulator over either Raw Ethernet packets or UDP broadcasts.
+that runs on a modern PC. It is designed to work with the ContrAlto Alto
+emulator over either Raw Ethernet packets or UDP broadcasts, as well as with
+real hardware when using the BeagleBone-based Alto Ethernet Interface available at
+https://github.com/shirriff/alto-ethernet-interface.
It provides the following IFS services:
@@ -84,8 +86,12 @@ Directory configuration:
(User mail folders are placed in this directory.)
Interface configuration:
- - InterfaceType: "RAW" or "UDP". Specifies the transport to use for
- communication.
+ - InterfaceTypes: Any combination of "RAW" "UDP" or "3MBIT". Specifies
+ the transports to use for communication:
+ - RAW: Raw Ethernet frames
+ - UDP: UDP Datagram
+ - 3MBIT: The Beaglebone-based Alto Ethernet Interface
+
- InterfaceName: The name of the host network adapter to use for
communication.
@@ -104,6 +110,12 @@ Debugging configuration:
DirectoryServices, PUP, FTP, BreathOfLife, EFTP,
BootServer, UDP, Mail, Configuration, or All
+Misc:
+ - RunIFSServices: Whether to run the full suite of IFS services or just
+ perform basic bridging (this is mostly useful when
+ working with real hardware via the Alto Interface Hardware
+ at https://github.com/shirriff/alto-ethernet-interface.)
+
2.2 hosts.txt:
--------------
@@ -398,9 +410,9 @@ The following documents may be useful in actually using the Alto-land client
tools (FTP, CopyDisk, mail, etc) to communicate with the IFS server:
The Alto User's Handbook:
- http://bitsavers.org/pdf/xerox/alto/Alto_Users_Handbook_Sep79.pdf
+ http://bitsavers.org/pdf/xerox/alto/Alto_Users_Handbook_Sep79.pdf
Alto Subsystems:
- http://bitsavers.org/pdf/xerox/alto/memos_1981/Alto_Subsystems_May81.pdf
+ http://bitsavers.org/pdf/xerox/alto/memos_1981/Alto_Subsystems_May81.pdf
The following specifications were used to implement the IFS protocol suite:
@@ -414,16 +426,48 @@ Misc Services:
http://xeroxalto.computerhistory.org/_cd8_/pup/.miscservices.press!1.pdf
-8.0 Packet-Level Protocol
+8.0 Using With Real Hardware
+============================
+
+IFS can be used to talk with real hardware that has a 3mbit Ethernet interface
+(Altos, Dolphins, Dorados, PERQs, and early Sun hardware).
+This is accomplished by using the BeagleBone Alto Ethernet Interface developed by
+Ken Sherriff and documented here: https://github.com/shirriff/alto-ethernet-interface.
+
+Ken provides his own modification of this IFS code to use with his hardware,
+but as of V1.4, this IFS implementation provides all the functionality of his
+software, making his fork unnecessary. All that is necessary is to place the
+release binaries on the BeagleBone and ensure that IFS.exe is running (you must
+disable any earlier versions of IFS.exe and the 'gateway' program before doing so.)
+
+Use of the BeagleBone Alto Ethernet Interface is enabled by specifying "3MBIT" as one
+of the InterfaceTypes options in Ifs.cfg (see section 2.1).
+
+When running in this configuration, by default IFS will run just as it does on desktop PCs and
+will provide network services directly to the system it is connected to.
+
+8.1 Bridging Mode
+-----------------
+Additionally, by setting the "RunIFSServices" parameter to false, IFS will run in
+a mode where it does not provide any IFS services, but instead bridges interfaces together:
+Packets received from a real Alto will be sent out over UDP and RAW interfaces (if configured)
+and similarly, packets coming in over UDP and RAW interfaces will be sent to the real Alto.
+This allows the IFS software to let real hardware directly communicate with other 3mbit devices on
+your local network (real or emulated) with a minimum of effort. The expectation in this
+mode is that some other system on the network (a PC or a server somewhere) will be providing
+IFS services, not the IFS running on the BeagleBone.
+
+
+9.0 Packet-Level Protocol
=========================
IFS (and ContrAlto) use a very simple encapsulation for transmitting 3mbit
Ethernet packets over modern transports. An encapsulated packet consists of
two fields:
- - Packet Length (2 bytes): Length (in 16-bit words) of the 3mbit Packet
- Data field (see below)
- - Packet Data (N bytes): The 3mbit packet, including 3mbit Ethernet header
- but excluding the checksum word.
+ - Packet Length (2 bytes): Length (in 16-bit words) of the 3mbit Packet
+ Data field (see below)
+ - Packet Data (N bytes): The 3mbit packet, including 3mbit Ethernet header
+ but excluding the checksum word.
All words are stored in big-endian format.
@@ -437,7 +481,7 @@ As discussed in Section 5.0, all packets are broadcast. The technical reasons
for this are documented in the source code; see Transport\UDP.cs for details.
-9.0 Thanks
+10.0 Thanks
==========
This project would not have been possible without the conservation efforts of