Commit 64361c55 authored by Andreas Müller's avatar Andreas Müller

Fixed problems with errno on UnixIOException

parent d946520f
Pipeline #26 passed with stage
in 2 minutes and 58 seconds
......@@ -305,8 +305,8 @@ namespace AMWD.Modbus.Serial.Client
{
var rs485 = GetDriverState();
serialDriverFlags = rs485.Flags;
rs485.Flags |= RS485Flags.SerRS485Enabled;
rs485.Flags &= ~RS485Flags.SerRS485RxDuringTx;
rs485.Flags |= RS485Flags.Enabled;
rs485.Flags &= ~RS485Flags.RxDuringTx;
SetDriverState(rs485);
driverModified = true;
}
......
......@@ -5,20 +5,29 @@ using System.Security.Permissions;
namespace AMWD.Modbus.Serial.Util
{
/// <summary>
/// Implements a safe handle for unix systems.
/// Found on https://stackoverflow.com/a/10388107
/// </summary>
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal sealed class SafeUnixHandle : SafeHandle
{
/// <summary>
/// Initializes a new instance of the <see cref="SafeUnixHandle"/> class.
/// </summary>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private SafeUnixHandle()
: base(new IntPtr(-1), true)
{ }
/// <inheritdoc/>
public override bool IsInvalid
{
get { return handle == new IntPtr(-1); }
}
/// <inheritdoc/>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
......
......@@ -3,22 +3,49 @@ using System.Runtime.InteropServices;
namespace AMWD.Modbus.Serial.Util
{
/// <summary>
/// Represents the structure of the driver settings for RS485.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 32)]
internal struct SerialRS485
{
/// <summary>
/// The flags to change the driver state.
/// </summary>
public RS485Flags Flags;
/// <summary>
/// The delay in milliseconds before send.
/// </summary>
public uint RtsDelayBeforeSend;
/// <summary>
/// The delay in milliseconds after send.
/// </summary>
public uint RtsDelayAfterSend;
}
/// <summary>
/// The flags for the driver state.
/// </summary>
[Flags]
internal enum RS485Flags : uint
{
SerRS485Enabled = 1,
SerRS485RtsOnSend = 2,
SerRS485RtsAfterSend = 4,
SerRS485RxDuringTx = 16
/// <summary>
/// RS485 is enabled.
/// </summary>
Enabled = 1,
/// <summary>
/// RS485 uses RTS on send.
/// </summary>
RtsOnSend = 2,
/// <summary>
/// RS485 uses RTS after send.
/// </summary>
RtsAfterSend = 4,
/// <summary>
/// Receive during send (duplex).
/// </summary>
RxDuringTx = 16
}
}
......@@ -2,12 +2,12 @@
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text;
namespace AMWD.Modbus.Serial.Util
{
/// <summary>
/// Represents a unix specific IO exception.
/// Found on https://stackoverflow.com/a/10388107
/// </summary>
[Serializable]
public class UnixIOException : ExternalException
......@@ -74,33 +74,28 @@ namespace AMWD.Modbus.Serial.Util
/// </summary>
public int NativeErrorCode { get; }
/// <summary>
/// Tries to get the object data.
/// </summary>
/// <param name="info">The serialization information.</param>
/// <param name="context">The stream context.</param>
/// <inheritdoc/>
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
throw new ArgumentNullException(nameof(info));
}
info.AddValue("NativeErrorCode", NativeErrorCode);
base.GetObjectData(info, context);
}
private static string GetErrorMessage(int error)
private static string GetErrorMessage(int errno)
{
try
{
var buffer = new StringBuilder(256);
var res = UnsafeNativeMethods.StrError(error, buffer, (ulong)buffer.Capacity);
return res == -1 ? $"Unknown error (0x{error:x})" : buffer.ToString();
var ptr = UnsafeNativeMethods.StrError(errno);
return Marshal.PtrToStringAnsi(ptr);
}
catch (EntryPointNotFoundException)
catch
{
return $"Unknown error (0x{error:x})";
return $"Unknown error (0x{errno:x})";
}
}
}
......
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
namespace AMWD.Modbus.Serial.Util
{
/// <summary>
/// Definitions of the unsafe system methods.
/// Found on https://stackoverflow.com/a/10388107
/// </summary>
internal static class UnsafeNativeMethods
{
/// <summary>
/// A flag for <see cref="Open(string, uint)"/>.
/// </summary>
internal const int O_RDWR = 2;
/// <summary>
/// A flag for <see cref="Open(string, uint)"/>.
/// </summary>
internal const int O_NOCTTY = 256;
/// <summary>
/// A flag for <see cref="IoCtl(SafeUnixHandle, uint, ref SerialRS485)"/>.
/// </summary>
internal const uint TIOCGRS485 = 0x542E;
/// <summary>
/// A flag for <see cref="IoCtl(SafeUnixHandle, uint, ref SerialRS485)"/>.
/// </summary>
internal const uint TIOCSRS485 = 0x542F;
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("libc", EntryPoint = "close", SetLastError = true)]
internal static extern int Close(IntPtr handle);
/// <summary>
/// Opens a handle to a defined path (serial port).
/// </summary>
/// <param name="path">The path to open the handle.</param>
/// <param name="flag">The flags for the handle.</param>
/// <returns></returns>
[DllImport("libc", EntryPoint = "open", SetLastError = true)]
internal static extern SafeUnixHandle Open(string path, uint flag);
/// <summary>
/// Performs an ioctl request to the open handle.
/// </summary>
/// <param name="handle">The handle.</param>
/// <param name="request">The request.</param>
/// <param name="serialRs485">The data structure to read / write.</param>
/// <returns></returns>
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
internal static extern int IoCtl(SafeUnixHandle handle, uint request, ref SerialRS485 serialRs485);
[DllImport("libc", EntryPoint = "open", SetLastError = true)]
internal static extern SafeUnixHandle Open(string path, uint flag);
/// <summary>
/// Closes an open handle.
/// </summary>
/// <param name="handle">The handle.</param>
/// <returns></returns>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("libc", EntryPoint = "close", SetLastError = true)]
internal static extern int Close(IntPtr handle);
[DllImport("libc", EntryPoint = "strerr", SetLastError = true)]
internal static extern int StrError(int error, [Out] StringBuilder buffer, ulong bufferLength);
/// <summary>
/// Converts the given error number (errno) into a readable string.
/// </summary>
/// <param name="errno">The error number (errno).</param>
/// <returns></returns>
[DllImport("libc", EntryPoint = "strerror", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr StrError(int errno);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment