-
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding memory mapped file IPC mechanism * IPC test success * Added driver context for IPC * Added IPC clean-up * Update Driver.c * Code clean-up * Update IPC.c * Update dshidmini.sln.DotSettings * Added sync mutext object * Update IPC.h * Added worker thread and sync * Update IPC.c * Update IPC.c * Update IPC.c * Update IPC.c * Added IPC types * Update IPC.c * More comments Enabled verbose tracing * Added dispatcher function * Update IPC.c * Added ipctest app * Namespace fixes * More IPC fun * More IPC implementation * Fixes * Ping implementation works * Update DsHidMiniInterop.cs * Project structure clean-up * Refined boundary checks * Bugfixes * Update DsHidMiniInterop.cs * Added API docs * Shortened timeout * Update DsHidMiniInterop.cs * Added multi-threading protection * Update DsHidMiniInterop.cs * Added device slots to context * Added slots assignment * Implemented per device message dispatcher * Update DsHidMiniInterop.cs * Finished pairing command skeleton * Logging improvements * Source clean-up * Update IPC.Device.c * Added API docs * Implemented DSHM_IPC_MSG_CMD_DEVICE_PAIR_TO via IPC * New IPC based pairing works * Added docs * Minor fixes * Added DsHidMiniInteropInvalidDeviceIndexException Fixed exposure * Update Program.cs * Update DsHidMiniInterop.cs * Update IPC.c * Update IPC.c * Update IPC.c * Update IPC.c * Code simplification * Added DSHM_IPC_MSG_CMD_DEVICE_SET_PLAYER_INDEX * Added SET_PLAYER_INDEX types * Update appveyor.yml * Fixed ARM64 build warnings * Update appveyor.yml * Update appveyor.yml * Update appveyor.yml * Update appveyor.yml * Moved models to class library Consolidated copyright info and common properties * Removed obsolete build logic * Update Directory.Build.props * Adding support for multiple shared regions * Synced driver and SDK code * Improved locking logic * Fixed issue with None address in SetHostAddress * Update DsHidMiniInterop.cs * Renamed mutex * Bugfix * Added copying raw input report to shared memory region * Added GetRawInputReport * Fixed offsets * Update DsHidMiniInterop.cs * Adding input report event * Input report reading performance fixes * Create README.md * Implemented HID IPC clean-up * Added auto-generated docs * Updated summary * Split class * Update nefarius.dshidmini.ipc.dshidminiinterop.md * Update DsHidMiniInterop.Commands.cs * Update DsHidMiniInterop.cs * Updated docs * Update DsHidMiniInterop.cs * Added more missing API docs * Update RawInputReport.cs * Update README.md * Updated report API * Update Program.cs * Updated API docs * Update DsHidMiniInterop.Commands.cs * Update Directory.Build.props * Update Directory.Build.props * Update Directory.Build.props * Update Directory.Build.props * Update Directory.Build.props * Removed unused exception * Update Exceptions.cs * Copied some shared types * Update DsHidMiniDriver.cs * Update DsHidMiniDriver.cs * Updated API docs * Reworking connection state machine * Update DsHidMiniInterop.cs * Minor version bump * IPC bugfixes * Improving pointer shenanigans * Removed structure packing * Update README.md * Update dshidmini_tvp.twx * Introduced native WinAPIs * Alligned section sizes with page size * Synced C# with C side * Fixed shared memory alignment BS for good * Cleaned up use of pointers * Update DsHidMiniInterop.Commands.cs * Overhauled exception handling * More exceptions cleanup * Update Program.cs * Name consolidation * Added WDF_USB_CONTROL_SETUP_PACKET * Bumped minor version * Added more API docs * Fixed background image problem * Updated dependencies for build project
- Loading branch information
Showing
54 changed files
with
4,331 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<Project> | ||
|
||
<PropertyGroup> | ||
<!-- Copyright information --> | ||
<Authors>Kanuan, Benjamin Höglinger-Stelzer</Authors> | ||
<Copyright>Copyright © Nefarius Software Solutions e.U. 2022-2024</Copyright> | ||
|
||
<!-- Common solution settings --> | ||
<!--<OutputPath>$(SolutionDir)bin\</OutputPath>--> | ||
<LangVersion>latest</LangVersion> | ||
|
||
<!-- Suppress warnings if CI environment variable is set to true --> | ||
<NoWarn Condition="'$(CI)' == 'True'">$(NoWarn),0219,1587,1591,8600,8601,8602,8603,8604,8618,8619,8622,8629,8765,8767</NoWarn> | ||
|
||
<!-- Docs and symbols generation --> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<IncludeSymbols>true</IncludeSymbols> | ||
<SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||
<PublishRepositoryUrl>true</PublishRepositoryUrl> | ||
</PropertyGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
305 changes: 305 additions & 0 deletions
305
SDK/Nefarius.DsHidMini.IPC/DsHidMiniInterop.Commands.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
using System.ComponentModel; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Net.NetworkInformation; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
using Nefarius.DsHidMini.IPC.Exceptions; | ||
using Nefarius.DsHidMini.IPC.Models; | ||
using Nefarius.DsHidMini.IPC.Models.Public; | ||
|
||
namespace Nefarius.DsHidMini.IPC; | ||
|
||
public partial class DsHidMiniInterop | ||
{ | ||
/// <summary> | ||
/// Attempts to read the <see cref="DS3_RAW_INPUT_REPORT" /> from a given device instance. | ||
/// </summary> | ||
/// <remarks> | ||
/// If <paramref name="timeout" /> is null, this method returns the last known input report copy immediately. If | ||
/// you use this call in a busy loop, you should set a timeout so this call becomes event-based, meaning the call will | ||
/// only return when the driver signaled that new data is available, otherwise you will just burn through CPU for no | ||
/// good reason. A new input report is typically available each average 5 milliseconds, depending on the connection | ||
/// (wired or wireless) so a timeout of 20 milliseconds should be a good recommendation. | ||
/// </remarks> | ||
/// <param name="deviceIndex">The one-based device index.</param> | ||
/// <param name="report">The <see cref="DS3_RAW_INPUT_REPORT" /> to populate.</param> | ||
/// <param name="timeout">Optional timeout to wait for a report update to arrive. Default invocation returns immediately.</param> | ||
/// <exception cref="DsHidMiniInteropAccessDeniedException"> | ||
/// Driver process interaction failed due to missing permissions; | ||
/// this operation requires elevated privileges. | ||
/// </exception> | ||
/// <exception cref="DsHidMiniInteropUnexpectedReplyException">The driver returned unexpected or malformed data.</exception> | ||
/// <exception cref="Win32Exception">Handle duplication failed.</exception> | ||
/// <exception cref="DsHidMiniInteropReplyTimeoutException">The driver didn't respond within an expected period.</exception> | ||
/// <exception cref="DsHidMiniInteropConcurrencyException">A different thread is currently performing a data exchange.</exception> | ||
/// <exception cref="DsHidMiniInteropUnavailableException"> | ||
/// No driver instance is available. Make sure that at least one | ||
/// device is connected and that the driver is installed and working properly. Call <see cref="IsAvailable" /> prior to | ||
/// avoid this exception. | ||
/// </exception> | ||
/// <returns> | ||
/// TRUE if <paramref name="report" /> got filled in or FALSE if the given <paramref name="deviceIndex" /> is not | ||
/// occupied. | ||
/// </returns> | ||
[SuppressMessage("ReSharper", "UnusedMember.Global")] | ||
public unsafe bool GetRawInputReport(int deviceIndex, ref DS3_RAW_INPUT_REPORT report, TimeSpan? timeout = null) | ||
{ | ||
if (_hidView is null) | ||
{ | ||
throw new DsHidMiniInteropUnavailableException(); | ||
} | ||
|
||
ValidateDeviceIndex(deviceIndex); | ||
|
||
ref IPC_HID_INPUT_REPORT_MESSAGE message = ref Unsafe.AsRef<IPC_HID_INPUT_REPORT_MESSAGE>(_hidView); | ||
|
||
if (timeout.HasValue) | ||
{ | ||
_inputReportEvent ??= GetHidReportWaitHandle(deviceIndex); | ||
|
||
_inputReportEvent.WaitOne(timeout.Value); | ||
} | ||
|
||
// | ||
// Device is/got disconnected | ||
// | ||
if (message.SlotIndex == 0) | ||
{ | ||
return false; | ||
} | ||
|
||
// | ||
// Index mismatch is not supposed to happen | ||
// | ||
if (message.SlotIndex != deviceIndex) | ||
{ | ||
throw new DsHidMiniInteropUnexpectedReplyException(); | ||
} | ||
|
||
report = message.InputReport; | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Send a PING to the driver and awaits the reply. | ||
/// </summary> | ||
/// <exception cref="DsHidMiniInteropUnavailableException"> | ||
/// Driver IPC unavailable, make sure that at least one compatible | ||
/// controller is connected and operational. | ||
/// </exception> | ||
/// <exception cref="DsHidMiniInteropReplyTimeoutException">The driver didn't respond within an expected period.</exception> | ||
/// <exception cref="DsHidMiniInteropUnexpectedReplyException">The driver returned unexpected or malformed data.</exception> | ||
[SuppressMessage("ReSharper", "UnusedMember.Global")] | ||
public unsafe void SendPing() | ||
{ | ||
if (_commandMutex is null || _cmdView is null) | ||
{ | ||
throw new DsHidMiniInteropUnavailableException(); | ||
} | ||
|
||
AcquireCommandLock(); | ||
|
||
try | ||
{ | ||
ref DSHM_IPC_MSG_HEADER message = ref Unsafe.AsRef<DSHM_IPC_MSG_HEADER>(_cmdView); | ||
|
||
message.Type = DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_RESPONSE; | ||
message.Target = DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_DRIVER; | ||
message.Command.Driver = DSHM_IPC_MSG_CMD_DRIVER.DSHM_IPC_MSG_CMD_DRIVER_PING; | ||
message.TargetIndex = 0; | ||
message.Size = (uint)Marshal.SizeOf<DSHM_IPC_MSG_HEADER>(); | ||
|
||
if (!SendAndWait()) | ||
{ | ||
throw new DsHidMiniInteropReplyTimeoutException(); | ||
} | ||
|
||
ref DSHM_IPC_MSG_HEADER reply = ref Unsafe.AsRef<DSHM_IPC_MSG_HEADER>(_cmdView); | ||
|
||
// | ||
// Plausibility check | ||
// | ||
if (reply is | ||
{ | ||
Type: DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_REPLY, | ||
Target: DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_CLIENT, | ||
Command.Driver: DSHM_IPC_MSG_CMD_DRIVER.DSHM_IPC_MSG_CMD_DRIVER_PING, TargetIndex: 0 | ||
} | ||
&& reply.Size == Marshal.SizeOf<DSHM_IPC_MSG_HEADER>()) | ||
{ | ||
return; | ||
} | ||
|
||
throw new DsHidMiniInteropUnexpectedReplyException(ref reply); | ||
} | ||
finally | ||
{ | ||
_commandMutex.ReleaseMutex(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Writes a new host address to the given device. | ||
/// </summary> | ||
/// <exception cref="DsHidMiniInteropUnavailableException"> | ||
/// Driver IPC unavailable, make sure that at least one compatible | ||
/// controller is connected and operational. | ||
/// </exception> | ||
/// <returns>A <see cref="SetHostResult" /> containing success (or error) details.</returns> | ||
/// <remarks>This is synonymous with "pairing" to a new Bluetooth host.</remarks> | ||
/// <param name="deviceIndex">The one-based device index.</param> | ||
/// <param name="hostAddress">The new host address.</param> | ||
/// <exception cref="DsHidMiniInteropInvalidDeviceIndexException"> | ||
/// The <paramref name="deviceIndex" /> was outside a valid | ||
/// range. | ||
/// </exception> | ||
/// <exception cref="DsHidMiniInteropConcurrencyException">A different thread is currently performing a data exchange.</exception> | ||
/// <exception cref="DsHidMiniInteropReplyTimeoutException">The driver didn't respond within an expected period.</exception> | ||
/// <exception cref="DsHidMiniInteropUnexpectedReplyException">The driver returned unexpected or malformed data.</exception> | ||
[SuppressMessage("ReSharper", "UnusedMember.Global")] | ||
public unsafe SetHostResult SetHostAddress(int deviceIndex, PhysicalAddress hostAddress) | ||
{ | ||
if (_commandMutex is null || _cmdView is null) | ||
{ | ||
throw new DsHidMiniInteropUnavailableException(); | ||
} | ||
|
||
ValidateDeviceIndex(deviceIndex); | ||
|
||
AcquireCommandLock(); | ||
|
||
try | ||
{ | ||
ref DSHM_IPC_MSG_PAIR_TO_REQUEST request = ref Unsafe.AsRef<DSHM_IPC_MSG_PAIR_TO_REQUEST>(_cmdView); | ||
|
||
request.Header.Type = DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_RESPONSE; | ||
request.Header.Target = DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_DEVICE; | ||
request.Header.Command.Device = DSHM_IPC_MSG_CMD_DEVICE.DSHM_IPC_MSG_CMD_DEVICE_PAIR_TO; | ||
request.Header.TargetIndex = (uint)deviceIndex; | ||
request.Header.Size = (uint)Marshal.SizeOf<DSHM_IPC_MSG_PAIR_TO_REQUEST>(); | ||
|
||
fixed (byte* source = hostAddress.GetAddressBytes()) | ||
fixed (byte* address = request.Address) | ||
{ | ||
if (source is not null) | ||
{ | ||
Buffer.MemoryCopy(source, address, 6, 6); | ||
} | ||
else | ||
{ | ||
// there might be previous values there we need to zero out | ||
Unsafe.InitBlockUnaligned(address, 0, 6); | ||
} | ||
} | ||
|
||
if (!SendAndWait()) | ||
{ | ||
throw new DsHidMiniInteropReplyTimeoutException(); | ||
} | ||
|
||
ref DSHM_IPC_MSG_PAIR_TO_REPLY reply = ref Unsafe.AsRef<DSHM_IPC_MSG_PAIR_TO_REPLY>(_cmdView); | ||
|
||
// | ||
// Plausibility check | ||
// | ||
if (reply.Header.Type == DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_REPLY | ||
&& reply.Header is | ||
{ | ||
Target: DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_CLIENT, | ||
Command.Device: DSHM_IPC_MSG_CMD_DEVICE.DSHM_IPC_MSG_CMD_DEVICE_PAIR_TO | ||
} | ||
&& reply.Header.TargetIndex == deviceIndex | ||
&& reply.Header.Size == Marshal.SizeOf<DSHM_IPC_MSG_PAIR_TO_REPLY>()) | ||
{ | ||
return new SetHostResult { WriteStatus = reply.WriteStatus, ReadStatus = reply.ReadStatus }; | ||
} | ||
|
||
throw new DsHidMiniInteropUnexpectedReplyException(ref reply.Header); | ||
} | ||
finally | ||
{ | ||
_commandMutex.ReleaseMutex(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Overwrites the player slot indicator (player LEDs) of the given device. | ||
/// </summary> | ||
/// <param name="deviceIndex">The one-based device index.</param> | ||
/// <param name="playerIndex">The player index to set to. Valid values include 1 to 7.</param> | ||
/// <exception cref="DsHidMiniInteropUnavailableException"> | ||
/// Driver IPC unavailable, make sure that at least one compatible | ||
/// controller is connected and operational. | ||
/// </exception> | ||
/// <returns></returns> | ||
/// <exception cref="ArgumentOutOfRangeException"> | ||
/// The <paramref name="deviceIndex" /> or <paramref name="playerIndex" /> | ||
/// were out of range. | ||
/// </exception> | ||
/// <exception cref="DsHidMiniInteropConcurrencyException">A different thread is currently performing a data exchange.</exception> | ||
/// <exception cref="DsHidMiniInteropReplyTimeoutException">The driver didn't respond within an expected period.</exception> | ||
/// <exception cref="DsHidMiniInteropUnexpectedReplyException">The driver returned unexpected or malformed data.</exception> | ||
[SuppressMessage("ReSharper", "UnusedMember.Global")] | ||
public unsafe UInt32 SetPlayerIndex(int deviceIndex, byte playerIndex) | ||
{ | ||
if (_commandMutex is null || _cmdView is null) | ||
{ | ||
throw new DsHidMiniInteropUnavailableException(); | ||
} | ||
|
||
ValidateDeviceIndex(deviceIndex); | ||
|
||
if (playerIndex is < 1 or > 7) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(playerIndex), | ||
"Player index must be between (including) 1 and 7."); | ||
} | ||
|
||
AcquireCommandLock(); | ||
|
||
try | ||
{ | ||
ref DSHM_IPC_MSG_SET_PLAYER_INDEX_REQUEST request = | ||
ref Unsafe.AsRef<DSHM_IPC_MSG_SET_PLAYER_INDEX_REQUEST>(_cmdView); | ||
|
||
request.Header.Type = DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_RESPONSE; | ||
request.Header.Target = DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_DEVICE; | ||
request.Header.Command.Device = DSHM_IPC_MSG_CMD_DEVICE.DSHM_IPC_MSG_CMD_DEVICE_SET_PLAYER_INDEX; | ||
request.Header.TargetIndex = (uint)deviceIndex; | ||
request.Header.Size = (uint)Marshal.SizeOf<DSHM_IPC_MSG_SET_PLAYER_INDEX_REQUEST>(); | ||
|
||
request.PlayerIndex = playerIndex; | ||
|
||
if (!SendAndWait()) | ||
{ | ||
throw new DsHidMiniInteropReplyTimeoutException(); | ||
} | ||
|
||
ref DSHM_IPC_MSG_SET_PLAYER_INDEX_REPLY reply = | ||
ref Unsafe.AsRef<DSHM_IPC_MSG_SET_PLAYER_INDEX_REPLY>(_cmdView); | ||
|
||
// | ||
// Plausibility check | ||
// | ||
if (reply.Header is | ||
{ | ||
Type: DSHM_IPC_MSG_TYPE.DSHM_IPC_MSG_TYPE_REQUEST_REPLY, | ||
Target: DSHM_IPC_MSG_TARGET.DSHM_IPC_MSG_TARGET_CLIENT, | ||
Command.Device: DSHM_IPC_MSG_CMD_DEVICE.DSHM_IPC_MSG_CMD_DEVICE_SET_PLAYER_INDEX | ||
} | ||
&& reply.Header.TargetIndex == deviceIndex | ||
&& reply.Header.Size == Marshal.SizeOf<DSHM_IPC_MSG_SET_PLAYER_INDEX_REPLY>()) | ||
{ | ||
return reply.NtStatus; | ||
} | ||
|
||
throw new DsHidMiniInteropUnexpectedReplyException(ref reply.Header); | ||
} | ||
finally | ||
{ | ||
_commandMutex.ReleaseMutex(); | ||
} | ||
} | ||
} |
Oops, something went wrong.