mirror of
https://github.com/gusmanb/logicanalyzer.git
synced 2025-02-05 10:08:06 +00:00
412 lines
15 KiB
C#
412 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Net.Sockets;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Channels;
|
|
using System.Threading.Tasks;
|
|
using static System.Collections.Specialized.BitVector32;
|
|
|
|
namespace SharedDriver
|
|
{
|
|
public class MultiAnalyzerDriver : AnalyzerDriverBase
|
|
{
|
|
|
|
/*
|
|
|
|
VID/PID: 1902/3020
|
|
Windows: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\usbser\Enum
|
|
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_1209&PID_3020&MI_00
|
|
|
|
Linux: /sys/bus/usb/devices/x-y
|
|
/sys/bus/usb/devices/x-y:1-0/tty
|
|
*/
|
|
|
|
#region Properties
|
|
public override bool IsCapturing { get { return capturing; } }
|
|
public override bool IsNetwork { get { return false; } }
|
|
public override string? DeviceVersion { get { return version; } }
|
|
public override int ChannelCount { get { return connectedDevices.Min(d => d.ChannelCount) * connectedDevices.Length; } }
|
|
public override int MaxFrequency { get { return connectedDevices.Min(d => d.MaxFrequency); } }
|
|
public override int MinFrequency { get { return connectedDevices.Max(d => d.MinFrequency); } }
|
|
public override int BufferSize { get { return connectedDevices.Min(d => d.BufferSize); } }
|
|
public override int BlastFrequency
|
|
{
|
|
get
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
public override AnalyzerDriverType DriverType
|
|
{
|
|
get
|
|
{
|
|
return AnalyzerDriverType.Multi;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Events
|
|
public override event EventHandler<CaptureEventArgs>? CaptureCompleted;
|
|
#endregion
|
|
|
|
#region Variables
|
|
|
|
//Multidevice variables
|
|
LogicAnalyzerDriver[] connectedDevices;
|
|
deviceCapture[]? tempCapture;
|
|
CaptureSession? sourceSession;
|
|
|
|
object locker = new object();
|
|
|
|
//General data
|
|
bool capturing = false;
|
|
string? version;
|
|
|
|
//Optional callback
|
|
private Action<CaptureEventArgs>? currentCaptureHandler;
|
|
#endregion
|
|
|
|
public MultiAnalyzerDriver(string[] ConnectionStrings) //First connection string must belong to the master device
|
|
{
|
|
if (ConnectionStrings == null || ConnectionStrings.Length < 2 || ConnectionStrings.Length > 5)
|
|
throw new ArgumentOutOfRangeException(nameof(ConnectionStrings), $"Invalid devices specified, 2 to 5 connection strings must be provided");
|
|
|
|
int pos = 0;
|
|
connectedDevices = new LogicAnalyzerDriver[ConnectionStrings.Length];
|
|
|
|
try
|
|
{
|
|
for (pos = 0; pos < ConnectionStrings.Length; pos++)
|
|
{
|
|
connectedDevices[pos] = new LogicAnalyzerDriver(ConnectionStrings[pos]);
|
|
}
|
|
foreach (var dev in connectedDevices)
|
|
dev.CaptureCompleted += Dev_CaptureCompleted;
|
|
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
for (int buc = 0; buc < connectedDevices.Length; buc++)
|
|
{
|
|
if (connectedDevices[buc] != null)
|
|
connectedDevices[buc].Dispose();
|
|
}
|
|
|
|
throw new DeviceConnectionException($"Error connecting to device {ConnectionStrings[pos]}.", ex);
|
|
}
|
|
|
|
DeviceVersion? masterVersion = null;
|
|
|
|
foreach (var device in connectedDevices)
|
|
{
|
|
|
|
var devVer = VersionValidator.GetVersion(device.DeviceVersion);
|
|
|
|
if (!devVer.IsValid)
|
|
{
|
|
Dispose();
|
|
throw new DeviceConnectionException($"Invalid device version ({device.DeviceVersion}) found on device {ConnectionStrings[(int)device.Tag]}");
|
|
}
|
|
|
|
if (masterVersion == null)
|
|
masterVersion = devVer;
|
|
else
|
|
{
|
|
if (masterVersion.Major != devVer.Major || masterVersion.Minor != devVer.Minor)
|
|
{
|
|
Dispose();
|
|
throw new DeviceConnectionException($"Different device versions found. Master version: V{masterVersion.Major}_{masterVersion.Minor}, device {ConnectionStrings[(int)device.Tag]} version: V{devVer.Major}_{devVer.Minor}.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (masterVersion == null)
|
|
throw new DeviceConnectionException("No devices found.");
|
|
|
|
version = $"MULTI_ANALYZER_{masterVersion.Major}_{masterVersion.Minor}";
|
|
}
|
|
|
|
#region Capture code
|
|
|
|
public override CaptureError StartCapture(CaptureSession Session, Action<CaptureEventArgs>? CaptureCompletedHandler = null)
|
|
{
|
|
if (Session.TriggerType == TriggerType.Edge)
|
|
return CaptureError.BadParams;
|
|
|
|
try
|
|
{
|
|
if (capturing)
|
|
return CaptureError.Busy;
|
|
|
|
if (Session.CaptureChannels == null || Session.CaptureChannels.Length < 0)
|
|
return CaptureError.BadParams;
|
|
|
|
var numChan = Session.CaptureChannels.Select(c => c.ChannelNumber).ToArray();
|
|
|
|
var captureLimits = GetLimits(numChan);
|
|
var captureMode = GetCaptureMode(numChan);
|
|
|
|
if (
|
|
numChan.Min() < 0 ||
|
|
numChan.Max() > ChannelCount - 1 ||
|
|
Session.TriggerBitCount < 1 ||
|
|
Session.TriggerBitCount > 16 ||
|
|
Session.TriggerChannel < 0 ||
|
|
Session.TriggerChannel > 15 ||
|
|
Session.TriggerChannel + Session.TriggerBitCount > (Session.TriggerType == TriggerType.Complex ? 16 : 5) ||
|
|
Session.PreTriggerSamples < captureLimits.MinPreSamples ||
|
|
Session.PostTriggerSamples < captureLimits.MinPostSamples ||
|
|
Session.PreTriggerSamples > captureLimits.MaxPreSamples ||
|
|
Session.PostTriggerSamples > captureLimits.MaxPostSamples ||
|
|
Session.PreTriggerSamples + Session.PostTriggerSamples > captureLimits.MaxTotalSamples ||
|
|
Session.Frequency < MinFrequency ||
|
|
Session.Frequency > MaxFrequency
|
|
)
|
|
return CaptureError.BadParams;
|
|
|
|
|
|
int[][] channelsPerDevice = SplitChannelsPerDevice(numChan);
|
|
|
|
if (channelsPerDevice.Length > numChan.Length)
|
|
return CaptureError.BadParams;
|
|
|
|
if (channelsPerDevice[0].Length < 1)
|
|
return CaptureError.BadParams;
|
|
|
|
double samplePeriod = 1000000000.0 / Session.Frequency;
|
|
double delay = Session.TriggerType == TriggerType.Fast ? TriggerDelays.FastTriggerDelay : TriggerDelays.ComplexTriggerDelay;
|
|
int offset = (int)(Math.Round((delay / samplePeriod) + 0.3, 0));
|
|
|
|
tempCapture = new deviceCapture[connectedDevices.Length];
|
|
|
|
for (int bc = 0; bc < tempCapture.Length; bc++)
|
|
tempCapture[bc] = new deviceCapture();
|
|
|
|
currentCaptureHandler = CaptureCompletedHandler;
|
|
sourceSession = Session;
|
|
|
|
capturing = true;
|
|
|
|
int channelsCapturing = 1;
|
|
|
|
//Start capturing on all devices except master, master will be the last one to start
|
|
for (int buc = 1; buc < channelsPerDevice.Length; buc++)
|
|
{
|
|
var chan = channelsPerDevice[buc];
|
|
|
|
if (chan.Length == 0)
|
|
{
|
|
tempCapture[buc].Completed = true;
|
|
continue;
|
|
}
|
|
|
|
var devSes = Session.Clone();
|
|
|
|
devSes.CaptureChannels = new AnalyzerChannel[chan.Length];
|
|
|
|
for(int bChan = 0; bChan < chan.Length; bChan++)
|
|
devSes.CaptureChannels[bChan] = new AnalyzerChannel { ChannelNumber = chan[bChan] };
|
|
|
|
devSes.TriggerChannel = 24;
|
|
devSes.TriggerType = TriggerType.Edge;
|
|
devSes.PreTriggerSamples = Session.PreTriggerSamples + offset;
|
|
devSes.PostTriggerSamples = Session.PostTriggerSamples - offset;
|
|
devSes.LoopCount = 0;
|
|
devSes.MeasureBursts = false;
|
|
devSes.TriggerInverted = false;
|
|
|
|
connectedDevices[buc].Tag = channelsCapturing;
|
|
var err = connectedDevices[buc].StartCapture(devSes);
|
|
|
|
if (err != CaptureError.None)
|
|
{
|
|
StopCapture();
|
|
return err;
|
|
}
|
|
|
|
channelsCapturing++;
|
|
}
|
|
|
|
connectedDevices[0].Tag = 0;
|
|
|
|
var chanMaster = channelsPerDevice[0];
|
|
|
|
var masterSes = Session.Clone();
|
|
masterSes.CaptureChannels = new AnalyzerChannel[chanMaster.Length];
|
|
|
|
for (int bChan = 0; bChan < chanMaster.Length; bChan++)
|
|
masterSes.CaptureChannels[bChan] = new AnalyzerChannel { ChannelNumber = chanMaster[bChan] };
|
|
|
|
var errMaster = connectedDevices[0].StartCapture(masterSes);
|
|
|
|
if (errMaster != CaptureError.None)
|
|
{
|
|
StopCapture();
|
|
return errMaster;
|
|
}
|
|
|
|
return CaptureError.None;
|
|
}
|
|
catch { return CaptureError.UnexpectedError; }
|
|
}
|
|
|
|
public override bool StopCapture()
|
|
{
|
|
if (!capturing)
|
|
return false;
|
|
|
|
foreach (var dev in connectedDevices)
|
|
dev.StopCapture();
|
|
|
|
capturing = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
private void Dev_CaptureCompleted(object? sender, CaptureEventArgs e)
|
|
{
|
|
lock (locker)
|
|
{
|
|
if (!capturing)
|
|
return;
|
|
|
|
if(!e.Success)
|
|
{
|
|
StopCapture();
|
|
|
|
tempCapture = null;
|
|
|
|
if (currentCaptureHandler != null)
|
|
currentCaptureHandler(new CaptureEventArgs { Success = false, Session = sourceSession });
|
|
else if (CaptureCompleted != null)
|
|
CaptureCompleted(this, new CaptureEventArgs { Success = false, Session = sourceSession });
|
|
|
|
return;
|
|
}
|
|
|
|
int idx = (int)((sender as LogicAnalyzerDriver).Tag);
|
|
|
|
tempCapture[idx].Session = e.Session;
|
|
tempCapture[idx].Completed = true;
|
|
|
|
if (tempCapture.All(c => c.Completed))
|
|
{
|
|
var maxChanPerDev = connectedDevices.Min(c => c.ChannelCount);
|
|
|
|
for (int buc = 0; buc < tempCapture.Length; buc++)
|
|
{
|
|
if (tempCapture[buc].Session != null)
|
|
{
|
|
foreach(var chan in tempCapture[buc].Session.CaptureChannels)
|
|
{
|
|
var destChan = sourceSession.CaptureChannels.First(c => c.ChannelNumber == chan.ChannelNumber + buc * maxChanPerDev);
|
|
|
|
destChan.Samples = chan.Samples;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentCaptureHandler != null)
|
|
currentCaptureHandler(new CaptureEventArgs { Success = true, Session = sourceSession });
|
|
else if (CaptureCompleted != null)
|
|
CaptureCompleted(this, new CaptureEventArgs { Success = true, Session = sourceSession });
|
|
|
|
capturing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Bootloader-related functions
|
|
public override bool EnterBootloader()
|
|
{
|
|
try
|
|
{
|
|
if (capturing)
|
|
return false;
|
|
|
|
return connectedDevices.All(d => d.EnterBootloader());
|
|
}
|
|
catch { return false; }
|
|
}
|
|
#endregion
|
|
|
|
#region Device information functions
|
|
public override CaptureMode GetCaptureMode(int[] Channels)
|
|
{
|
|
var split = SplitChannelsPerDevice(Channels);
|
|
var maxChannel = split.Select(c => c.DefaultIfEmpty(0).Max()).DefaultIfEmpty(0).Max();
|
|
return maxChannel < 8 ? CaptureMode.Channels_8 : (maxChannel < 16 ? CaptureMode.Channels_16 : CaptureMode.Channels_24);
|
|
}
|
|
|
|
private int[][] SplitChannelsPerDevice(int[] Channels)
|
|
{
|
|
List<int[]> channelsPerDevice = new List<int[]>();
|
|
|
|
var maxChanPerDev = connectedDevices.Min(c => c.ChannelCount);
|
|
|
|
for (int buc = 0; buc < connectedDevices.Length; buc++)
|
|
{
|
|
int firstChan = buc * maxChanPerDev;
|
|
int lastChan = (buc + 1) * maxChanPerDev;
|
|
|
|
int[] devChan = Channels.Where(c => c >= firstChan && c < lastChan).Select(c => c - firstChan).ToArray();
|
|
channelsPerDevice.Add(devChan);
|
|
}
|
|
|
|
return channelsPerDevice.ToArray();
|
|
}
|
|
|
|
public override CaptureLimits GetLimits(int[] Channels)
|
|
{
|
|
|
|
var split = SplitChannelsPerDevice(Channels);
|
|
var limits = connectedDevices.Select((dev, idx) => dev.GetLimits(split[idx])).ToArray();
|
|
|
|
var minimalLimits = new CaptureLimits
|
|
{
|
|
MinPreSamples = limits.Max(l => l.MinPreSamples),
|
|
MaxPreSamples = limits.Min(l => l.MaxPreSamples),
|
|
MinPostSamples = limits.Max(l => l.MinPostSamples),
|
|
MaxPostSamples = limits.Min(l => l.MaxPostSamples),
|
|
};
|
|
|
|
return minimalLimits;
|
|
}
|
|
#endregion
|
|
|
|
#region Network-related functions
|
|
public override unsafe bool SendNetworkConfig(string AccesPointName, string Password, string IPAddress, ushort Port)
|
|
{
|
|
return false;
|
|
}
|
|
public override string? GetVoltageStatus()
|
|
{
|
|
return "UNSUPPORTED";
|
|
}
|
|
#endregion
|
|
|
|
#region IDisposable implementation
|
|
public override void Dispose()
|
|
{
|
|
foreach (var dev in connectedDevices)
|
|
dev.Dispose();
|
|
}
|
|
#endregion
|
|
|
|
class deviceCapture
|
|
{
|
|
public bool Completed { get; set; }
|
|
public CaptureSession? Session { get; set; }
|
|
}
|
|
}
|
|
}
|