Skip to content

Commit

Permalink
Initial CAN XL VCID Support - Closes #81
Browse files Browse the repository at this point in the history
  • Loading branch information
derek-will committed Sep 8, 2024
1 parent a8a9c5f commit f50348e
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 29 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The classes such as `CanNetworkInterface`, `RawCanSocket`, and `IsoTpCanSocket`
| Classical CAN | 2.6.25 | 0.1 |
| CAN FD | 3.6 | 0.1 |
| CAN XL | 6.2 | Planned Support ([#50](//github.com/derek-will/SocketCANSharp/issues/50)) |
| CAN XL VCID | 6.9 | Planned Support ([#81](//github.com/derek-will/SocketCANSharp/issues/81)) |
| epoll API | 2.5.44 | 0.4[^1], 0.8[^4] |
| Capabilities API | 2.2 | 0.11 |

Expand Down
62 changes: 62 additions & 0 deletions src/SocketCANSharp/CanRawVcidOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#region License
/*
BSD 3-Clause License
Copyright (c) 2024, Derek Will
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

using System.Runtime.InteropServices;

namespace SocketCANSharp
{
/// <summary>
/// CAN XL Virtual CAN network ID Handling Configuration
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public class CanRawVcidOptions
{
/// <summary>
/// Flags for VCID Behavior Options.
/// </summary>
public CanXlVcidHandlingOption Flags { get; set; }
/// <summary>
/// VCID Value to set into the Priority composite field of CAN XL Frame structs when CAN_RAW_XL_VCID_TX_SET is set in Flags.
/// </summary>
public byte TxVCID { get; set; }
/// <summary>
/// VCID Value to compare after applying mask on received CAN XL Frames for filtering purposes when CAN_RAW_XL_VCID_RX_FILTER is set in Flags.
/// </summary>
public byte RxVCID { get; set; }
/// <summary>
/// VCID Mask to apply on received CAN XL Frames for filtering purposes when CAN_RAW_XL_VCID_RX_FILTER is set in Flags.
/// </summary>
public byte RxVCIDMask { get; set; }
}
}
66 changes: 43 additions & 23 deletions src/SocketCANSharp/CanXlFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,12 @@ namespace SocketCANSharp
[StructLayout(LayoutKind.Sequential)]
public struct CanXlFrame
{
private uint _priority;

/// <summary>
/// 11-bit arbitration priority field.
/// Priority property contains multiple elements:
/// Bits 0-10: 11-bit Priority ID for bus arbitration purposes on the CAN XL network.
/// Bits 15-23: 8-bit Virtual CAN network ID which allows running up to 256 logical networks on a single physical CAN XL network.
/// </summary>
public uint Priority
{
get
{
return _priority;
}
set
{
if (value > 0x7ff)
throw new ArgumentException("Priority field cannot exceed 11 bits.", nameof(value));

_priority = value;
}
}
public uint Priority { get; set; }
/// <summary>
/// CAN XL specific flags for SEC, XL Format.
/// </summary>
Expand All @@ -81,31 +68,28 @@ public uint Priority
/// </summary>
public uint AcceptanceField { get; set; }
/// <summary>
/// CAN FD Frame payload.
/// CAN XL Frame payload.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst=SocketCanConstants.CANXL_MAX_DLEN)]
public byte[] Data;

/// <summary>
/// Initializes a new instance of the CanXlFrame structure using the supplied priority, SDT, acceptance field, flags and data.
/// </summary>
/// <param name="priority">11-bit arbitration priority field.</param>
/// <param name="priority">Priority property containing Priority ID and VCID.</param>
/// <param name="sdt">SDU (Service Data Unit) Type.</param>
/// <param name="acceptanceField">Acceptance field containing address information.</param>
/// <param name="data">Payload data.</param>
/// <param name="flags">CAN XL specific flags.</param>
public CanXlFrame(uint priority, CanXlSduType sdt, uint acceptanceField, byte[] data, CanXlFlags flags)
{
if (priority > 0x7ff)
throw new ArgumentException("Priority field cannot exceed 11 bits.", nameof(priority));

if (data == null)
throw new ArgumentNullException(nameof(data));

if (data.Length > SocketCanConstants.CANXL_MAX_DLEN)
throw new ArgumentOutOfRangeException(nameof(data), $"Data must be {SocketCanConstants.CANXL_MAX_DLEN} bytes or less");

_priority = priority;
Priority = priority;
SduType = sdt;
AcceptanceField = acceptanceField;
Length = (ushort) data?.Length;
Expand All @@ -118,6 +102,42 @@ public CanXlFrame(uint priority, CanXlSduType sdt, uint acceptanceField, byte[]
}
}

/// <summary>
/// Shortcut to set the Priority ID element on the Priority property.
/// </summary>
/// <param name="priorityId">11-bit Priority ID</param>
public void SetPriorityId(ushort priorityId)
{
Priority = SocketCanUtils.SetCanXlPriorityId(Priority, priorityId);
}

/// <summary>
/// Shortcut to get the Priority ID element from the Priority property.
/// </summary>
/// <returns>11-bit Priority ID</returns>
public ushort GetPriorityId()
{
return SocketCanUtils.GetCanXlPriorityId(Priority);
}

/// <summary>
/// Shortcut to set the VCID element on the Priority property.
/// </summary>
/// <param name="vcid">8-bit Virtual CAN network ID</param>
public void SetVCID(byte vcid)
{
Priority = SocketCanUtils.SetCanXlVCID(Priority, vcid);
}

/// <summary>
/// Shortcut to get the VCID element from the Priority property.
/// </summary>
/// <returns>8-bit Virtual CAN network ID</returns>
public byte GetVCID()
{
return SocketCanUtils.GetCanXlVCID(Priority);
}

/// <summary>
/// Returns a string that represents the current CanXlFrame object.
/// </summary>
Expand Down
25 changes: 25 additions & 0 deletions src/SocketCANSharp/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ public enum CanSocketOptions
/// Allow for CAN XL frames (off by default)
/// </summary>
CAN_RAW_XL_FRAMES = 7,
/// <summary>
/// CAN XL Virtual CAN network ID (VCID) Configuration Options
/// </summary>
CAN_RAW_XL_VCID_OPTS = 8,
}

/// <summary>
Expand Down Expand Up @@ -854,6 +858,27 @@ public enum CanXlSduType : byte
/// Mapped Ethernet Frame Tunneling with a truncated destination MAC address in the Acceptance Field.
/// </summary>
MappedEthernetFrameTunneling = 0x05,
}

/// <summary>
/// Option flags for CAN XL Virtual CAN network ID (VCID) handling.
/// </summary>
[Flags]
public enum CanXlVcidHandlingOption : byte
{
/// <summary>
/// Set VCID in the transmitted CAN XL Frame in kernel space.
/// </summary>
CAN_RAW_XL_VCID_TX_SET = 0x01,
/// <summary>
/// Accept VCID value set from user space and do not clear it.
/// Note: If CAN_RAW_XL_VCID_TX_SET is also enabled, then the associated VCID value for that option will override any value set from user space leveraging this option.
/// </summary>
CAN_RAW_XL_VCID_TX_PASS = 0x02,
/// <summary>
/// Apply VCID receive filter to received CAN XL Frames before passing them to user space.
/// Note: If this option is not set, then only untagged CAN XL Frames (VCID = 0x00) are passed to user space.
/// </summary>
CAN_RAW_XL_VCID_RX_FILTER = 0x04,
}
}
24 changes: 24 additions & 0 deletions src/SocketCANSharp/LibcNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,30 @@ public static class LibcNativeMethods
/// <returns>0 on success, -1 on error</returns>
[DllImport("libc", EntryPoint = "getsockopt", SetLastError = true)]
public static extern int GetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, int optionName, IntPtr optionValue, ref int optionValueSize);

/// <summary>
/// Set the socket option specified by the option name and socket level to the provided option value for the supplied socket.
/// </summary>
/// <param name="socketHandle">CAN_RAW socket handle</param>
/// <param name="socketLevel">SOL_CAN_RAW</param>
/// <param name="optionName">SOL_CAN_RAW socket option</param>
/// <param name="optionValue">VCID Options object</param>
/// <param name="optionValueSize">Size of VCID Options object in bytes</param>
/// <returns>0 on success, -1 on error</returns>
[DllImport("libc", EntryPoint="setsockopt", SetLastError=true)]
public static extern int SetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, CanSocketOptions optionName, CanRawVcidOptions optionValue, int optionValueSize);

/// <summary>
/// Get the socket option specified by the option name and socket level to the provided option value for the supplied socket.
/// </summary>
/// <param name="socketHandle">CAN_RAW Socket handle</param>
/// <param name="socketLevel">SOL_CAN_RAW</param>
/// <param name="optionName">SOL_CAN_RAW socket option</param>
/// <param name="optionValue">VCID Options object</param>
/// <param name="optionValueSize">Size of VCID Options object in bytes</param>
/// <returns>0 on success, -1 on error</returns>
[DllImport("libc", EntryPoint="getsockopt", SetLastError=true)]
public static extern int GetSockOpt(SafeFileDescriptorHandle socketHandle, SocketLevel socketLevel, CanSocketOptions optionName, CanRawVcidOptions optionValue, ref int optionValueSize);

/// <summary>
/// Returns a pointer to an array of IfNameIndex objects. Each IfNameIndex object includes information about one of the network interfaces on the local system.
Expand Down
12 changes: 10 additions & 2 deletions src/SocketCANSharp/SocketCanConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,14 @@ public static class SocketCanConstants
/// </summary>
public const uint CAN_ERR_MASK = 0x1FFFFFFF;
/// <summary>
/// Valid bits in CAN XL Priority field.
/// Mask used to retrieve the 11-bit Priority ID embedded in the priority field of the CAN XL Frame struct.
/// </summary>
public const uint CANXL_PRIO_MASK = 0x000007FF;
/// <summary>
/// Mask used to retrieve the 8-bit VCID embedded in the priority field of the CAN XL Frame struct.
/// </summary>
public const uint CANXL_VCID_MASK = 0x00FF0000;
/// <summary>
/// Special flag to be set in the CAN ID of a CAN Filter to invert the CAN Filter.
/// </summary>
public const uint CAN_INV_FILTER = 0x20000000;
Expand Down Expand Up @@ -147,5 +151,9 @@ public static class SocketCanConstants
/// Interface Name Buffer Size.
/// </summary>
public const int IF_NAMESIZE = 16;
/// <summary>
/// Bit Offset of the VCID in the priority field of the CAN XL Frame struct
/// </summary>
public const int CANXL_VCID_OFFSET = 16;
}
}
}
50 changes: 50 additions & 0 deletions src/SocketCANSharp/SocketCanUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,55 @@ public static uint CreateCanIdWithFormatFlag(uint rawCanId)
else
return rawCanId;
}

/// <summary>
/// Helper method to set the Priority ID sub-element on the provided Priority composite element.
/// </summary>
/// <param name="priority">Priority composite element</param>
/// <param name="priorityId">11-bit Priority ID</param>
/// <returns>Priority composite element with the 11-bit Priority ID set to the provided value.</returns>
/// <exception cref="ArgumentException">Priority ID cannot exceed 11 bits.</exception>
public static uint SetCanXlPriorityId(uint priority, ushort priorityId)
{
if (priorityId > 0x7ff)
throw new ArgumentException("Priority ID cannot exceed 11 bits.", nameof(priorityId));

priority &= SocketCanConstants.CANXL_VCID_MASK;
priority |= priorityId;
return priority;
}

/// <summary>
/// Helper method to get the Priority ID sub-element from the provided Priority composite element.
/// </summary>
/// <param name="priority">Priority composite element</param>
/// <returns>11-bit Priority ID from the Priority composite element.</returns>
public static ushort GetCanXlPriorityId(uint priority)
{
return (ushort)(priority & SocketCanConstants.CANXL_PRIO_MASK);
}

/// <summary>
/// Helper method to set the VCID sub-element on the provided Priority composite element.
/// </summary>
/// <param name="priority">Priority composite element</param>
/// <param name="vcid">8-bit Virtual CAN network ID</param>
/// <returns>Priority composite element with the 8-bit Virtual CAN network ID set to the provided value.</returns>
public static uint SetCanXlVCID(uint priority, byte vcid)
{
priority &= SocketCanConstants.CANXL_PRIO_MASK;
priority |= (uint)(vcid << SocketCanConstants.CANXL_VCID_OFFSET);
return priority;
}

/// <summary>
/// Helper method to get the VCID sub-element from the provided Priority composite element.
/// </summary>
/// <param name="priority">Priority composite element</param>
/// <returns>8-bit Virtual CAN network ID from the Priority composite element.</returns>
public static byte GetCanXlVCID(uint priority)
{
return (byte)((priority & SocketCanConstants.CANXL_VCID_MASK) >> SocketCanConstants.CANXL_VCID_OFFSET);
}
}
}
10 changes: 6 additions & 4 deletions test/SocketCANSharpTest/CanXlFrameWriteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,18 @@ public void CanXlFrameWrite_SimpleExtendedContent_Success_Test()
}

[Test]
public void CanXlFrameWrite_InvalidAddress_Ctor_Failure_Test()
public void CanXlFrameWrite_InvalidAddress_Ctor_Pass_Test()
{
Assert.Throws<ArgumentException>(() => new CanXlFrame(0x800, CanXlSduType.ClassicalAndFdFrameTunneling, 0x321, new byte[] { 0x33, 0x22, 0x11 }, CanXlFlags.CANXL_XLF));
var frame = new CanXlFrame(0x800, CanXlSduType.ClassicalAndFdFrameTunneling, 0x321, new byte[] { 0x33, 0x22, 0x11 }, CanXlFlags.CANXL_XLF);
Assert.AreEqual(0x800, frame.Priority);
}

[Test]
public void CanXlFrameWrite_InvalidAddress_Property_Failure_Test()
public void CanXlFrameWrite_InvalidAddress_Property_Pass_Test()
{
var canXlFrame = new CanXlFrame(0x654, CanXlSduType.ClassicalAndFdFrameTunneling, 0x321, new byte[] { 0x33, 0x22, 0x11 }, CanXlFlags.CANXL_XLF);
Assert.Throws<ArgumentException>(() => canXlFrame.Priority = 0x800);
canXlFrame.Priority = 0x800;
Assert.AreEqual(0x800, canXlFrame.Priority);
}

[Test]
Expand Down
Loading

0 comments on commit f50348e

Please sign in to comment.