From ad6990172497cb1b3460c9ec77cb97d132707234 Mon Sep 17 00:00:00 2001 From: Apollo3zehn Date: Thu, 18 Feb 2021 08:38:53 +0100 Subject: [PATCH 1/4] Prevent possible bug if product code is larger than int.MaxValue. --- src/EtherCAT.NET/EsiUtilities.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EtherCAT.NET/EsiUtilities.cs b/src/EtherCAT.NET/EsiUtilities.cs index b58460a..2cc93f2 100644 --- a/src/EtherCAT.NET/EsiUtilities.cs +++ b/src/EtherCAT.NET/EsiUtilities.cs @@ -151,8 +151,8 @@ private static (EtherCATInfo etherCATInfo, EtherCATInfoDescriptionsDevice device { var found = !string.IsNullOrWhiteSpace(currentDevice.Type.ProductCode) && !string.IsNullOrWhiteSpace(currentDevice.Type.RevisionNo) && - (int)EsiUtilities.ParseHexDecString(currentDevice.Type.ProductCode) == productCode && - (int)EsiUtilities.ParseHexDecString(currentDevice.Type.RevisionNo) == revision; + (uint)EsiUtilities.ParseHexDecString(currentDevice.Type.ProductCode) == productCode && + (uint)EsiUtilities.ParseHexDecString(currentDevice.Type.RevisionNo) == revision; if (found) info = currentInfo; From 2f1ee3c6261a6f4f781064dd12d9f01f40e34a59 Mon Sep 17 00:00:00 2001 From: Apollo3zehn Date: Wed, 24 Feb 2021 08:32:05 +0100 Subject: [PATCH 2/4] Fix ScanDevices performance issue. --- native/SOEM_wrapper/soem_wrapper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/SOEM_wrapper/soem_wrapper.c b/native/SOEM_wrapper/soem_wrapper.c index 2330def..7706488 100644 --- a/native/SOEM_wrapper/soem_wrapper.c +++ b/native/SOEM_wrapper/soem_wrapper.c @@ -559,12 +559,12 @@ int CALLCONV ScanDevices(ecx_contextt* context, char* interfaceName, ec_slave_in *slaveInfoSet = (ec_slave_info_t*)calloc(*context->slavecount + 1, sizeof(ec_slave_info_t)); *slaveCount = *context->slavecount; - // wait for all slaves to reach PREOP state - int counter = 200; + // request PREOP state for all slaves + int counter = 5; do { - ecx_statecheck(context, 0, EC_STATE_PRE_OP | EC_STATE_ACK, 5 * EC_TIMEOUTSTATE); + ecx_statecheck(context, 0, EC_STATE_PRE_OP, EC_TIMEOUTSTATE); } while (counter-- && (context->slavelist[0].state != (EC_STATE_PRE_OP | EC_STATE_ACK))); if (context->slavelist[0].state != (EC_STATE_PRE_OP | EC_STATE_ACK)) From 613baa1404d853b5a55bea43250c7ddf052c0da4 Mon Sep 17 00:00:00 2001 From: XzuluX <77434988+XzuluX@users.noreply.github.com> Date: Wed, 24 Feb 2021 08:55:39 +0100 Subject: [PATCH 3/4] Implemented Fw update over FoE (#56) * Implemented Fw update over FoE * Run processdata methods only in OP state --- native/SOEM_wrapper/exports.def | 8 +- native/SOEM_wrapper/soem_wrapper.c | 301 +++++++++++++++++- src/EtherCAT.NET/EcMaster.cs | 167 ++++++++-- .../Infrastructure/EthercatType.cs | 22 ++ src/SOEM.PInvoke/PInvoke/EcHL.cs | 43 ++- 5 files changed, 494 insertions(+), 47 deletions(-) create mode 100644 src/EtherCAT.NET/Infrastructure/EthercatType.cs diff --git a/native/SOEM_wrapper/exports.def b/native/SOEM_wrapper/exports.def index 2b4d2ca..a7bad40 100644 --- a/native/SOEM_wrapper/exports.def +++ b/native/SOEM_wrapper/exports.def @@ -17,7 +17,11 @@ EXPORTS ReadSlaveState ReadState RegisterCallback - RequestOpState + RequestState + GetState + DownloadFirmware + RegisterFOECallback + RequestCommonState ScanDevices SdoWrite UpdateCsa @@ -125,4 +129,4 @@ EXPORTS ec_sdoerror2string ec_ALstatuscode2string ec_soeerror2string - ecx_elist2string \ No newline at end of file + ecx_elist2string diff --git a/native/SOEM_wrapper/soem_wrapper.c b/native/SOEM_wrapper/soem_wrapper.c index 7706488..46d3af0 100644 --- a/native/SOEM_wrapper/soem_wrapper.c +++ b/native/SOEM_wrapper/soem_wrapper.c @@ -17,13 +17,26 @@ #include #include #include +#include #include "soem_wrapper.h" #define EC_VER2 +// size of firmware buffer +#define FWBUFSIZE (30 * 1024 * 1024) +char filebuffer[FWBUFSIZE]; // 30MB buffer + // private int _referenceSlave; +// FMMU / SM register content, needed to revert bootsrap config +ec_fmmut FMMU[2]; +char fmmu_zero[sizeof(ec_fmmut)]; + +ec_smt SM[2]; +char sm_zero[sizeof(ec_smt)]; + + uint16 CalculateCrc(byte* data) { uint16 result = 0xFF; @@ -329,25 +342,293 @@ int CALLCONV GetSyncManagerType(ecx_contextt* context, uint16 slaveIndex, uint16 return 0; } -int CALLCONV RequestOpState(ecx_contextt* context) +/* + * Clear FMMU and SM registers of slave in order to + * program bootstrap configuration + * + * context: Current context pointer + * slave: Slave number + */ +static void clear_FMMU_and_SM_registers(ecx_contextt* context, int slave) { - int counter = 200; + memset(sm_zero, 0, sizeof(sm_zero)); + memset(fmmu_zero, 0, sizeof(fmmu_zero)); + + /* clean FMMU registers */ + ecx_FPWR(context->port, context->slavelist[slave].configadr, ECT_REG_FMMU0, + sizeof(fmmu_zero), fmmu_zero, EC_TIMEOUTRET3); + ecx_FPWR(context->port, context->slavelist[slave].configadr, ECT_REG_FMMU1, + sizeof(fmmu_zero), fmmu_zero, EC_TIMEOUTRET3); + + /* clean SM0 and SM1 registers to set new bootstrap values later */ + ecx_FPWR(context->port, context->slavelist[slave].configadr, ECT_REG_SM0, + sizeof(ec_smt), sm_zero, EC_TIMEOUTRET3); + ecx_FPWR(context->port, context->slavelist[slave].configadr, ECT_REG_SM1, + sizeof(ec_smt), sm_zero, EC_TIMEOUTRET3); +} - context->slavelist[0].state = EC_STATE_OPERATIONAL; +/* + * Set boot mailbox configuration master --> slave + * + * slave: Pointer to ec_slavet + * startAddress: SM start address + * length: SM length + */ +static void set_rx_boot_mailbox(ec_slavet* slave, uint16 startAddress, uint16 length) +{ + slave->SM[0].StartAddr = startAddress; + slave->SM[0].SMlength = length; + /* store boot write mailbox address */ + slave->mbx_wo = startAddress; + /* store boot write mailbox size */ + slave->mbx_l = length; +} - ecx_send_processdata(context); - ecx_receive_processdata(context, EC_TIMEOUTRET); - ecx_writestate(context, 0); +/* + * Set boot mailbox configuration slave --> master + * + * slave: Pointer to ec_slavet + * startAddress: SM start address + * length: SM length + */ +static void set_tx_boot_mailbox(ec_slavet* slave, uint16 startAddress, uint16 length) +{ + slave->SM[1].StartAddr = startAddress; + slave->SM[1].SMlength = length; + /* store boot read mailbox address */ + slave->mbx_ro = startAddress; + /* store boot read mailbox size */ + slave->mbx_rl = length; +} + +/* + * Set SM mailbox bootstrap configuration + * + * context: Current context pointer + * slave: Slave number + */ +static void set_bootstrap(ecx_contextt* context, int slave) +{ + memset(FMMU, 0, sizeof(ec_fmmut) * 2); + /* read content of current FMMU registers */ + ecx_FPRD(context->port, context->slavelist[slave].configadr, ECT_REG_FMMU0, + sizeof(ec_fmmut), &FMMU[0], EC_TIMEOUTRET3); + ecx_FPRD(context->port, context->slavelist[slave].configadr, ECT_REG_FMMU1, + sizeof(ec_fmmut), &FMMU[1], EC_TIMEOUTRET3); + + memset(SM, 0, sizeof(ec_smt) * 2); + /* read content of current SM registers */ + ecx_FPRD(context->port, context->slavelist[slave].configadr, ECT_REG_SM0, + sizeof(ec_smt), &SM[0], EC_TIMEOUTRET3); + ecx_FPRD(context->port, context->slavelist[slave].configadr, ECT_REG_SM1, + sizeof(ec_smt), &SM[1], EC_TIMEOUTRET3); + + clear_FMMU_and_SM_registers(context, slave); + + /* read BOOT mailbox data, master -> slave */ + uint32 data = ecx_readeeprom(context, slave, ECT_SII_BOOTRXMBX, EC_TIMEOUTEEP); + set_rx_boot_mailbox(&context->slavelist[slave], (uint16)LO_WORD(data), (uint16)HI_WORD(data)); + + /* read BOOT mailbox data, slave -> master */ + data = ecx_readeeprom(context, slave, ECT_SII_BOOTTXMBX, EC_TIMEOUTEEP); + set_tx_boot_mailbox(&context->slavelist[slave], (uint16)LO_WORD(data), (uint16)HI_WORD(data)); + + /* program SM0 mailbox in for slave */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_SM0, + sizeof(ec_smt), &context->slavelist[slave].SM[0], EC_TIMEOUTRET); + /* program SM1 mailbox out for slave */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_SM1, + sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET); +} + +/* + * Revert SM mailbox bootstrap configuration + * + * context: Current context pointer + * slave: Slave number + */ +static void revert_bootstrap(ecx_contextt* context, int slave) +{ + clear_FMMU_and_SM_registers(context, slave); + + /* restore mailbox data, master -> slave */ + set_rx_boot_mailbox(&context->slavelist[slave], SM[0].StartAddr, SM[0].SMlength); + /* restore mailbox data, slave -> master */ + set_tx_boot_mailbox(&context->slavelist[slave], SM[1].StartAddr, SM[1].SMlength); + + /* restore SM0 mailbox in for slave */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_SM0, + sizeof(ec_smt), &context->slavelist[slave].SM[0], EC_TIMEOUTRET); + /* restore SM1 mailbox out for slave */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_SM1, + sizeof(ec_smt), &context->slavelist[slave].SM[1], EC_TIMEOUTRET); + + /* copy stored FMMU registers */ + memcpy(&context->slavelist[slave].FMMU[0], &FMMU[0], sizeof(ec_fmmut)); + memcpy(&context->slavelist[slave].FMMU[1], &FMMU[1], sizeof(ec_fmmut)); + + /* restore FMMU0 */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_FMMU0, + sizeof(ec_fmmut), &context->slavelist[slave].FMMU[0], EC_TIMEOUTRET); + /* restore FMMU0 */ + ecx_FPWR (context->port, context->slavelist[slave].configadr, ECT_REG_FMMU1, + sizeof(ec_fmmut), &context->slavelist[slave].FMMU[1], EC_TIMEOUTRET); +} + +/* + * Read firmware file to filebuffer + * + * fileName: File name + * length [out]: File length + * + * returns: 1 if operation was successful, -1 otherwise + */ +static int read_file(const char *fileName, int *length) +{ + memset(&filebuffer, 0, FWBUFSIZE); + + FILE * file = fopen(fileName, "rb"); + if(file == NULL) + return -1; + + int counter = 0, c; + while (((c = fgetc(file)) != EOF) && (counter < FWBUFSIZE)) + filebuffer[counter ++] = (uint8)c; + + *length = counter; + fclose(file); + return 1; +} + +/* + * Request new slave state. + * + * context: Current context pointer + slave: Slave number + * state: Requested slave state + * + * returns: Slave state which was set + */ +uint16 CALLCONV RequestState(ecx_contextt* context, int slave, uint16 state) +{ + if(state == EC_STATE_BOOT) + { + // set mailbox bootstrap configuration + set_bootstrap(context, slave); + } + + /* if current state is EC_STATE_BOOT and requested state is EC_STATE_INIT + we have to restore FMMU and SM registers */ + if((context->slavelist[slave].state == EC_STATE_BOOT) && + (state == EC_STATE_INIT)) + { + revert_bootstrap(context, slave); + } + + context->slavelist[slave].state = state; + ecx_writestate(context, slave); + + uint16 slaveState = EC_STATE_NONE; + int counter = 10; - // wait for all slaves to reach OP state do + { + /* wait for slave to reach requested state, + returns current slave state */ + slaveState = ecx_statecheck(context, slave, state, EC_TIMEOUTSTATE); + } while ((counter--) && (slaveState != state)); + + return slaveState; +} + +/* + * Return current slave state. + * + * context: Current context pointer + * slave: Slave number + * + * returns: Current slave state + */ +uint16 CALLCONV GetState(ecx_contextt* context, int slave) +{ + return context->slavelist[slave].state; +} + +/* + * Download firmware file to slave. + * + * context: Current context pointer + * slave: Slave number + * fileName: File name + * length: File length + * + * returns: Workcounter from last slave response + */ +int CALLCONV DownloadFirmware(ecx_contextt* context, int slave, char *fileName, int length) +{ + if(context->slavelist[slave].state != EC_STATE_BOOT) + return -1; + + int wk = 0; + int readLength = 0; + if(read_file(fileName, &readLength)) + { + if(readLength == length) + { + wk = ecx_FOEwrite(context, slave, fileName, 0, readLength , &filebuffer, EC_TIMEOUTSTATE); + } + } + + return (wk > 0) ? 1 : -1; +} + +/* + * Register callback for FoE. + * + * context: Current context pointer + * callback: Callback function pointer + * + */ +void CALLCONV RegisterFOECallback(ecx_contextt* context, int CALLCONV callback(uint16 slave, int packetnumber, int datasize)) +{ + context->FOEhook = (int (*)(uint16 slave, int packetnumber, int datasize))callback; +} + +/* + * Request specific state for all slaves. + * + * context: Current context pointer + * state: Requested state + * + * returns: 1 if operation was successful, -0x0601 otherwise + */ +int CALLCONV RequestCommonState(ecx_contextt* context, uint16 state) +{ + int counter = 200; + context->slavelist[0].state = state; + bool operationalState = (state == EC_STATE_OPERATIONAL); + + if(operationalState) { ecx_send_processdata(context); ecx_receive_processdata(context, EC_TIMEOUTRET); - ecx_statecheck(context, 0, EC_STATE_OPERATIONAL, 5 * EC_TIMEOUTSTATE); - } while (counter-- && (context->slavelist[0].state != EC_STATE_OPERATIONAL)); + } - return context->slavelist[0].state == EC_STATE_OPERATIONAL ? 1 : -0x0601; + ecx_writestate(context, 0); + + // wait for all slaves to reach state + do + { + if(operationalState) + { + ecx_send_processdata(context); + ecx_receive_processdata(context, EC_TIMEOUTRET); + } + + ecx_statecheck(context, 0, state, 5 * EC_TIMEOUTSTATE); + } while (counter-- && (context->slavelist[0].state != state)); + + return context->slavelist[0].state == state ? 1 : -0x0601; } int CALLCONV CheckSafeOpState(ecx_contextt* context) diff --git a/src/EtherCAT.NET/EcMaster.cs b/src/EtherCAT.NET/EcMaster.cs index 42b887e..0a532f1 100644 --- a/src/EtherCAT.NET/EcMaster.cs +++ b/src/EtherCAT.NET/EcMaster.cs @@ -11,6 +11,7 @@ using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using System.IO; namespace EtherCAT.NET { @@ -52,6 +53,7 @@ public class EcMaster : IDisposable private bool _isReconfiguring; private CancellationTokenSource _cts; private Task _watchdogTask; + private bool _watchDogActive = true; #endregion @@ -278,7 +280,7 @@ public void Configure(SlaveInfo rootSlave = null) #region "Op" - EcUtilities.CheckErrorCode(this.Context, EcHL.RequestOpState(this.Context), nameof(EcHL.RequestOpState)); + EcUtilities.CheckErrorCode(this.Context, EcHL.RequestCommonState(this.Context, (UInt16)SlaveState.Operational), nameof(EcHL.RequestCommonState)); #endregion @@ -377,56 +379,157 @@ public void UpdateIO(DateTime referenceDateTime) } } + /// + /// Activate watchdog. + /// + /// True: Watchdog is activated, + /// false: Watchdog is deactivated. + public void ActivateWatchdog(bool value) + { + _watchDogActive = value; + } + + /// + /// Request slave state transition. + /// + /// Slave index. + /// Slave target state. + /// True if transition was successful, false otherwise./returns> + public bool RequestState(int slaveIndex, SlaveState slaveState) + { + UInt16 stateSet = EcHL.RequestState(this.Context, slaveIndex, (UInt16)slaveState); + return (SlaveState)stateSet == slaveState; + } + + /// + /// Return current state of slave. + /// + /// Slave index. + /// Current slave state./returns> + public SlaveState GetState(int slaveIndex) + { + ushort slaveState = EcHL.GetState(this.Context, slaveIndex); + return (SlaveState)slaveState; + } + + /// + /// Download firmware file to slave. + /// 1. All detected slaves are set to PREOP state. + /// 2. Target slave is set to INIT state. + /// 3. Target slave is set to BOOT state. + /// 4. Firmware file is downloaded to target slave. + /// 5. Target slave is set to INIT state regardless of whether + /// the file download was successful or not. + /// + /// Slave index. + /// Absolute path to firmware file. + /// True if operation was successful, false otherwise./returns> + public bool DownloadFirmware(int slaveIndex, string fileName) + { + FileInfo fileInfo = new FileInfo(fileName); + if (!fileInfo.Exists) + return false; + + bool success = false; + if (EcHL.RequestCommonState(this.Context, (UInt16)SlaveState.PreOp) == 1) + { + UInt16 currentState = EcHL.RequestState(this.Context, slaveIndex, (UInt16)SlaveState.Init); + if (currentState == (UInt16)SlaveState.Init) + { + currentState = EcHL.RequestState(this.Context, slaveIndex, (UInt16)SlaveState.Boot); + if (currentState == (UInt16)SlaveState.Boot) + { + int currentPackageNumber = -1; + int totalPackages = 0; + int remainingSize = -1; + + EcHL.FOECallback callback = (slaveIndex, packageNumber, datasize) => + { + if(packageNumber == 0) + _logger.LogInformation($"FoE: Write {datasize} bytes to {slaveIndex}. slave"); + else + _logger.LogInformation($"FoE: {packageNumber}. package with {remainingSize - datasize} bytes written to {slaveIndex}. slave. Remaining data: {datasize} bytes"); + + if (currentPackageNumber != packageNumber) + { + currentPackageNumber = packageNumber; + if(packageNumber != 0) + totalPackages++; + } + + remainingSize = datasize; + return 0; + }; + + EcHL.RegisterFOECallback(this.Context, callback); + GC.KeepAlive(callback); + + int wk = EcHL.DownloadFirmware(this.Context, slaveIndex, fileName, (int)fileInfo.Length); + + _logger.LogInformation($"FoE: {totalPackages} packages written"); + success = (remainingSize == 0) && (wk > 0); + + EcHL.RequestState(this.Context, slaveIndex, (UInt16)SlaveState.Init); + } + } + } + + return success; + } + + #endregion private void WatchdogRoutine() { while (!_cts.IsCancellationRequested) { - var state = EcHL.ReadState(this.Context); - - if (state < 8) + if (_watchDogActive) { - _statusCheckFailedCounter++; + var state = EcHL.ReadState(this.Context); - if (_statusCheckFailedCounter >= _settings.MaxRetries) + if (state < 8) { - try + _statusCheckFailedCounter++; + + if (_statusCheckFailedCounter >= _settings.MaxRetries) { - lock (_lock) + try { - _isReconfiguring = true; - _logger.LogInformation("reconfiguration started"); - } + lock (_lock) + { + _isReconfiguring = true; + _logger.LogInformation("reconfiguration started"); + } - this.Configure(); - _logger.LogInformation("reconfiguration successful"); + this.Configure(); + _logger.LogInformation("reconfiguration successful"); - _isReconfiguring = false; - } - catch (Exception) - { - _logger.LogWarning("reconfiguration failed"); - } - finally - { - _statusCheckFailedCounter = 0; + _isReconfiguring = false; + } + catch (Exception) + { + _logger.LogWarning("reconfiguration failed"); + } + finally + { + _statusCheckFailedCounter = 0; + } } } - } - else - { - if (_isReconfiguring) + else { - _logger.LogInformation("communication restored"); - _isReconfiguring = false; - } + if (_isReconfiguring) + { + _logger.LogInformation("communication restored"); + _isReconfiguring = false; + } - _statusCheckFailedCounter = 0; + _statusCheckFailedCounter = 0; + } } - _cts.Token.WaitHandle.WaitOne(TimeSpan.FromSeconds(_settings.WatchdogSleepTime)); - } + } } #region IDisposable Support diff --git a/src/EtherCAT.NET/Infrastructure/EthercatType.cs b/src/EtherCAT.NET/Infrastructure/EthercatType.cs new file mode 100644 index 0000000..83eb04e --- /dev/null +++ b/src/EtherCAT.NET/Infrastructure/EthercatType.cs @@ -0,0 +1,22 @@ +using System; +namespace EtherCAT.NET.Infrastructure +{ + public enum SlaveState : ushort + { + // No valid state. + None = 0x00, + // Init state* + Init = 0x01, + // Pre-operational. + PreOp = 0x02, + // Boot state + Boot = 0x03, + // Safe-operational. + SafeOp = 0x04, + // Operational + Operational = 0x08, + // Error or ACK error + Ack = 0x10, + Error = 0x10 + } +} diff --git a/src/SOEM.PInvoke/PInvoke/EcHL.cs b/src/SOEM.PInvoke/PInvoke/EcHL.cs index f660b59..76b5e63 100644 --- a/src/SOEM.PInvoke/PInvoke/EcHL.cs +++ b/src/SOEM.PInvoke/PInvoke/EcHL.cs @@ -9,6 +9,10 @@ public static class EcHL [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate double PO2SOCallback(UInt16 slaveIndex); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int FOECallback(UInt16 slave, int packetnumber, int datasize); + + #region "Helper" #endregion @@ -134,12 +138,45 @@ public static class EcHL public static extern int CheckSafeOpState(IntPtr context); /// - /// Requests OP state. + /// Requests slave state. /// - /// Returns the status code of the unmanaged operation. + /// Returns the current state of slave. + [SuppressUnmanagedCodeSecurity] + [DllImport(EcShared.NATIVE_DLL_NAME)] + public static extern UInt16 RequestState(IntPtr context, int slaveIndex, UInt16 state); + + + /// + /// Gets current slave state. + /// + /// Returns the current state of slave. [SuppressUnmanagedCodeSecurity] [DllImport(EcShared.NATIVE_DLL_NAME)] - public static extern int RequestOpState(IntPtr context); + public static extern UInt16 GetState(IntPtr context, int slaveIndex); + + + /// + /// Downloads firmware file to slave. + /// + /// Returns workcounter from last slave response. + [SuppressUnmanagedCodeSecurity] + [DllImport(EcShared.NATIVE_DLL_NAME)] + public static extern int DownloadFirmware(IntPtr context, int slaveIndex, string fileName, int length); + + + [SuppressUnmanagedCodeSecurity] + [DllImport(EcShared.NATIVE_DLL_NAME)] + public static extern void RegisterFOECallback(IntPtr context, [MarshalAs(UnmanagedType.FunctionPtr)] FOECallback callback); + + + /// + /// Request specific state for all slaves. + /// + /// Returns 1 if operation was successful, -0x0601 otherwise. + [SuppressUnmanagedCodeSecurity] + [DllImport(EcShared.NATIVE_DLL_NAME)] + public static extern int RequestCommonState(IntPtr context, UInt16 state); + [SuppressUnmanagedCodeSecurity] [DllImport(EcShared.NATIVE_DLL_NAME)] From 37e3a11062db4f2fe3a80fb1ad70e8ea381ca4d8 Mon Sep 17 00:00:00 2001 From: Apollo3zehn Date: Wed, 24 Mar 2021 12:51:17 +0100 Subject: [PATCH 4/4] Bump version. --- build/build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.props b/build/build.props index 26a3137..2f9bbb0 100644 --- a/build/build.props +++ b/build/build.props @@ -13,7 +13,7 @@ 1 0 0 - alpha.6 + alpha.7