1
0
mirror of https://github.com/livingcomputermuseum/IFS.git synced 2026-02-28 01:25:31 +00:00
Files
livingcomputermuseum.IFS/PUP/Transport/3MbitAdapter.cs

722 lines
27 KiB
C#

/*
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 <http://www.gnu.org/licenses/>.
*/
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
{
/// <summary>
/// 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.
/// </summary>
public class Ether3MbitInterface : IPacketInterface
{
public Ether3MbitInterface()
{
_ledController = new BeagleBoneLedController();
InitializePRU();
StartReceiver();
StartHeartbeat();
}
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 StartHeartbeat()
{
ThreadPool.QueueUserWorkItem((ctx) =>
{
while(true)
{
_ledController.BlinkLed(0, 500);
Thread.Sleep(75);
_ledController.BlinkLed(0, 100);
Thread.Sleep(100);
}
}, null);
}
private void StartReceiver()
{
ThreadPool.QueueUserWorkItem((ctx) =>
{
Log.Write(LogType.Normal, LogComponent.E3Mbit, "Starting receiver thread.");
ReceiveWorker();
}, null);
}
/// <summary>
/// Worker thread function. Waits for incoming packets on the 3mbit network and handles them
/// when they arrive.
/// </summary>
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);
}
/// <summary>
/// Pulls data received from the 3mbit interface and passes it to the router.
/// </summary>
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;
}
_ledController.SetLed(3, 1);
// 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.");
_ledController.SetLed(3, 0);
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);
_ledController.SetLed(3, 0);
Log.Write(LogType.Verbose, LogComponent.E3Mbit, "Received packet (0x{0:x} bytes), sent to router.", receivedDataLength);
}
/// <summary>
/// Sends data to the 3mbit interface.
/// </summary>
private void SendToNetworkInterface(byte[] data)
{
if (!HostOwnsWriteBuffer())
{
// Shouldn't happen
Log.Write(LogType.Error, LogComponent.E3Mbit, "SendToNetworkInterface called when PRU is not ready.");
return;
}
_ledController.SetLed(2, 1);
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);
_ledController.SetLed(2, 0);
Log.Write(LogType.Verbose, LogComponent.E3Mbit, "Packet sent to 3mbit interface.");
}
/// <summary>
/// Decodes bit timings into packet data. Returns null if issues were found with the data.
/// </summary>
/// <param name="durationBuf"></param>
/// <returns></returns>
byte[] DecodeDurationBuffer(byte[] durationBuf) {
Log.Write(LogType.Verbose, LogComponent.E3Mbit, $"Decoding duration buffer of length {durationBuf.Length}.");
List<byte> byteBuffer = new List<byte>();
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;
}
private IntPtr _sharedPruMemory;
private ReceivedPacketCallback _routerCallback;
private BeagleBoneLedController _ledController;
// 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;
}
public class BeagleBoneLedController
{
const string _ledPath = "/sys/class/leds/beaglebone:green:usr";
public BeagleBoneLedController()
{
try
{
Init();
}
catch(Exception e)
{
Log.Write(LogType.Warning, LogComponent.E3Mbit, "Failed to intialize LEDs. Error: {0}", e.Message);
}
}
public void SetLed(int n, int brightness)
{
if (n >= 0 && n <= _ledStream?.Length)
{
_ledStream?[n]?.WriteLine($"{brightness}");
}
}
public void BlinkLed(int n, int durationMsec)
{
SetLed(n, 1);
Thread.Sleep(durationMsec);
SetLed(n, 0);
}
private void Init()
{
_ledStream = new StreamWriter[4];
for (int i = 0; i < 4; i++)
{
using (StreamWriter sw = new StreamWriter(Path.Combine(_ledPath + $"{i}", "trigger")))
{
sw.WriteLine("none");
}
_ledStream[i] = new StreamWriter(Path.Combine(_ledPath + $"{i}", "brightness"));
_ledStream[i].AutoFlush = true;
SetLed(i, 0);
}
}
private StreamWriter[] _ledStream;
}
/// <summary>
/// Provides constants, structs, and functions needed to P/Invoke into prussdrv lib calls.
/// </summary>
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);
}
}