diff --git a/src/SocketCANSharp/CanFrameFast.cs b/src/SocketCANSharp/CanFrameFast.cs
new file mode 100644
index 0000000..d841538
--- /dev/null
+++ b/src/SocketCANSharp/CanFrameFast.cs
@@ -0,0 +1,108 @@
+#region License
+/*
+BSD 3-Clause License
+
+Copyright (c) 2025, Brill Power
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#endregion
+
+#if NET8_0_OR_GREATER
+using System;
+using System.Runtime.InteropServices;
+
+namespace SocketCANSharp
+{
+ ///
+ /// Represents a CAN frame (classical or FD) that exists only on the stack.
+ ///
+ public readonly ref struct CanFrameFast
+ {
+ private readonly Span _buffer;
+ private readonly Span _bufferAsUint;
+
+ ///
+ /// Construct a new CanFrameFast instance with the supplied buffer.
+ ///
+ public CanFrameFast(ref Span buffer)
+ {
+ if (buffer.Length != 16 && buffer.Length != 72)
+ {
+ throw new ArgumentException("Supplied buffer is too small.");
+ }
+ _buffer = buffer;
+ _bufferAsUint = MemoryMarshal.Cast(buffer);
+ }
+
+ ///
+ /// Gets a reference to the 11 or 29-bit CAN ID.
+ ///
+ public ref uint CanId
+ {
+ get { return ref _bufferAsUint[0]; }
+ }
+
+ ///
+ /// Frame length in bytes.
+ ///
+ public ref byte Length
+ {
+ get { return ref _buffer[4]; }
+ }
+
+ ///
+ /// CAN FD specific flags for ESI, BRS, etc.
+ ///
+ public CanFdFlags Flags
+ {
+ get { return (CanFdFlags)_buffer[5]; }
+ set { _buffer[5] = (byte)value; }
+ }
+
+ ///
+ /// CAN frame payload.
+ ///
+ public Span Data => _buffer.Slice(8);
+
+ ///
+ /// Gets the total size of this CAN frame (either 16 or 72 bytes).
+ ///
+ public int Size => _buffer.Length;
+
+ ///
+ /// Indexes into the payload of this CAN frame.
+ ///
+ public ref byte this[int index]
+ {
+ get { return ref _buffer[8 + index]; }
+ }
+
+ internal Span Buffer => _buffer;
+ }
+}
+#endif // NET8_0_OR_GREATER
\ No newline at end of file
diff --git a/src/SocketCANSharp/LibcNativeMethods.cs b/src/SocketCANSharp/LibcNativeMethods.cs
index 6350b98..7798918 100644
--- a/src/SocketCANSharp/LibcNativeMethods.cs
+++ b/src/SocketCANSharp/LibcNativeMethods.cs
@@ -1,5 +1,5 @@
#region License
-/*
+/*
BSD 3-Clause License
Copyright (c) 2021, Derek Will
@@ -28,7 +28,7 @@ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#endregion
@@ -89,7 +89,7 @@ public static class LibcNativeMethods
public static extern int Ioctl(SafeFileDescriptorHandle socketHandle, int request, [In][Out] IfreqMtu ifreq);
///
- /// Used to obtain a timeval struct with the receive timestamp of the last packet passed to the user.
+ /// Used to obtain a timeval struct with the receive timestamp of the last packet passed to the user.
///
/// Socket Handle Wrapper Instance
/// Request Code
@@ -147,7 +147,19 @@ public static class LibcNativeMethods
/// 0 on success, -1 on error
[DllImport("libc", EntryPoint="connect", SetLastError=true)]
public static extern int Connect(SafeFileDescriptorHandle socketHandle, SockAddrCanJ1939 addr, int addrSize);
-
+
+#if NET8_0_OR_GREATER
+ ///
+ /// Write the contents of a byte buffer to the socket.
+ ///
+ /// Socket Handle Wrappper Instance
+ /// Reference to a buffer to write
+ /// Size of the buffer in bytes
+ /// The number of bytes written on success, -1 on error
+ [DllImport("libc", EntryPoint = "write", SetLastError = true)]
+ internal static extern int Write(SafeFileDescriptorHandle socketHandle, ref byte frame, int frameSize);
+#endif // NET8_0_OR_GREATER
+
///
/// Write the CanFrame to the socket.
///
@@ -288,6 +300,18 @@ public static class LibcNativeMethods
[DllImport("libc", EntryPoint="write", SetLastError=true)]
public static extern int Write(SafeFileDescriptorHandle socketHandle, byte[] data, int dataSize);
+#if NET8_0_OR_GREATER
+ ///
+ /// Read into a byte buffer from the socket.
+ ///
+ /// Socket Handle Wrapper Instance
+ /// A reference to a byte buffer to populate
+ /// Size of byte buffer
+ /// The number of bytes read on success, -1 on error
+ [DllImport("libc", EntryPoint = "read", SetLastError = true)]
+ internal static extern int Read(SafeFileDescriptorHandle socketHandle, ref byte frame, int frameSize);
+#endif // NET8_0_OR_GREATER
+
///
/// Read a CanFrame from the socket.
///
@@ -337,7 +361,7 @@ public static class LibcNativeMethods
/// The number of bytes read on success, -1 on error
[DllImport("libc", EntryPoint="read", SetLastError=true)]
public static extern int Read(SafeFileDescriptorHandle socketHandle, [Out] BcmGenericMessage message, int msgSize);
-
+
///
/// Read a BcmGenericMessage from the socket. Variant for 32-bit.
///
@@ -715,7 +739,7 @@ public static class LibcNativeMethods
/// 0 or 1 on success depending on option name and value, -1 on error
[DllImport("libc", EntryPoint="setsockopt", SetLastError=true)]
public static extern int SetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, J1939SocketOptions optionName, ref int optionValue, int optionValueSize);
-
+
///
/// Get the socket option specified by the option name and socket level to the provided option value for the supplied socket.
///
@@ -751,7 +775,7 @@ public static class LibcNativeMethods
/// 0 on success, -1 on error
[DllImport("libc", EntryPoint="getsockopt", SetLastError=true)]
public static extern int GetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, J1939SocketOptions optionName, [In, Out] J1939Filter[] filters, ref int optionValueSize);
-
+
///
/// Set the socket option specified by the option name and socket level to the provided option value for the supplied socket.
///
@@ -775,7 +799,7 @@ public static class LibcNativeMethods
/// 0 on success, -1 on error
[DllImport("libc", EntryPoint = "getsockopt", SetLastError = true)]
public static extern int GetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, int optionName, IntPtr optionValue, ref int optionValueSize);
-
+
///
/// Set the socket option specified by the option name and socket level to the provided option value for the supplied socket.
///
@@ -817,12 +841,12 @@ public static class LibcNativeMethods
///
/// Opens an epoll file descriptor.
///
- /// The size argument is ignored since Linux 2.6.8, but must be greater than zero for backwards compatibility.
+ /// The size argument is ignored since Linux 2.6.8, but must be greater than zero for backwards compatibility.
/// Originally, this argument was intended as a hint to the kernel as to the number of file descriptors that the caller expected to add to the epoll instance.
/// On success, returns a valid file descriptor handle. On failure, returns an invalid file descriptor handle.
[DllImport("libc", EntryPoint="epoll_create", SetLastError=true)]
public static extern SafeFileDescriptorHandle EpollCreate(int size);
-
+
///
/// Control interface used to add, modify, and delete entries from the interest list of an epoll file descriptor.
///
@@ -833,7 +857,7 @@ public static class LibcNativeMethods
/// 0 on success, -1 on error
[DllImport("libc", EntryPoint="epoll_ctl", SetLastError=true)]
public static extern int EpollControl(SafeFileDescriptorHandle epfd, EpollOperation op, SafeFileDescriptorHandle fd, ref EpollEvent evnt);
-
+
///
/// Waits for an I/O event on an epoll file descriptor.
///
@@ -902,7 +926,7 @@ public static class LibcNativeMethods
/// The number of bytes received on success, -1 on error
[DllImport("libc", EntryPoint="recvmsg", SetLastError=true)]
public static extern int RecvMsg(SafeFileDescriptorHandle socketHandle, ref MessageHeader canMessage, MessageFlags flags);
-
+
///
/// Retrieves the index of the network interface corresponding to the specified name.
///
diff --git a/src/SocketCANSharp/Network/RawCanSocket.cs b/src/SocketCANSharp/Network/RawCanSocket.cs
index 2dc3152..d421d2d 100644
--- a/src/SocketCANSharp/Network/RawCanSocket.cs
+++ b/src/SocketCANSharp/Network/RawCanSocket.cs
@@ -1,5 +1,5 @@
#region License
-/*
+/*
BSD 3-Clause License
Copyright (c) 2022, Derek Will
@@ -28,7 +28,7 @@ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#endregion
@@ -57,7 +57,7 @@ public SockAddrCan Address
///
/// CAN Filters to control the reception of CAN frames.
///
- public CanFilter[] CanFilters
+ public CanFilter[] CanFilters
{
get
{
@@ -72,7 +72,7 @@ public CanFilter[] CanFilters
///
/// Error Mask to filter which Error Message Frames are passed to the socket receive queue.
///
- public CanErrorClass ErrorFilters
+ public CanErrorClass ErrorFilters
{
get
{
@@ -87,7 +87,7 @@ public CanErrorClass ErrorFilters
///
/// Local loopback to receive messages sent from other sockets on this CAN node.
///
- public bool LocalLoopback
+ public bool LocalLoopback
{
get
{
@@ -102,7 +102,7 @@ public bool LocalLoopback
///
/// Enables a socket to receive the messages that it sent itself.
///
- public bool ReceiveOwnMessages
+ public bool ReceiveOwnMessages
{
get
{
@@ -117,7 +117,7 @@ public bool ReceiveOwnMessages
///
/// Allows the socket to handle CAN FD frames.
///
- public bool EnableCanFdFrames
+ public bool EnableCanFdFrames
{
get
{
@@ -132,7 +132,7 @@ public bool EnableCanFdFrames
///
/// Allows the socket to handle CAN XL frames.
///
- public bool EnableCanXlFrames
+ public bool EnableCanXlFrames
{
get
{
@@ -146,9 +146,9 @@ public bool EnableCanXlFrames
///
- /// If true, then all CAN filters must match (logical AND) for a CAN frame to be placed into the receive queue. If false, then if any CAN filter matches (logical OR) then a CAN frame will be placed into the receive queue.
+ /// If true, then all CAN filters must match (logical AND) for a CAN frame to be placed into the receive queue. If false, then if any CAN filter matches (logical OR) then a CAN frame will be placed into the receive queue.
///
- public bool AllCanFiltersMustMatch
+ public bool AllCanFiltersMustMatch
{
get
{
@@ -173,7 +173,7 @@ public RawCanSocket()
if (SafeHandle.IsInvalid)
throw new SocketCanException("Failed to create CAN_RAW socket.");
}
-
+
///
/// Assigns the SocketCAN Base Address Structure to the CAN_RAW socket.
///
@@ -203,7 +203,7 @@ public void Bind(SockAddrCan addr)
/// CanNetworkInterface instance is null.
public void Bind(CanNetworkInterface iface)
{
- if (iface == null)
+ if (iface == null)
throw new ArgumentNullException(nameof(iface));
Bind(new SockAddrCan()
@@ -213,6 +213,27 @@ public void Bind(CanNetworkInterface iface)
});
}
+#if NET8_0_OR_GREATER
+ ///
+ /// Writes the supplied CAN Frame to the socket.
+ ///
+ /// CAN Frame to transmit onto the CAN network.
+ /// Number of bytes written to the socket.
+ /// The socket has been closed.
+ /// Writing to the underlying CAN_RAW socket failed.
+ public int Write(ref CanFrameFast canFrame)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ int bytesWritten = LibcNativeMethods.Write(SafeHandle, ref MemoryMarshal.GetReference(canFrame.Buffer), canFrame.Size);
+ if (bytesWritten == -1)
+ throw new SocketCanException("Writing to the underlying CAN_RAW socket failed.");
+
+ return bytesWritten;
+ }
+#endif // NET8_0_OR_GREATER
+
///
/// Writes the supplied Classical CAN Frame to the socket.
///
@@ -270,6 +291,27 @@ public int Write(CanXlFrame canXlFrame)
return bytesWritten;
}
+#if NET8_0_OR_GREATER
+ ///
+ /// Reads a Classical CAN Frame from the socket.
+ ///
+ /// Classical CAN Frame to receive from the CAN network.
+ /// Number of bytes read from the socket.
+ /// The socket has been closed.
+ /// Reading from the underlying CAN_RAW socket failed.
+ public int Read(ref CanFrameFast canFrame)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ int bytesRead = LibcNativeMethods.Read(SafeHandle, ref MemoryMarshal.GetReference(canFrame.Buffer), canFrame.Size);
+ if (bytesRead == -1)
+ throw new SocketCanException("Reading from the underlying CAN_RAW socket failed.");
+
+ return bytesRead;
+ }
+#endif // NET8_0_OR_GREATER
+
///
/// Reads a Classical CAN Frame from the socket.
///
@@ -389,7 +431,7 @@ private void SetRawCanFilters(CanFilter[] canFilterArray)
int len = canFilterArray != null ? Marshal.SizeOf(typeof(CanFilter)) * canFilterArray.Length : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_FILTER, canFilterArray, len);
if (result != 0)
- throw new SocketCanException("Unable to set CAN_RAW_FILTER on CAN_RAW socket.");
+ throw new SocketCanException("Unable to set CAN_RAW_FILTER on CAN_RAW socket.");
}
private CanFilter[] GetRawCanFilters()
@@ -430,14 +472,14 @@ private void SetRawCanErrorFrameFilters(CanErrorClass errorMask)
uint err_mask = (uint)errorMask;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_ERR_FILTER, ref err_mask, Marshal.SizeOf(err_mask));
if (result != 0)
- throw new SocketCanException("Unable to set CAN_RAW_ERR_FILTER on CAN_RAW socket.");
+ throw new SocketCanException("Unable to set CAN_RAW_ERR_FILTER on CAN_RAW socket.");
}
private CanErrorClass GetRawCanErrorFrameFilters()
{
if (_disposed)
throw new ObjectDisposedException(GetType().FullName);
-
+
uint err_mask = 0;
int len = Marshal.SizeOf(err_mask);
int result = LibcNativeMethods.GetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_ERR_FILTER, ref err_mask, ref len);
@@ -455,7 +497,7 @@ private void SetLoopback(bool enable)
int loopback = enable ? 1 : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_LOOPBACK, ref loopback, Marshal.SizeOf(loopback));
-
+
if (result != 0)
throw new SocketCanException("Unable to set CAN_RAW_LOOPBACK on CAN_RAW socket.");
}
@@ -482,7 +524,7 @@ private void SetReceiveOwnMessages(bool enable)
int recv_own_msgs = enable ? 1 : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_RECV_OWN_MSGS, ref recv_own_msgs, Marshal.SizeOf(recv_own_msgs));
-
+
if (result != 0)
throw new SocketCanException("Unable to set CAN_RAW_RECV_OWN_MSGS on CAN_RAW socket.");
}
@@ -509,7 +551,7 @@ private void SetEnableCanFdFrames(bool enable)
int can_fd_enabled = enable ? 1 : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_FD_FRAMES, ref can_fd_enabled, Marshal.SizeOf(can_fd_enabled));
-
+
if (result != 0)
throw new SocketCanException("Unable to set CAN_RAW_FD_FRAMES on CAN_RAW socket.");
}
@@ -536,7 +578,7 @@ private void SetEnableCanXlFrames(bool enable)
int can_xl_enabled = enable ? 1 : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_XL_FRAMES, ref can_xl_enabled, Marshal.SizeOf());
-
+
if (result != 0)
throw new SocketCanException("Unable to set CAN_RAW_XL_FRAMES on CAN_RAW socket.");
}
@@ -563,7 +605,7 @@ private void SetAllCanFiltersMustMatch(bool enable)
int join_filter = enable ? 1 : 0;
int result = LibcNativeMethods.SetSockOpt(SafeHandle, SocketLevel.SOL_CAN_RAW, CanSocketOptions.CAN_RAW_JOIN_FILTERS, ref join_filter, Marshal.SizeOf(join_filter));
-
+
if (result != 0)
throw new SocketCanException("Unable to set CAN_RAW_JOIN_FILTERS on CAN_RAW socket.");
}
@@ -600,12 +642,12 @@ private SockAddrCan GetSockAddr()
private int Read(out T frame, out bool txSuccess, out bool localhost)
{
- int ctrlMsgSize = ControlMessageMacros.CMSG_SPACE(Marshal.SizeOf()) + ControlMessageMacros.CMSG_SPACE(Marshal.SizeOf());
+ int ctrlMsgSize = ControlMessageMacros.CMSG_SPACE(Marshal.SizeOf()) + ControlMessageMacros.CMSG_SPACE(Marshal.SizeOf());
IntPtr addrPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
IntPtr iovecPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
IntPtr framePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
IntPtr ctrlMsgPtr = Marshal.AllocHGlobal(ctrlMsgSize);
-
+
try
{
var iovec = new IoVector() { Base = framePtr, Length = new IntPtr(Marshal.SizeOf()) };
diff --git a/src/SocketCANSharp/SocketCANSharp.csproj b/src/SocketCANSharp/SocketCANSharp.csproj
index d151832..b601a5b 100644
--- a/src/SocketCANSharp/SocketCANSharp.csproj
+++ b/src/SocketCANSharp/SocketCANSharp.csproj
@@ -1,7 +1,7 @@
- netstandard2.0
+ netstandard2.0;net8.0
Library
SocketCANSharp
0.13.0.0
diff --git a/test/SocketCANSharpTest/RawCanSocketTests.cs b/test/SocketCANSharpTest/RawCanSocketTests.cs
index 402630a..3523841 100644
--- a/test/SocketCANSharpTest/RawCanSocketTests.cs
+++ b/test/SocketCANSharpTest/RawCanSocketTests.cs
@@ -1,5 +1,5 @@
#region License
-/*
+/*
BSD 3-Clause License
Copyright (c) 2022, Derek Will
@@ -28,7 +28,7 @@ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#endregion
@@ -39,6 +39,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
namespace SocketCANSharpTest
{
@@ -269,7 +270,7 @@ public void RawCanSocket_ErrorFilters_Success_Test()
{
rawCanSocket.ErrorFilters = CanErrorClass.CAN_ERR_ACK | CanErrorClass.CAN_ERR_BUSERROR;
CanErrorClass errFilters = rawCanSocket.ErrorFilters;
- Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_ACK));
+ Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_ACK));
Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_BUSERROR));
Assert.IsFalse(errFilters.HasFlag(CanErrorClass.CAN_ERR_BUSOFF));
Assert.IsFalse(errFilters.HasFlag(CanErrorClass.CAN_ERR_CRTL));
@@ -319,7 +320,7 @@ public void RawCanSocket_ErrorFilters_Post_Bind_Assignment_Success_Test()
rawCanSocket.Bind(iface);
rawCanSocket.ErrorFilters = CanErrorClass.CAN_ERR_ACK | CanErrorClass.CAN_ERR_BUSERROR;
CanErrorClass errFilters = rawCanSocket.ErrorFilters;
- Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_ACK));
+ Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_ACK));
Assert.IsTrue(errFilters.HasFlag(CanErrorClass.CAN_ERR_BUSERROR));
Assert.IsFalse(errFilters.HasFlag(CanErrorClass.CAN_ERR_BUSOFF));
Assert.IsFalse(errFilters.HasFlag(CanErrorClass.CAN_ERR_CRTL));
@@ -1145,5 +1146,131 @@ public void RawCanSocket_Read_CanXlFrame_TxSuccess_And_Localhost_Success_Test()
Assert.AreEqual(true, localhost2);
}
}
+
+ [Test]
+ public void RawCanSocket_Read_CanFrameFast_Success_Test()
+ {
+ IEnumerable collection = CanNetworkInterface.GetAllInterfaces(true);
+ Assert.IsNotNull(collection);
+ Assert.GreaterOrEqual(collection.Count(), 1);
+
+ var iface = collection.FirstOrDefault(i => i.Name.Equals("vcan0"));
+ Assert.IsNotNull(iface);
+
+ using (var senderSocket = new RawCanSocket())
+ using (var receiverSocket = new RawCanSocket())
+ {
+ senderSocket.Bind(iface);
+ receiverSocket.Bind(iface);
+
+ byte[] data = [ 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef ];
+ Span write = stackalloc byte[16];
+ CanFrameFast writeFrame = new CanFrameFast(ref write);
+ writeFrame.CanId = 0x123;
+ writeFrame.Length = (byte)data.Length;
+ data.CopyTo(writeFrame.Data);
+ int bytesWritten = senderSocket.Write(ref writeFrame);
+ Assert.AreEqual(16, bytesWritten);
+
+ Span read = stackalloc byte[16];
+ CanFrameFast readFrame = new CanFrameFast(ref read);
+ int bytesRead = receiverSocket.Read(ref readFrame);
+ Assert.AreEqual(16, bytesRead);
+ Assert.AreEqual(0x123, readFrame.CanId);
+ Assert.IsTrue(readFrame.Data.ToArray().Take(readFrame.Length).SequenceEqual(new byte[] { 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }));
+ }
+ }
+
+ [Test]
+ public void RawCanSocket_Write_CanFrameFast_Success_Test()
+ {
+ IEnumerable collection = CanNetworkInterface.GetAllInterfaces(true);
+ Assert.IsNotNull(collection);
+ Assert.GreaterOrEqual(collection.Count(), 1);
+
+ var iface = collection.FirstOrDefault(i => i.Name.Equals("vcan0"));
+ Assert.IsNotNull(iface);
+
+ using (var rawCanSocket = new RawCanSocket())
+ {
+ rawCanSocket.Bind(iface);
+
+ byte[] data = [ 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef ];
+ Span write = stackalloc byte[16];
+ CanFrameFast writeFrame = new CanFrameFast(ref write);
+ writeFrame.CanId = 0x123;
+ writeFrame.Length = (byte)data.Length;
+ data.CopyTo(writeFrame.Data);
+ int bytesWritten = rawCanSocket.Write(ref writeFrame);
+ Assert.AreEqual(16, bytesWritten);
+ }
+ }
+
+ [Test]
+ public void RawCanSocket_Write_CanFrameFast_Fd_Success_Test()
+ {
+ IEnumerable collection = CanNetworkInterface.GetAllInterfaces(true);
+ Assert.IsNotNull(collection);
+ Assert.GreaterOrEqual(collection.Count(), 1);
+
+ var iface = collection.FirstOrDefault(i => i.Name.Equals("vcan0"));
+ Assert.IsNotNull(iface);
+
+ using (var rawCanSocket = new RawCanSocket())
+ {
+ Assume.That(iface.MaximumTransmissionUnit, Is.GreaterThanOrEqualTo(SocketCanConstants.CANFD_MTU));
+ rawCanSocket.EnableCanFdFrames = true;
+ rawCanSocket.Bind(iface);
+
+ byte[] data = [ 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef ];
+ Span write = stackalloc byte[72];
+ CanFrameFast writeFrame = new CanFrameFast(ref write);
+ writeFrame.CanId = 0x123;
+ writeFrame.Length = (byte)data.Length;
+ writeFrame.Flags = CanFdFlags.CANFD_BRS;
+ data.CopyTo(writeFrame.Data);
+ int bytesWritten = rawCanSocket.Write(ref writeFrame);
+ Assert.AreEqual(72, bytesWritten);
+ }
+ }
+
+ [Test]
+ public void RawCanSocket_Read_CanFrameFast_Fd_Success_Test()
+ {
+ IEnumerable collection = CanNetworkInterface.GetAllInterfaces(true);
+ Assert.IsNotNull(collection);
+ Assert.GreaterOrEqual(collection.Count(), 1);
+
+ var iface = collection.FirstOrDefault(i => i.Name.Equals("vcan0"));
+ Assert.IsNotNull(iface);
+
+ using (var senderSocket = new RawCanSocket())
+ using (var receiverSocket = new RawCanSocket())
+ {
+ Assume.That(iface.MaximumTransmissionUnit, Is.GreaterThanOrEqualTo(SocketCanConstants.CANFD_MTU));
+ senderSocket.EnableCanFdFrames = true;
+ receiverSocket.EnableCanFdFrames = true;
+ senderSocket.Bind(iface);
+ receiverSocket.Bind(iface);
+
+ byte[] data = [ 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef ];
+ Span write = stackalloc byte[72];
+ CanFrameFast writeFrame = new CanFrameFast(ref write);
+ writeFrame.CanId = 0x123;
+ writeFrame.Length = (byte)data.Length;
+ writeFrame.Flags = CanFdFlags.CANFD_BRS;
+ data.CopyTo(writeFrame.Data);
+ int bytesWritten = senderSocket.Write(ref writeFrame);
+ Assert.AreEqual(72, bytesWritten);
+
+ Span read = stackalloc byte[72];
+ CanFrameFast readFrame = new CanFrameFast(ref read);
+ int bytesRead = receiverSocket.Read(ref readFrame);
+ Assert.AreEqual(72, bytesRead);
+ Assert.AreEqual(0x123, readFrame.CanId);
+ Assert.IsTrue(readFrame.Data.ToArray().Take(readFrame.Length).SequenceEqual(new byte[] { 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }));
+ Assert.IsTrue(readFrame.Flags.HasFlag(CanFdFlags.CANFD_BRS)); // In Kernel 6.1 and higher - CANFD_FDF flag will also be set. Changing to just check for BRS to be backwards compatible.
+ }
+ }
}
}