mirror of
https://github.com/nefarius/ViGEmBus.git
synced 2025-08-10 00:52:17 +00:00
Merge branch 'master' of github.com:ViGEm/ViGEmBus
This commit is contained in:
157
ViGEmBus.sln
Normal file
157
ViGEmBus.sln
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2024
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ViGEmBus", "sys\ViGEmBus.vcxproj", "{040101B0-EE5C-4EF1-99EE-9F81C795C001}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ViGEmClient", "lib\ViGEmClient.vcxproj", "{7DB06674-1F4F-464B-8E1C-172E9587F9DC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug (dynamic)|ARM = Debug (dynamic)|ARM
|
||||
Debug (dynamic)|ARM64 = Debug (dynamic)|ARM64
|
||||
Debug (dynamic)|x64 = Debug (dynamic)|x64
|
||||
Debug (dynamic)|x86 = Debug (dynamic)|x86
|
||||
Debug (static)|ARM = Debug (static)|ARM
|
||||
Debug (static)|ARM64 = Debug (static)|ARM64
|
||||
Debug (static)|x64 = Debug (static)|x64
|
||||
Debug (static)|x86 = Debug (static)|x86
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release (dynamic)|ARM = Release (dynamic)|ARM
|
||||
Release (dynamic)|ARM64 = Release (dynamic)|ARM64
|
||||
Release (dynamic)|x64 = Release (dynamic)|x64
|
||||
Release (dynamic)|x86 = Release (dynamic)|x86
|
||||
Release (static)|ARM = Release (static)|ARM
|
||||
Release (static)|ARM64 = Release (static)|ARM64
|
||||
Release (static)|x64 = Release (static)|x64
|
||||
Release (static)|x86 = Release (static)|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM.ActiveCfg = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM.Build.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM.Deploy.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM64.ActiveCfg = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM64.Build.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|ARM64.Deploy.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x64.ActiveCfg = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x64.Build.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x64.Deploy.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x86.ActiveCfg = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x86.Build.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (dynamic)|x86.Deploy.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM.ActiveCfg = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM.Build.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM.Deploy.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM64.ActiveCfg = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM64.Build.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|ARM64.Deploy.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x64.ActiveCfg = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x64.Build.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x64.Deploy.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x86.ActiveCfg = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x86.Build.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug (static)|x86.Deploy.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|ARM64.Deploy.0 = Debug|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x64.Build.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x86.Build.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Debug|x86.Deploy.0 = Debug|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM.ActiveCfg = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM.Build.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM.Deploy.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM64.ActiveCfg = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM64.Build.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|ARM64.Deploy.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x64.ActiveCfg = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x64.Build.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x64.Deploy.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x86.ActiveCfg = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x86.Build.0 = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (dynamic)|x86.Deploy.0 = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM.ActiveCfg = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM.Build.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM.Deploy.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM64.ActiveCfg = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM64.Build.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|ARM64.Deploy.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x64.ActiveCfg = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x64.Build.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x64.Deploy.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x86.ActiveCfg = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x86.Build.0 = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release (static)|x86.Deploy.0 = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM.Build.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|ARM64.Deploy.0 = Release|ARM64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x64.ActiveCfg = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x64.Build.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x64.Deploy.0 = Release|x64
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x86.ActiveCfg = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x86.Build.0 = Release|Win32
|
||||
{040101B0-EE5C-4EF1-99EE-9F81C795C001}.Release|x86.Deploy.0 = Release|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|ARM.ActiveCfg = Debug (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|ARM64.ActiveCfg = Debug (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|x64.ActiveCfg = Debug (dynamic)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|x64.Build.0 = Debug (dynamic)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|x86.ActiveCfg = Debug (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (dynamic)|x86.Build.0 = Debug (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|ARM.ActiveCfg = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|ARM64.ActiveCfg = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|x64.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|x64.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|x86.ActiveCfg = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug (static)|x86.Build.0 = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|ARM.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|ARM.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|ARM64.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|ARM64.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|x64.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|x64.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|x86.ActiveCfg = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Debug|x86.Build.0 = Debug (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|ARM.ActiveCfg = Release (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|ARM64.ActiveCfg = Release (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|x64.ActiveCfg = Release (dynamic)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|x64.Build.0 = Release (dynamic)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|x86.ActiveCfg = Release (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (dynamic)|x86.Build.0 = Release (dynamic)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|ARM.ActiveCfg = Release (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|ARM64.ActiveCfg = Release (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|x64.ActiveCfg = Release (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|x64.Build.0 = Release (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|x86.ActiveCfg = Release (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release (static)|x86.Build.0 = Release (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|ARM.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|ARM.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|ARM64.ActiveCfg = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|ARM64.Build.0 = Debug (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|x64.ActiveCfg = Release (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|x64.Build.0 = Release (static)|x64
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|x86.ActiveCfg = Release (static)|Win32
|
||||
{7DB06674-1F4F-464B-8E1C-172E9587F9DC}.Release|x86.Build.0 = Release (static)|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D5CD61FD-80BB-4E0E-840C-BAF66ABB1CF0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
8
lib/README.md
Normal file
8
lib/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# ViGEm user-mode client library
|
||||
This static library provides the gateway to the bus drivers functionalities. It offers:
|
||||
* Searching and connecting to a library-compatible bus device on the system
|
||||
* Attaching and removing a (artificially limited) number of virtual devices
|
||||
* Feeding the emulated devices (aka providing input state changes)
|
||||
|
||||
## Dependencies
|
||||
In addition to this library you'll need to link against `setupapi.lib`.
|
||||
636
lib/ViGEmClient.cpp
Normal file
636
lib/ViGEmClient.cpp
Normal file
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <SetupAPI.h>
|
||||
#include <initguid.h>
|
||||
|
||||
#include "ViGEmBusShared.h"
|
||||
#include "ViGEmClient.h"
|
||||
#include <winioctl.h>
|
||||
#include <limits.h>
|
||||
#include <mutex>
|
||||
|
||||
#define VIGEM_TARGETS_MAX USHRT_MAX
|
||||
|
||||
//
|
||||
// Represents a driver connection object.
|
||||
//
|
||||
typedef struct _VIGEM_CLIENT_T
|
||||
{
|
||||
HANDLE hBusDevice;
|
||||
|
||||
} VIGEM_CLIENT;
|
||||
|
||||
//
|
||||
// Represents the (connection) state of a target device object.
|
||||
//
|
||||
typedef enum _VIGEM_TARGET_STATE
|
||||
{
|
||||
VIGEM_TARGET_NEW,
|
||||
VIGEM_TARGET_INITIALIZED,
|
||||
VIGEM_TARGET_CONNECTED,
|
||||
VIGEM_TARGET_DISCONNECTED
|
||||
} VIGEM_TARGET_STATE, *PVIGEM_TARGET_STATE;
|
||||
|
||||
//
|
||||
// Represents a virtual gamepad object.
|
||||
//
|
||||
typedef struct _VIGEM_TARGET_T
|
||||
{
|
||||
ULONG Size;
|
||||
ULONG SerialNo;
|
||||
VIGEM_TARGET_STATE State;
|
||||
USHORT VendorId;
|
||||
USHORT ProductId;
|
||||
VIGEM_TARGET_TYPE Type;
|
||||
DWORD_PTR Notification;
|
||||
|
||||
} VIGEM_TARGET;
|
||||
|
||||
//
|
||||
// Initializes a virtual gamepad object.
|
||||
//
|
||||
PVIGEM_TARGET FORCEINLINE VIGEM_TARGET_ALLOC_INIT(
|
||||
_In_ VIGEM_TARGET_TYPE Type
|
||||
)
|
||||
{
|
||||
auto target = static_cast<PVIGEM_TARGET>(malloc(sizeof(VIGEM_TARGET)));
|
||||
RtlZeroMemory(target, sizeof(VIGEM_TARGET));
|
||||
|
||||
target->Size = sizeof(VIGEM_TARGET);
|
||||
target->State = VIGEM_TARGET_INITIALIZED;
|
||||
target->Type = Type;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
PVIGEM_CLIENT vigem_alloc()
|
||||
{
|
||||
auto driver = static_cast<PVIGEM_CLIENT>(malloc(sizeof(VIGEM_CLIENT)));
|
||||
|
||||
RtlZeroMemory(driver, sizeof(VIGEM_CLIENT));
|
||||
driver->hBusDevice = INVALID_HANDLE_VALUE;
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
void vigem_free(PVIGEM_CLIENT vigem)
|
||||
{
|
||||
if (vigem)
|
||||
free(vigem);
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_connect(PVIGEM_CLIENT vigem)
|
||||
{
|
||||
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
|
||||
deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
|
||||
DWORD memberIndex = 0;
|
||||
DWORD requiredSize = 0;
|
||||
auto error = VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
|
||||
// check for already open handle as re-opening accidentally would destroy all live targets
|
||||
if (vigem->hBusDevice != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
auto deviceInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_BUSENUM_VIGEM, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
// enumerate device instances
|
||||
while (SetupDiEnumDeviceInterfaces(deviceInfoSet, nullptr, &GUID_DEVINTERFACE_BUSENUM_VIGEM, memberIndex++, &deviceInterfaceData))
|
||||
{
|
||||
// get required target buffer size
|
||||
SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, nullptr, 0, &requiredSize, nullptr);
|
||||
|
||||
// allocate target buffer
|
||||
auto detailDataBuffer = static_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(malloc(requiredSize));
|
||||
detailDataBuffer->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
// get detail buffer
|
||||
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, detailDataBuffer, requiredSize, &requiredSize, nullptr))
|
||||
{
|
||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
free(detailDataBuffer);
|
||||
error = VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
continue;
|
||||
}
|
||||
|
||||
// bus found, open it
|
||||
vigem->hBusDevice = CreateFile(detailDataBuffer->DevicePath,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED,
|
||||
nullptr);
|
||||
|
||||
// check bus open result
|
||||
if (vigem->hBusDevice == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = VIGEM_ERROR_BUS_ACCESS_FAILED;
|
||||
free(detailDataBuffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD transfered = 0;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
VIGEM_CHECK_VERSION version;
|
||||
VIGEM_CHECK_VERSION_INIT(&version, VIGEM_COMMON_VERSION);
|
||||
|
||||
// send compiled library version to driver to check compatibility
|
||||
DeviceIoControl(vigem->hBusDevice, IOCTL_VIGEM_CHECK_VERSION, &version, version.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
// wait for result
|
||||
if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
error = VIGEM_ERROR_NONE;
|
||||
free(detailDataBuffer);
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
error = VIGEM_ERROR_BUS_VERSION_MISMATCH;
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
free(detailDataBuffer);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void vigem_disconnect(PVIGEM_CLIENT vigem)
|
||||
{
|
||||
if (vigem->hBusDevice != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(vigem->hBusDevice);
|
||||
|
||||
RtlZeroMemory(vigem, sizeof(VIGEM_CLIENT));
|
||||
vigem->hBusDevice = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
PVIGEM_TARGET vigem_target_x360_alloc(void)
|
||||
{
|
||||
auto target = VIGEM_TARGET_ALLOC_INIT(Xbox360Wired);
|
||||
|
||||
target->VendorId = 0x045E;
|
||||
target->ProductId = 0x028E;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
PVIGEM_TARGET vigem_target_ds4_alloc(void)
|
||||
{
|
||||
auto target = VIGEM_TARGET_ALLOC_INIT(DualShock4Wired);
|
||||
|
||||
target->VendorId = 0x054C;
|
||||
target->ProductId = 0x05C4;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
void vigem_target_free(PVIGEM_TARGET target)
|
||||
{
|
||||
if (target)
|
||||
free(target);
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_add(PVIGEM_CLIENT vigem, PVIGEM_TARGET target)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->State == VIGEM_TARGET_NEW)
|
||||
{
|
||||
return VIGEM_ERROR_TARGET_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (target->State == VIGEM_TARGET_CONNECTED)
|
||||
{
|
||||
return VIGEM_ERROR_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
DWORD transfered = 0;
|
||||
VIGEM_PLUGIN_TARGET plugin;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
for (target->SerialNo = 1; target->SerialNo <= VIGEM_TARGETS_MAX; target->SerialNo++)
|
||||
{
|
||||
VIGEM_PLUGIN_TARGET_INIT(&plugin, target->SerialNo, target->Type);
|
||||
|
||||
plugin.VendorId = target->VendorId;
|
||||
plugin.ProductId = target->ProductId;
|
||||
|
||||
DeviceIoControl(vigem->hBusDevice, IOCTL_VIGEM_PLUGIN_TARGET, &plugin, plugin.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
target->State = VIGEM_TARGET_CONNECTED;
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
return VIGEM_ERROR_NO_FREE_SLOT;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_add_async(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PVIGEM_TARGET_ADD_RESULT result)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->State == VIGEM_TARGET_NEW)
|
||||
{
|
||||
return VIGEM_ERROR_TARGET_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (target->State == VIGEM_TARGET_CONNECTED)
|
||||
{
|
||||
return VIGEM_ERROR_ALREADY_CONNECTED;
|
||||
}
|
||||
|
||||
std::thread _async{ [](
|
||||
PVIGEM_TARGET _Target,
|
||||
PVIGEM_CLIENT _Client,
|
||||
PVIGEM_TARGET_ADD_RESULT _Result)
|
||||
{
|
||||
DWORD transfered = 0;
|
||||
VIGEM_PLUGIN_TARGET plugin;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
for (_Target->SerialNo = 1; _Target->SerialNo <= VIGEM_TARGETS_MAX; _Target->SerialNo++)
|
||||
{
|
||||
VIGEM_PLUGIN_TARGET_INIT(&plugin, _Target->SerialNo, _Target->Type);
|
||||
|
||||
plugin.VendorId = _Target->VendorId;
|
||||
plugin.ProductId = _Target->ProductId;
|
||||
|
||||
DeviceIoControl(_Client->hBusDevice, IOCTL_VIGEM_PLUGIN_TARGET, &plugin, plugin.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(_Client->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
_Target->State = VIGEM_TARGET_CONNECTED;
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
if (_Result)
|
||||
_Result(_Client, _Target, VIGEM_ERROR_NONE);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
if (_Result)
|
||||
_Result(_Client, _Target, VIGEM_ERROR_NO_FREE_SLOT);
|
||||
|
||||
}, target, vigem, result };
|
||||
|
||||
_async.detach();
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_remove(PVIGEM_CLIENT vigem, PVIGEM_TARGET target)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->State == VIGEM_TARGET_NEW)
|
||||
{
|
||||
return VIGEM_ERROR_TARGET_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (target->State != VIGEM_TARGET_CONNECTED)
|
||||
{
|
||||
return VIGEM_ERROR_TARGET_NOT_PLUGGED_IN;
|
||||
}
|
||||
|
||||
DWORD transfered = 0;
|
||||
VIGEM_UNPLUG_TARGET unplug;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
VIGEM_UNPLUG_TARGET_INIT(&unplug, target->SerialNo);
|
||||
|
||||
DeviceIoControl(vigem->hBusDevice, IOCTL_VIGEM_UNPLUG_TARGET, &unplug, unplug.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
target->State = VIGEM_TARGET_DISCONNECTED;
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
//g_xusbCallbacks.erase(Target->SerialNo);
|
||||
//g_ds4Callbacks.erase(Target->SerialNo);
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
return VIGEM_ERROR_REMOVAL_FAILED;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_x360_register_notification(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PVIGEM_X360_NOTIFICATION notification)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->SerialNo == 0 || notification == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
|
||||
if (target->Notification == reinterpret_cast<DWORD_PTR>(notification))
|
||||
{
|
||||
return VIGEM_ERROR_CALLBACK_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
target->Notification = reinterpret_cast<DWORD_PTR>(notification);
|
||||
|
||||
std::thread _async{ [](
|
||||
PVIGEM_TARGET _Target,
|
||||
PVIGEM_CLIENT _Client)
|
||||
{
|
||||
DWORD error = ERROR_SUCCESS;
|
||||
DWORD transfered = 0;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
XUSB_REQUEST_NOTIFICATION notify;
|
||||
XUSB_REQUEST_NOTIFICATION_INIT(¬ify, _Target->SerialNo);
|
||||
|
||||
do
|
||||
{
|
||||
DeviceIoControl(_Client->hBusDevice,
|
||||
IOCTL_XUSB_REQUEST_NOTIFICATION,
|
||||
¬ify,
|
||||
notify.Size,
|
||||
¬ify,
|
||||
notify.Size,
|
||||
&transfered,
|
||||
&lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(_Client->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
if (_Target->Notification == NULL)
|
||||
{
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
reinterpret_cast<PVIGEM_X360_NOTIFICATION>(_Target->Notification)(
|
||||
_Client,
|
||||
_Target,
|
||||
notify.LargeMotor,
|
||||
notify.SmallMotor,
|
||||
notify.LedNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = GetLastError();
|
||||
}
|
||||
} while (error != ERROR_OPERATION_ABORTED && error != ERROR_ACCESS_DENIED);
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
}, target, vigem };
|
||||
|
||||
_async.detach();
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_ds4_register_notification(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PVIGEM_DS4_NOTIFICATION notification)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->SerialNo == 0 || notification == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
|
||||
if (target->Notification == reinterpret_cast<DWORD_PTR>(notification))
|
||||
{
|
||||
return VIGEM_ERROR_CALLBACK_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
target->Notification = reinterpret_cast<DWORD_PTR>(notification);
|
||||
|
||||
std::thread _async{ [](
|
||||
PVIGEM_TARGET _Target,
|
||||
PVIGEM_CLIENT _Client)
|
||||
{
|
||||
DWORD error = ERROR_SUCCESS;
|
||||
DWORD transfered = 0;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
DS4_REQUEST_NOTIFICATION notify;
|
||||
DS4_REQUEST_NOTIFICATION_INIT(¬ify, _Target->SerialNo);
|
||||
|
||||
do
|
||||
{
|
||||
DeviceIoControl(_Client->hBusDevice,
|
||||
IOCTL_DS4_REQUEST_NOTIFICATION,
|
||||
¬ify,
|
||||
notify.Size,
|
||||
¬ify,
|
||||
notify.Size,
|
||||
&transfered,
|
||||
&lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(_Client->hBusDevice, &lOverlapped, &transfered, TRUE) != 0)
|
||||
{
|
||||
if (_Target->Notification == NULL)
|
||||
{
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
reinterpret_cast<PVIGEM_DS4_NOTIFICATION>(_Target->Notification)(
|
||||
_Client,
|
||||
_Target,
|
||||
notify.Report.LargeMotor,
|
||||
notify.Report.SmallMotor,
|
||||
notify.Report.LightbarColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = GetLastError();
|
||||
}
|
||||
} while (error != ERROR_OPERATION_ABORTED && error != ERROR_ACCESS_DENIED);
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
}, target, vigem };
|
||||
|
||||
_async.detach();
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
void vigem_target_x360_unregister_notification(PVIGEM_TARGET target)
|
||||
{
|
||||
target->Notification = NULL;
|
||||
}
|
||||
|
||||
void vigem_target_ds4_unregister_notification(PVIGEM_TARGET target)
|
||||
{
|
||||
target->Notification = NULL;
|
||||
}
|
||||
|
||||
void vigem_target_set_vid(PVIGEM_TARGET target, USHORT vid)
|
||||
{
|
||||
target->VendorId = vid;
|
||||
}
|
||||
|
||||
void vigem_target_set_pid(PVIGEM_TARGET target, USHORT pid)
|
||||
{
|
||||
target->ProductId = pid;
|
||||
}
|
||||
|
||||
USHORT vigem_target_get_vid(PVIGEM_TARGET target)
|
||||
{
|
||||
return target->VendorId;
|
||||
}
|
||||
|
||||
USHORT vigem_target_get_pid(PVIGEM_TARGET target)
|
||||
{
|
||||
return target->ProductId;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_x360_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, XUSB_REPORT report)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->SerialNo == 0)
|
||||
{
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
|
||||
DWORD transfered = 0;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
XUSB_SUBMIT_REPORT xsr;
|
||||
XUSB_SUBMIT_REPORT_INIT(&xsr, target->SerialNo);
|
||||
|
||||
xsr.Report = report;
|
||||
|
||||
DeviceIoControl(vigem->hBusDevice, IOCTL_XUSB_SUBMIT_REPORT, &xsr, xsr.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) == 0)
|
||||
{
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED)
|
||||
{
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
VIGEM_ERROR vigem_target_ds4_update(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, DS4_REPORT report)
|
||||
{
|
||||
if (vigem->hBusDevice == nullptr)
|
||||
{
|
||||
return VIGEM_ERROR_BUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (target->SerialNo == 0)
|
||||
{
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
|
||||
DWORD transfered = 0;
|
||||
OVERLAPPED lOverlapped = { 0 };
|
||||
lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
|
||||
DS4_SUBMIT_REPORT dsr;
|
||||
DS4_SUBMIT_REPORT_INIT(&dsr, target->SerialNo);
|
||||
|
||||
dsr.Report = report;
|
||||
|
||||
DeviceIoControl(vigem->hBusDevice, IOCTL_DS4_SUBMIT_REPORT, &dsr, dsr.Size, nullptr, 0, &transfered, &lOverlapped);
|
||||
|
||||
if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) == 0)
|
||||
{
|
||||
if (GetLastError() == ERROR_ACCESS_DENIED)
|
||||
{
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
return VIGEM_ERROR_INVALID_TARGET;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(lOverlapped.hEvent);
|
||||
|
||||
return VIGEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
ULONG vigem_target_get_index(PVIGEM_TARGET target)
|
||||
{
|
||||
return target->SerialNo;
|
||||
}
|
||||
|
||||
VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target)
|
||||
{
|
||||
return target->Type;
|
||||
}
|
||||
|
||||
BOOL vigem_target_is_attached(PVIGEM_TARGET target)
|
||||
{
|
||||
return (target->State == VIGEM_TARGET_CONNECTED);
|
||||
}
|
||||
99
lib/ViGEmClient.rc
Normal file
99
lib/ViGEmClient.rc
Normal file
@@ -0,0 +1,99 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// German (Austria) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEA)
|
||||
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,14,3,0
|
||||
PRODUCTVERSION 1,14,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x7L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "0c0704b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Benjamin H<>glinger-Stelzer"
|
||||
VALUE "FileDescription", "Virtual Gamepad Emulation User-Mode Library"
|
||||
VALUE "FileVersion", "1.14.3.0"
|
||||
VALUE "InternalName", "Virtual Gamepad Emulation User-Mode Library"
|
||||
VALUE "LegalCopyright", "Copyright (C) Benjamin H<>glinger-Stelzer 2017"
|
||||
VALUE "OriginalFilename", "ViGEmCli.lib"
|
||||
VALUE "ProductName", "Virtual Gamepad Emulation User-Mode Library"
|
||||
VALUE "ProductVersion", "1.14.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0xc07, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // German (Austria) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
290
lib/ViGEmClient.vcxproj
Normal file
290
lib/ViGEmClient.vcxproj
Normal file
@@ -0,0 +1,290 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug (dynamic)|Win32">
|
||||
<Configuration>Debug (dynamic)</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug (dynamic)|x64">
|
||||
<Configuration>Debug (dynamic)</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug (static)|Win32">
|
||||
<Configuration>Debug (static)</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release (dynamic)|Win32">
|
||||
<Configuration>Release (dynamic)</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release (dynamic)|x64">
|
||||
<Configuration>Release (dynamic)</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release (static)|Win32">
|
||||
<Configuration>Release (static)</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug (static)|x64">
|
||||
<Configuration>Debug (static)</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release (static)|x64">
|
||||
<Configuration>Release (static)</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{7DB06674-1F4F-464B-8E1C-172E9587F9DC}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>ViGEmClient</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug (static)|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release (static)|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug (static)|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release (static)|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|Win32'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|x64'">
|
||||
<IncludePath>$(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>VIGEM_DYNAMIC;VIGEM_EXPORTS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalDependencies>setupapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (static)|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug (dynamic)|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>VIGEM_DYNAMIC;VIGEM_EXPORTS;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<AdditionalDependencies>setupapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>VIGEM_DYNAMIC;VIGEM_EXPORTS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>setupapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release (static)|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release (dynamic)|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>VIGEM_DYNAMIC;VIGEM_EXPORTS;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>setupapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusShared.h" />
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmClient.h" />
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmCommon.h" />
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmUtil.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ViGEmClient.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmClient.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
44
lib/ViGEmClient.vcxproj.filters
Normal file
44
lib/ViGEmClient.vcxproj.filters
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusShared.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmClient.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmCommon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmUtil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ViGEmClient.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmClient.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
3
lib/index.rst
Normal file
3
lib/index.rst
Normal file
@@ -0,0 +1,3 @@
|
||||
======
|
||||
Client
|
||||
======
|
||||
14
lib/resource.h
Normal file
14
lib/resource.h
Normal file
@@ -0,0 +1,14 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by ViGEmClient.rc
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
189
sys/ByteArray.c
Normal file
189
sys/ByteArray.c
Normal file
@@ -0,0 +1,189 @@
|
||||
#include "ByteArray.h"
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
ULONG_PTR align_to_page_size(ULONG_PTR val)
|
||||
{
|
||||
return (val + (PAGE_SIZE - 1)) & -PAGE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Forward decalarations
|
||||
//
|
||||
|
||||
NTSTATUS IncreaseCapacityByteArray(IN PBYTE_ARRAY Array, IN ULONG NumElements);
|
||||
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
NTSTATUS InitByteArray(IN OUT PBYTE_ARRAY Array)
|
||||
{
|
||||
//
|
||||
// Initialize size and default capacity
|
||||
Array->Size = 0;
|
||||
Array->Capacity = INITIAL_ARRAY_CAPACITY;
|
||||
|
||||
//
|
||||
// Allocate memory
|
||||
Array->Data = (UCHAR*)ExAllocatePoolWithTag(PagedPool, Array->Capacity, ARRAY_POOL_TAG);
|
||||
if (Array->Data == NULL)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS AppendElementByteArray(IN PBYTE_ARRAY Array, IN PVOID Element)
|
||||
{
|
||||
//
|
||||
// Make sure there is room to expand into
|
||||
if (((Array->Size + 1) * sizeof(UCHAR)) > Array->Capacity)
|
||||
{
|
||||
//
|
||||
// Increase capacity
|
||||
NTSTATUS status = IncreaseCapacityByteArray(Array, sizeof(UCHAR));
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Append the element and increment the size
|
||||
RtlCopyMemory(Array->Data + (Array->Size * sizeof(UCHAR)), Element, sizeof(UCHAR));
|
||||
|
||||
//
|
||||
// Increment size
|
||||
Array->Size += 1;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS AppendElementsByteArray(IN PBYTE_ARRAY Array, IN PVOID Elements, IN ULONG NumElements)
|
||||
{
|
||||
//
|
||||
// Make sure there is room to expand into
|
||||
if ((Array->Size + NumElements) * sizeof(UCHAR) > Array->Capacity)
|
||||
{
|
||||
//
|
||||
// Increase capacity
|
||||
NTSTATUS status = IncreaseCapacityByteArray(Array, NumElements);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Append the elements and increase the size
|
||||
RtlCopyMemory(Array->Data + (Array->Size * sizeof(UCHAR)), Elements, NumElements * sizeof(UCHAR));
|
||||
|
||||
//
|
||||
// Increase size
|
||||
Array->Size += NumElements;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS IncreaseCapacityByteArray(IN PBYTE_ARRAY Array, IN ULONG NumElements)
|
||||
{
|
||||
UCHAR* NewData = NULL;
|
||||
|
||||
//
|
||||
// Align new size to the immediate next page boundary
|
||||
Array->Capacity = align_to_page_size((Array->Size + NumElements) * sizeof(UCHAR));
|
||||
|
||||
//
|
||||
// Allocate new data with new capacity
|
||||
NewData = (UCHAR*)ExAllocatePoolWithTag(PagedPool, Array->Capacity, ARRAY_POOL_TAG);
|
||||
if (NewData == NULL)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
//
|
||||
// Copy old data over
|
||||
RtlCopyMemory(NewData, Array->Data, Array->Size * sizeof(UCHAR));
|
||||
|
||||
//
|
||||
// Free old data
|
||||
ExFreePoolWithTag(Array->Data, ARRAY_POOL_TAG);
|
||||
|
||||
//
|
||||
// Set data pointer to new allocation
|
||||
Array->Data = NewData;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS GetElementByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, OUT PVOID Element)
|
||||
{
|
||||
//
|
||||
// Check array bounds
|
||||
if (Index >= Array->Size || (LONG)Index < 0)
|
||||
return STATUS_ARRAY_BOUNDS_EXCEEDED;
|
||||
|
||||
//
|
||||
// Copy data over
|
||||
RtlCopyMemory(Element, Array->Data + (Index * sizeof(UCHAR)), sizeof(UCHAR));
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS GetElementsByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, OUT PVOID Elements, IN ULONG NumElements)
|
||||
{
|
||||
//
|
||||
// Check array bounds
|
||||
if (Index >= Array->Size || (LONG)Index < 0)
|
||||
return STATUS_ARRAY_BOUNDS_EXCEEDED;
|
||||
|
||||
//
|
||||
// Copy data over
|
||||
RtlCopyMemory(Elements, Array->Data + (Index * sizeof(UCHAR)), NumElements * sizeof(UCHAR));
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS SetElementByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, IN PVOID Element)
|
||||
{
|
||||
//
|
||||
// Check array bounds
|
||||
if (Index >= Array->Size || (LONG)Index < 0)
|
||||
return STATUS_ARRAY_BOUNDS_EXCEEDED;
|
||||
|
||||
//
|
||||
// Copy data over
|
||||
RtlCopyMemory(Array->Data + (Index * sizeof(UCHAR)), Element, sizeof(UCHAR));
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS SetElementsByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, IN PVOID Elements, IN ULONG NumElements)
|
||||
{
|
||||
//
|
||||
// Check array bounds
|
||||
if (Index >= Array->Size || (LONG)Index < 0)
|
||||
return STATUS_ARRAY_BOUNDS_EXCEEDED;
|
||||
|
||||
//
|
||||
// Copy data over
|
||||
RtlCopyMemory(Array->Data + (Index * sizeof(UCHAR)), Elements, NumElements * sizeof(UCHAR));
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS FreeByteArray(IN PBYTE_ARRAY Array)
|
||||
{
|
||||
if (Array->Data == NULL)
|
||||
return STATUS_MEMORY_NOT_ALLOCATED;
|
||||
|
||||
//
|
||||
// Free data
|
||||
ExFreePoolWithTag(Array->Data, ARRAY_POOL_TAG);
|
||||
|
||||
//
|
||||
// Null out everything
|
||||
Array->Data = NULL;
|
||||
Array->Size = 0;
|
||||
Array->Capacity = 0;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
29
sys/ByteArray.h
Normal file
29
sys/ByteArray.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <ntifs.h>
|
||||
|
||||
#define INITIAL_ARRAY_CAPACITY PAGE_SIZE
|
||||
#define ARRAY_POOL_TAG 'arrA'
|
||||
|
||||
typedef struct _BYTE_ARRAY
|
||||
{
|
||||
UCHAR* Data; //> array of data we're storing
|
||||
ULONG_PTR Size; //> slots used so far
|
||||
ULONG_PTR Capacity; //> total available memory
|
||||
} BYTE_ARRAY, *PBYTE_ARRAY;
|
||||
|
||||
NTSTATUS InitByteArray(IN OUT PBYTE_ARRAY Array);
|
||||
|
||||
NTSTATUS AppendElementByteArray(IN PBYTE_ARRAY Array, IN PVOID Element);
|
||||
|
||||
NTSTATUS AppendElementsByteArray(IN PBYTE_ARRAY Array, IN PVOID Elements, IN ULONG NumElements);
|
||||
|
||||
NTSTATUS GetElementByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, OUT PVOID Element);
|
||||
|
||||
NTSTATUS GetElementsByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, OUT PVOID Elements, IN ULONG NumElements);
|
||||
|
||||
NTSTATUS SetElementByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, IN PVOID Element);
|
||||
|
||||
NTSTATUS SetElementsByteArray(IN PBYTE_ARRAY Array, IN ULONG Index, IN PVOID Elements, IN ULONG NumElements);
|
||||
|
||||
NTSTATUS FreeByteArray(IN PBYTE_ARRAY Array);
|
||||
173
sys/Context.h
Normal file
173
sys/Context.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Used to identify children in the device list of the bus.
|
||||
//
|
||||
typedef struct _PDO_IDENTIFICATION_DESCRIPTION
|
||||
{
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header
|
||||
|
||||
ULONG SerialNo;
|
||||
|
||||
//
|
||||
// PID of the process creating this PDO
|
||||
//
|
||||
DWORD OwnerProcessId;
|
||||
|
||||
//
|
||||
// Device type this PDO is emulating
|
||||
//
|
||||
VIGEM_TARGET_TYPE TargetType;
|
||||
|
||||
//
|
||||
// If set, the vendor ID the emulated device is reporting
|
||||
//
|
||||
USHORT VendorId;
|
||||
|
||||
//
|
||||
// If set, the product ID the emulated device is reporting
|
||||
//
|
||||
USHORT ProductId;
|
||||
|
||||
//
|
||||
// Is the current device owner another driver?
|
||||
//
|
||||
BOOLEAN OwnerIsDriver;
|
||||
|
||||
//
|
||||
// SessionId associated with file handle. Used to map file handles to emulated gamepad devices
|
||||
//
|
||||
LONG SessionId;
|
||||
|
||||
} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION;
|
||||
|
||||
//
|
||||
// The PDO device-extension (context).
|
||||
//
|
||||
typedef struct _PDO_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// Unique serial number of the device on the bus
|
||||
//
|
||||
ULONG SerialNo;
|
||||
|
||||
//
|
||||
// PID of the process creating this PDO
|
||||
//
|
||||
DWORD OwnerProcessId;
|
||||
|
||||
//
|
||||
// Device type this PDO is emulating
|
||||
//
|
||||
VIGEM_TARGET_TYPE TargetType;
|
||||
|
||||
//
|
||||
// If set, the vendor ID the emulated device is reporting
|
||||
//
|
||||
USHORT VendorId;
|
||||
|
||||
//
|
||||
// If set, the product ID the emulated device is reporting
|
||||
//
|
||||
USHORT ProductId;
|
||||
|
||||
//
|
||||
// Interface for PDO to FDO communication
|
||||
//
|
||||
VIGEM_BUS_INTERFACE BusInterface;
|
||||
|
||||
//
|
||||
// Queue for incoming data interrupt transfer
|
||||
//
|
||||
WDFQUEUE PendingUsbInRequests;
|
||||
|
||||
//
|
||||
// Queue for inverted calls
|
||||
//
|
||||
WDFQUEUE PendingNotificationRequests;
|
||||
|
||||
} PDO_DEVICE_DATA, *PPDO_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_DEVICE_DATA, PdoGetData)
|
||||
|
||||
//
|
||||
// FDO (bus device) context data
|
||||
//
|
||||
typedef struct _FDO_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// Counter of interface references
|
||||
//
|
||||
LONG InterfaceReferenceCounter;
|
||||
|
||||
//
|
||||
// Next SessionId to assign to a file handle
|
||||
//
|
||||
LONG NextSessionId;
|
||||
|
||||
//
|
||||
// Collection holding pending plugin requests
|
||||
//
|
||||
WDFCOLLECTION PendingPluginRequests;
|
||||
|
||||
//
|
||||
// Sync lock for pending request collection
|
||||
//
|
||||
WDFSPINLOCK PendingPluginRequestsLock;
|
||||
|
||||
} FDO_DEVICE_DATA, *PFDO_DEVICE_DATA;
|
||||
|
||||
#define FDO_FIRST_SESSION_ID 100
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_DEVICE_DATA, FdoGetData)
|
||||
|
||||
//
|
||||
// Context data associated with file objects created by user mode applications
|
||||
//
|
||||
typedef struct _FDO_FILE_DATA
|
||||
{
|
||||
//
|
||||
// SessionId associated with file handle. Used to map file handles to emulated gamepad devices
|
||||
//
|
||||
LONG SessionId;
|
||||
|
||||
} FDO_FILE_DATA, *PFDO_FILE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_FILE_DATA, FileObjectGetData)
|
||||
|
||||
//
|
||||
// Context data for plugin requests
|
||||
//
|
||||
typedef struct _FDO_PLUGIN_REQUEST_DATA
|
||||
{
|
||||
ULONG Serial;
|
||||
|
||||
} FDO_PLUGIN_REQUEST_DATA, *PFDO_PLUGIN_REQUEST_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_PLUGIN_REQUEST_DATA, PluginRequestGetData)
|
||||
|
||||
442
sys/Driver.c
Normal file
442
sys/Driver.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmguid.h>
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (INIT, DriverEntry)
|
||||
#pragma alloc_text (PAGE, Bus_EvtDeviceAdd)
|
||||
#pragma alloc_text (PAGE, Bus_DeviceFileCreate)
|
||||
#pragma alloc_text (PAGE, Bus_FileClose)
|
||||
#pragma alloc_text (PAGE, Bus_PdoStageResult)
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// Driver entry routine.
|
||||
//
|
||||
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
WDF_DRIVER_CONFIG config;
|
||||
NTSTATUS status;
|
||||
WDFDRIVER driver;
|
||||
|
||||
KdPrint((DRIVERNAME "Virtual Gamepad Emulation Bus Driver [built: %s %s]\n", __DATE__, __TIME__));
|
||||
|
||||
WDF_DRIVER_CONFIG_INIT(&config, Bus_EvtDeviceAdd);
|
||||
|
||||
status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, &driver);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDriverCreate failed with status 0x%x\n", status));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Bus-device creation routine.
|
||||
//
|
||||
NTSTATUS Bus_EvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
|
||||
{
|
||||
WDF_CHILD_LIST_CONFIG config;
|
||||
NTSTATUS status;
|
||||
WDFDEVICE device;
|
||||
WDF_IO_QUEUE_CONFIG queueConfig;
|
||||
PNP_BUS_INFORMATION busInfo;
|
||||
WDFQUEUE queue;
|
||||
WDF_FILEOBJECT_CONFIG foConfig;
|
||||
WDF_OBJECT_ATTRIBUTES fdoAttributes;
|
||||
WDF_OBJECT_ATTRIBUTES fileHandleAttributes;
|
||||
WDF_OBJECT_ATTRIBUTES collectionAttributes;
|
||||
PFDO_DEVICE_DATA pFDOData;
|
||||
VIGEM_BUS_INTERFACE busInterface;
|
||||
PINTERFACE interfaceHeader;
|
||||
|
||||
UNREFERENCED_PARAMETER(Driver);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtDeviceAdd: 0x%p\n", Driver));
|
||||
|
||||
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
|
||||
// More than one process may talk to the bus at the same time
|
||||
WdfDeviceInitSetExclusive(DeviceInit, FALSE);
|
||||
// Bus is power policy owner over all PDOs
|
||||
WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, TRUE);
|
||||
|
||||
#pragma region Prepare child list
|
||||
|
||||
WDF_CHILD_LIST_CONFIG_INIT(&config, sizeof(PDO_IDENTIFICATION_DESCRIPTION), Bus_EvtDeviceListCreatePdo);
|
||||
|
||||
config.EvtChildListIdentificationDescriptionCompare = Bus_EvtChildListIdentificationDescriptionCompare;
|
||||
|
||||
WdfFdoInitSetDefaultChildListConfig(DeviceInit, &config, WDF_NO_OBJECT_ATTRIBUTES);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Assign File Object Configuration
|
||||
|
||||
WDF_FILEOBJECT_CONFIG_INIT(&foConfig, Bus_DeviceFileCreate, Bus_FileClose, NULL);
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fileHandleAttributes, FDO_FILE_DATA);
|
||||
|
||||
WdfDeviceInitSetFileObjectConfig(DeviceInit, &foConfig, &fileHandleAttributes);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create FDO
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FDO_DEVICE_DATA);
|
||||
|
||||
status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &device);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Error creating device 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
KdPrint((DRIVERNAME "Created FDO: 0x%X\n", device));
|
||||
|
||||
pFDOData = FdoGetData(device);
|
||||
if (pFDOData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Error creating device context\n"));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
pFDOData->InterfaceReferenceCounter = 0;
|
||||
pFDOData->NextSessionId = FDO_FIRST_SESSION_ID;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create pending requests collection & lock
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes);
|
||||
collectionAttributes.ParentObject = device;
|
||||
|
||||
status = WdfCollectionCreate(&collectionAttributes, &pFDOData->PendingPluginRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfCollectionCreate failed with status 0x%X\n", status));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttributes);
|
||||
collectionAttributes.ParentObject = device;
|
||||
|
||||
status = WdfSpinLockCreate(&collectionAttributes, &pFDOData->PendingPluginRequestsLock);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfSpinLockCreate failed with status 0x%X\n", status));
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Add query interface
|
||||
|
||||
//
|
||||
// Set up the common interface header
|
||||
//
|
||||
interfaceHeader = &busInterface.InterfaceHeader;
|
||||
|
||||
interfaceHeader->Size = sizeof(VIGEM_BUS_INTERFACE);
|
||||
interfaceHeader->Version = VIGEM_BUS_INTERFACE_VERSION;
|
||||
interfaceHeader->Context = (PVOID)device;
|
||||
|
||||
//
|
||||
// We don't pay any particular attention to the reference
|
||||
// counting of this interface, but we MUST specify routines for
|
||||
// it. Luckily the framework provides dummy routines
|
||||
//
|
||||
interfaceHeader->InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
interfaceHeader->InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
busInterface.BusPdoStageResult = Bus_PdoStageResult;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG queryInterfaceConfig;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&queryInterfaceConfig,
|
||||
interfaceHeader,
|
||||
&GUID_VIGEM_INTERFACE_PDO,
|
||||
WDF_NO_EVENT_CALLBACK);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(device,
|
||||
&queryInterfaceConfig);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
KdPrint((DRIVERNAME "WdfDeviceAddQueryInterface failed 0x%0x\n", status));
|
||||
return(status);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create default I/O queue for FDO
|
||||
|
||||
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
|
||||
|
||||
queueConfig.EvtIoDeviceControl = Bus_EvtIoDeviceControl;
|
||||
queueConfig.EvtIoInternalDeviceControl = Bus_EvtIoInternalDeviceControl;
|
||||
queueConfig.EvtIoDefault = Bus_EvtIoDefault;
|
||||
|
||||
__analysis_assume(queueConfig.EvtIoStop != 0);
|
||||
status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);
|
||||
__analysis_assume(queueConfig.EvtIoStop == 0);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Expose FDO interface
|
||||
|
||||
status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_BUSENUM_VIGEM, NULL);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceCreateDeviceInterface failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Set bus information
|
||||
|
||||
busInfo.BusTypeGuid = GUID_BUS_TYPE_USB;
|
||||
busInfo.LegacyBusType = PNPBus;
|
||||
busInfo.BusNumber = 0;
|
||||
|
||||
WdfDeviceSetBusInformationForChildren(device, &busInfo);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Gets called when the user-land process (or kernel driver) exits or closes the handle,
|
||||
// and all IO has completed.
|
||||
//
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Bus_DeviceFileCreate(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ WDFFILEOBJECT FileObject
|
||||
)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
PFDO_FILE_DATA pFileData = NULL;
|
||||
PFDO_DEVICE_DATA pFDOData = NULL;
|
||||
LONG refCount = 0;
|
||||
LONG sessionId = 0;
|
||||
|
||||
UNREFERENCED_PARAMETER(Request);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
pFileData = FileObjectGetData(FileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_DeviceFileCreate: ERROR! File handle context not found\n"));
|
||||
}
|
||||
else
|
||||
{
|
||||
pFDOData = FdoGetData(Device);
|
||||
if (pFDOData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_DeviceFileCreate: ERROR! FDO context not found\n"));
|
||||
status = STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
else
|
||||
{
|
||||
refCount = InterlockedIncrement(&pFDOData->InterfaceReferenceCounter);
|
||||
sessionId = InterlockedIncrement(&pFDOData->NextSessionId);
|
||||
|
||||
pFileData->SessionId = sessionId;
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_DeviceFileCreate: File id=%d. Device refcount=%d\n", (int)sessionId, (int)refCount));
|
||||
}
|
||||
}
|
||||
|
||||
WdfRequestComplete(Request, status);
|
||||
}
|
||||
|
||||
//
|
||||
// Gets called when the user-land process (or kernel driver) exits or closes the handle.
|
||||
//
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Bus_FileClose(
|
||||
WDFFILEOBJECT FileObject
|
||||
)
|
||||
{
|
||||
WDFDEVICE device;
|
||||
WDFDEVICE hChild;
|
||||
NTSTATUS status;
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_LIST_ITERATOR iterator;
|
||||
WDF_CHILD_RETRIEVE_INFO childInfo;
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
PFDO_FILE_DATA pFileData = NULL;
|
||||
PFDO_DEVICE_DATA pFDOData = NULL;
|
||||
LONG refCount = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_FileClose called\n"));
|
||||
|
||||
// Check common context
|
||||
pFileData = FileObjectGetData(FileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_FileClose: ERROR! File handle context not found\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
device = WdfFileObjectGetDevice(FileObject);
|
||||
|
||||
pFDOData = FdoGetData(device);
|
||||
if (pFDOData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_FileClose: ERROR! FDO context not found\n"));
|
||||
status = STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
else
|
||||
{
|
||||
refCount = InterlockedDecrement(&pFDOData->InterfaceReferenceCounter);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_FileClose: Device refcount=%d\n", (int)refCount));
|
||||
}
|
||||
|
||||
list = WdfFdoGetDefaultChildList(device);
|
||||
|
||||
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
|
||||
|
||||
WdfChildListBeginIteration(list, &iterator);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
|
||||
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_FileClose enumerate: status=%d devicePID=%d currentPID=%d fileSessionId=%d deviceSessionId=%d ownerIsDriver=%d\n",
|
||||
(int)childInfo.Status,
|
||||
(int)description.OwnerProcessId,
|
||||
(int)CURRENT_PROCESS_ID(),
|
||||
(int)pFileData->SessionId,
|
||||
(int)description.SessionId,
|
||||
(int)description.OwnerIsDriver));
|
||||
|
||||
// Only unplug devices with matching session id
|
||||
if (childInfo.Status == WdfChildListRetrieveDeviceSuccess
|
||||
&& description.SessionId == pFileData->SessionId
|
||||
&& !description.OwnerIsDriver)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_FileClose unplugging\n"));
|
||||
|
||||
// "Unplug" child
|
||||
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfChildListUpdateChildDescriptionAsMissing failed with status 0x%X\n", status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WdfChildListEndIteration(list, &iterator);
|
||||
}
|
||||
|
||||
//
|
||||
// Called by PDO when a boot-up stage has been completed
|
||||
//
|
||||
_Use_decl_annotations_
|
||||
VOID
|
||||
Bus_PdoStageResult(
|
||||
_In_ PINTERFACE InterfaceHeader,
|
||||
_In_ VIGEM_PDO_STAGE Stage,
|
||||
_In_ ULONG Serial,
|
||||
_In_ NTSTATUS Status
|
||||
)
|
||||
{
|
||||
ULONG i;
|
||||
PFDO_DEVICE_DATA pFdoData;
|
||||
WDFREQUEST curRequest;
|
||||
ULONG curSerial;
|
||||
ULONG items;
|
||||
|
||||
UNREFERENCED_PARAMETER(InterfaceHeader);
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_PdoStageResult: Stage: %d, Serial: %d, status: 0x%X\n", Stage, Serial, Status));
|
||||
|
||||
pFdoData = FdoGetData(InterfaceHeader->Context);
|
||||
|
||||
//
|
||||
// If any stage fails or is last stage, get associated request and complete it
|
||||
//
|
||||
if (!NT_SUCCESS(Status) || Stage == ViGEmPdoInternalIoControl)
|
||||
{
|
||||
WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
items = WdfCollectionGetCount(pFdoData->PendingPluginRequests);
|
||||
|
||||
KdPrint((DRIVERNAME "Items count: %d\n", items));
|
||||
|
||||
for (i = 0; i < items; i++)
|
||||
{
|
||||
curRequest = WdfCollectionGetItem(pFdoData->PendingPluginRequests, i);
|
||||
curSerial = PluginRequestGetData(curRequest)->Serial;
|
||||
|
||||
KdPrint((DRIVERNAME "Serial: %d, curSerial: %d\n", Serial, curSerial));
|
||||
|
||||
if (Serial == curSerial)
|
||||
{
|
||||
WdfRequestComplete(curRequest, Status);
|
||||
|
||||
WdfCollectionRemove(pFdoData->PendingPluginRequests, curRequest);
|
||||
|
||||
KdPrint((DRIVERNAME "Removed item with serial: %d\n", curSerial));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock);
|
||||
}
|
||||
}
|
||||
378
sys/Ds4.c
Normal file
378
sys/Ds4.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <hidclass.h>
|
||||
|
||||
NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription)
|
||||
{
|
||||
NTSTATUS status;
|
||||
UNICODE_STRING buffer;
|
||||
|
||||
// prepare device description
|
||||
status = RtlUnicodeStringInit(DeviceDescription, L"Virtual DualShock 4 Controller");
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set hardware IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4&REV_0100");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_054C&PID_05C4");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set compatible IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00&Prot_00");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03&SubClass_00");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_03");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_QUERY_INTERFACE_CONFIG ifaceCfg;
|
||||
INTERFACE devinterfaceHid;
|
||||
|
||||
devinterfaceHid.Size = sizeof(INTERFACE);
|
||||
devinterfaceHid.Version = 1;
|
||||
devinterfaceHid.Context = (PVOID)Device;
|
||||
|
||||
devinterfaceHid.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
devinterfaceHid.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
// Expose GUID_DEVINTERFACE_HID so HIDUSB can initialize
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&devinterfaceHid, &GUID_DEVINTERFACE_HID, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceAddQueryInterface failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
PDS4_DEVICE_DATA ds4Data = Ds4GetData(Device);
|
||||
|
||||
// Set default HID input report (everything zero`d)
|
||||
UCHAR DefaultHidReport[DS4_REPORT_SIZE] =
|
||||
{
|
||||
0x01, 0x82, 0x7F, 0x7E, 0x80, 0x08, 0x00, 0x58,
|
||||
0x00, 0x00, 0xFD, 0x63, 0x06, 0x03, 0x00, 0xFE,
|
||||
0xFF, 0xFC, 0xFF, 0x79, 0xFD, 0x1B, 0x14, 0xD1,
|
||||
0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00,
|
||||
0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
|
||||
0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00
|
||||
};
|
||||
|
||||
// Initialize HID reports to defaults
|
||||
RtlCopyBytes(ds4Data->Report, DefaultHidReport, DS4_REPORT_SIZE);
|
||||
RtlZeroMemory(&ds4Data->OutputReport, sizeof(DS4_OUTPUT_REPORT));
|
||||
|
||||
// Start pending IRP queue flush timer
|
||||
WdfTimerStart(ds4Data->PendingUsbInRequestsTimer, DS4_QUEUE_FLUSH_PERIOD);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
{
|
||||
NTSTATUS status;
|
||||
PDS4_DEVICE_DATA ds4 = Ds4GetData(Device);
|
||||
|
||||
KdPrint((DRIVERNAME "Initializing DS4 context...\n"));
|
||||
|
||||
// Initialize periodic timer
|
||||
WDF_TIMER_CONFIG timerConfig;
|
||||
WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, Ds4_PendingUsbRequestsTimerFunc, DS4_QUEUE_FLUSH_PERIOD);
|
||||
|
||||
// Timer object attributes
|
||||
WDF_OBJECT_ATTRIBUTES timerAttribs;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs);
|
||||
|
||||
// PDO is parent
|
||||
timerAttribs.ParentObject = Device;
|
||||
|
||||
// Create timer
|
||||
status = WdfTimerCreate(&timerConfig, &timerAttribs, &ds4->PendingUsbInRequestsTimer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfTimerCreate failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
// Load/generate MAC address
|
||||
|
||||
// TODO: tidy up this region
|
||||
|
||||
WDFKEY keyParams, keyTargets, keyDS, keySerial;
|
||||
UNICODE_STRING keyName, valueName;
|
||||
|
||||
status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(), STANDARD_RIGHTS_ALL, WDF_NO_OBJECT_ATTRIBUTES, &keyParams);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDriverOpenParametersRegistryKey failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&keyName, L"Targets");
|
||||
|
||||
status = WdfRegistryCreateKey(keyParams, &keyName,
|
||||
KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, NULL, WDF_NO_OBJECT_ATTRIBUTES, &keyTargets);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRegistryCreateKey failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&keyName, L"DualShock");
|
||||
|
||||
status = WdfRegistryCreateKey(keyTargets, &keyName,
|
||||
KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, NULL, WDF_NO_OBJECT_ATTRIBUTES, &keyDS);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRegistryCreateKey failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
DECLARE_UNICODE_STRING_SIZE(serialPath, 4);
|
||||
RtlUnicodeStringPrintf(&serialPath, L"%04d", Description->SerialNo);
|
||||
|
||||
status = WdfRegistryCreateKey(keyDS, &serialPath,
|
||||
KEY_ALL_ACCESS, REG_OPTION_NON_VOLATILE, NULL, WDF_NO_OBJECT_ATTRIBUTES, &keySerial);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRegistryCreateKey failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
RtlUnicodeStringInit(&valueName, L"TargetMacAddress");
|
||||
|
||||
status = WdfRegistryQueryValue(keySerial, &valueName, sizeof(MAC_ADDRESS), &ds4->TargetMacAddress, NULL, NULL);
|
||||
|
||||
KdPrint((DRIVERNAME "MAC-Address: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||
ds4->TargetMacAddress.Vendor0,
|
||||
ds4->TargetMacAddress.Vendor1,
|
||||
ds4->TargetMacAddress.Vendor2,
|
||||
ds4->TargetMacAddress.Nic0,
|
||||
ds4->TargetMacAddress.Nic1,
|
||||
ds4->TargetMacAddress.Nic2));
|
||||
|
||||
if (status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||||
{
|
||||
GenerateRandomMacAddress(&ds4->TargetMacAddress);
|
||||
|
||||
status = WdfRegistryAssignValue(keySerial, &valueName, REG_BINARY, sizeof(MAC_ADDRESS), (PVOID)&ds4->TargetMacAddress);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRegistryAssignValue failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
else if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRegistryQueryValue failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
WdfRegistryClose(keySerial);
|
||||
WdfRegistryClose(keyDS);
|
||||
WdfRegistryClose(keyTargets);
|
||||
WdfRegistryClose(keyParams);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length)
|
||||
{
|
||||
UCHAR Ds4DescriptorData[DS4_DESCRIPTOR_SIZE] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xC0, // bmAttributes Self Powered
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0xD3, 0x01, // wDescriptorLength[0] 467
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x84, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x05, // bInterval 5 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x03, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x05, // bInterval 5 (unit depends on device speed)
|
||||
|
||||
// 41 bytes
|
||||
|
||||
// best guess: USB Standard Descriptor
|
||||
};
|
||||
|
||||
RtlCopyBytes(Buffer, Ds4DescriptorData, Length);
|
||||
}
|
||||
|
||||
VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon)
|
||||
{
|
||||
pDescriptor->bLength = 0x12;
|
||||
pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
|
||||
pDescriptor->bcdUSB = 0x0200; // USB v2.0
|
||||
pDescriptor->bDeviceClass = 0x00; // per Interface
|
||||
pDescriptor->bDeviceSubClass = 0x00;
|
||||
pDescriptor->bDeviceProtocol = 0x00;
|
||||
pDescriptor->bMaxPacketSize0 = 0x40;
|
||||
pDescriptor->idVendor = pCommon->VendorId;
|
||||
pDescriptor->idProduct = pCommon->ProductId;
|
||||
pDescriptor->bcdDevice = 0x0100;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
pDescriptor->iSerialNumber = 0x00;
|
||||
pDescriptor->bNumConfigurations = 0x01;
|
||||
}
|
||||
|
||||
VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo)
|
||||
{
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0x03; // HID
|
||||
pInfo->SubClass = 0x00;
|
||||
pInfo->Protocol = 0x00;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x84;
|
||||
pInfo->Pipes[0].Interval = 0x05;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x03;
|
||||
pInfo->Pipes[1].Interval = 0x05;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
}
|
||||
|
||||
//
|
||||
// Completes pending I/O requests if feeder is too slow.
|
||||
//
|
||||
VOID Ds4_PendingUsbRequestsTimerFunc(
|
||||
_In_ WDFTIMER Timer
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFREQUEST usbRequest;
|
||||
WDFDEVICE hChild;
|
||||
PDS4_DEVICE_DATA ds4Data;
|
||||
PIRP pendingIrp;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
|
||||
hChild = WdfTimerGetParentObject(Timer);
|
||||
pdoData = PdoGetData(hChild);
|
||||
ds4Data = Ds4GetData(hChild);
|
||||
|
||||
// Get pending USB request
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
// Get pending IRP
|
||||
pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
irpStack = IoGetCurrentIrpStackLocation(pendingIrp);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = (PURB)irpStack->Parameters.Others.Argument1;
|
||||
|
||||
// Get transfer buffer
|
||||
PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer;
|
||||
// Set buffer length to report size
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
|
||||
|
||||
// Copy cached report to transfer buffer
|
||||
if (Buffer)
|
||||
RtlCopyBytes(Buffer, ds4Data->Report, DS4_REPORT_SIZE);
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
}
|
||||
}
|
||||
|
||||
112
sys/Ds4.h
Normal file
112
sys/Ds4.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#define HID_GET_FEATURE_REPORT_SIZE_0 0x31
|
||||
#define HID_GET_FEATURE_REPORT_SIZE_1 0x25
|
||||
#define HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE 0x10
|
||||
|
||||
#define HID_SET_FEATURE_REPORT_SIZE_0 0x17
|
||||
#define HID_SET_FEATURE_REPORT_SIZE_1 0x11
|
||||
|
||||
#define HID_REPORT_ID_0 0xA3
|
||||
#define HID_REPORT_ID_1 0x02
|
||||
#define HID_REPORT_MAC_ADDRESSES_ID 0x12
|
||||
#define HID_REPORT_ID_3 0x13
|
||||
#define HID_REPORT_ID_4 0x14
|
||||
|
||||
#define DS4_DESCRIPTOR_SIZE 0x0029
|
||||
#if defined(_X86_)
|
||||
#define DS4_CONFIGURATION_SIZE 0x0050
|
||||
#else
|
||||
#define DS4_CONFIGURATION_SIZE 0x0070
|
||||
#endif
|
||||
#define DS4_HID_REPORT_DESCRIPTOR_SIZE 0x01D3
|
||||
|
||||
#define DS4_MANUFACTURER_NAME_LENGTH 0x38
|
||||
#define DS4_PRODUCT_NAME_LENGTH 0x28
|
||||
#define DS4_OUTPUT_BUFFER_OFFSET 0x04
|
||||
#define DS4_OUTPUT_BUFFER_LENGTH 0x05
|
||||
|
||||
#define DS4_REPORT_SIZE 0x40
|
||||
#define DS4_QUEUE_FLUSH_PERIOD 0x05
|
||||
|
||||
|
||||
//
|
||||
// DS4-specific device context data.
|
||||
//
|
||||
typedef struct _DS4_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// HID Input Report buffer
|
||||
//
|
||||
UCHAR Report[DS4_REPORT_SIZE];
|
||||
|
||||
//
|
||||
// Output report cache
|
||||
//
|
||||
DS4_OUTPUT_REPORT OutputReport;
|
||||
|
||||
//
|
||||
// Timer for dispatching interrupt transfer
|
||||
//
|
||||
WDFTIMER PendingUsbInRequestsTimer;
|
||||
|
||||
//
|
||||
// Auto-generated MAC address of the target device
|
||||
//
|
||||
MAC_ADDRESS TargetMacAddress;
|
||||
|
||||
//
|
||||
// Default MAC address of the host (not used)
|
||||
//
|
||||
MAC_ADDRESS HostMacAddress;
|
||||
|
||||
} DS4_DEVICE_DATA, *PDS4_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DS4_DEVICE_DATA, Ds4GetData)
|
||||
|
||||
|
||||
EVT_WDF_TIMER Ds4_PendingUsbRequestsTimerFunc;
|
||||
|
||||
NTSTATUS
|
||||
Bus_Ds4SubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PDS4_SUBMIT_REPORT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
//
|
||||
// DS4-specific functions
|
||||
//
|
||||
NTSTATUS Ds4_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription);
|
||||
NTSTATUS Ds4_PrepareHardware(WDFDEVICE Device);
|
||||
NTSTATUS Ds4_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description);
|
||||
VOID Ds4_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length);
|
||||
VOID Ds4_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon);
|
||||
VOID Ds4_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo);
|
||||
|
||||
349
sys/Queue.c
Normal file
349
sys/Queue.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (PAGE, Bus_EvtIoDefault)
|
||||
#endif
|
||||
|
||||
//
|
||||
// Responds to I/O control requests sent to the FDO.
|
||||
//
|
||||
VOID Bus_EvtIoDeviceControl(
|
||||
IN WDFQUEUE Queue,
|
||||
IN WDFREQUEST Request,
|
||||
IN size_t OutputBufferLength,
|
||||
IN size_t InputBufferLength,
|
||||
IN ULONG IoControlCode
|
||||
)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE Device;
|
||||
size_t length = 0;
|
||||
PXUSB_SUBMIT_REPORT xusbSubmit = NULL;
|
||||
PXUSB_REQUEST_NOTIFICATION xusbNotify = NULL;
|
||||
PDS4_SUBMIT_REPORT ds4Submit = NULL;
|
||||
PDS4_REQUEST_NOTIFICATION ds4Notify = NULL;
|
||||
PXGIP_SUBMIT_REPORT xgipSubmit = NULL;
|
||||
PXGIP_SUBMIT_INTERRUPT xgipInterrupt = NULL;
|
||||
PVIGEM_CHECK_VERSION pCheckVersion = NULL;
|
||||
|
||||
Device = WdfIoQueueGetDevice(Queue);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtIoDeviceControl: 0x%p\n", Device));
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
#pragma region IOCTL_VIGEM_CHECK_VERSION
|
||||
case IOCTL_VIGEM_CHECK_VERSION:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_CHECK_VERSION\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_CHECK_VERSION), (PVOID)&pCheckVersion, &length);
|
||||
|
||||
if (!NT_SUCCESS(status) || length != sizeof(VIGEM_CHECK_VERSION))
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = (pCheckVersion->Version == VIGEM_COMMON_VERSION) ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_VIGEM_PLUGIN_TARGET
|
||||
case IOCTL_VIGEM_PLUGIN_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_PLUGIN_TARGET\n"));
|
||||
|
||||
status = Bus_PlugInDevice(Device, Request, FALSE, &length);
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_VIGEM_UNPLUG_TARGET
|
||||
case IOCTL_VIGEM_UNPLUG_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_UNPLUG_TARGET\n"));
|
||||
|
||||
status = Bus_UnPlugDevice(Device, Request, FALSE, &length);
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_XUSB_SUBMIT_REPORT
|
||||
case IOCTL_XUSB_SUBMIT_REPORT:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_XUSB_SUBMIT_REPORT\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_SUBMIT_REPORT), (PVOID)&xusbSubmit, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(XUSB_SUBMIT_REPORT) == xusbSubmit->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (xusbSubmit->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_XusbSubmitReport(Device, xusbSubmit->SerialNo, xusbSubmit, FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_XUSB_REQUEST_NOTIFICATION
|
||||
case IOCTL_XUSB_REQUEST_NOTIFICATION:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_XUSB_REQUEST_NOTIFICATION\n"));
|
||||
|
||||
// Don't accept the request if the output buffer can't hold the results
|
||||
if (OutputBufferLength < sizeof(XUSB_REQUEST_NOTIFICATION))
|
||||
{
|
||||
KdPrint((DRIVERNAME "IOCTL_XUSB_REQUEST_NOTIFICATION: output buffer too small: %ul\n", OutputBufferLength));
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)&xusbNotify, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(XUSB_REQUEST_NOTIFICATION) == xusbNotify->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (xusbNotify->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_QueueNotification(Device, xusbNotify->SerialNo, Request);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_DS4_SUBMIT_REPORT
|
||||
case IOCTL_DS4_SUBMIT_REPORT:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_DS4_SUBMIT_REPORT\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_SUBMIT_REPORT), (PVOID)&ds4Submit, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(DS4_SUBMIT_REPORT) == ds4Submit->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (ds4Submit->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_Ds4SubmitReport(Device, ds4Submit->SerialNo, ds4Submit, FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_DS4_REQUEST_NOTIFICATION
|
||||
case IOCTL_DS4_REQUEST_NOTIFICATION:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_DS4_REQUEST_NOTIFICATION\n"));
|
||||
|
||||
// Don't accept the request if the output buffer can't hold the results
|
||||
if (OutputBufferLength < sizeof(DS4_REQUEST_NOTIFICATION))
|
||||
{
|
||||
KdPrint((DRIVERNAME "IOCTL_DS4_REQUEST_NOTIFICATION: output buffer too small: %ul\n", OutputBufferLength));
|
||||
break;
|
||||
}
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)&ds4Notify, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(DS4_REQUEST_NOTIFICATION) == ds4Notify->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (ds4Notify->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_QueueNotification(Device, ds4Notify->SerialNo, Request);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_XGIP_SUBMIT_REPORT
|
||||
case IOCTL_XGIP_SUBMIT_REPORT:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_XGIP_SUBMIT_REPORT\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XGIP_SUBMIT_REPORT), (PVOID)&xgipSubmit, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(XGIP_SUBMIT_REPORT) == xgipSubmit->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (xgipSubmit->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_XgipSubmitReport(Device, xgipSubmit->SerialNo, xgipSubmit, FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IOCTL_XGIP_SUBMIT_INTERRUPT
|
||||
case IOCTL_XGIP_SUBMIT_INTERRUPT:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_XGIP_SUBMIT_INTERRUPT\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(XGIP_SUBMIT_INTERRUPT), (PVOID)&xgipInterrupt, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sizeof(XGIP_SUBMIT_INTERRUPT) == xgipInterrupt->Size) && (length == InputBufferLength))
|
||||
{
|
||||
// This request only supports a single PDO at a time
|
||||
if (xgipInterrupt->SerialNo == 0)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
status = Bus_XgipSubmitInterrupt(Device, xgipSubmit->SerialNo, xgipInterrupt, FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
#pragma endregion
|
||||
|
||||
default:
|
||||
KdPrint((DRIVERNAME "UNKNOWN IOCTL CODE 0x%x\n", IoControlCode));
|
||||
break; // default status is STATUS_INVALID_PARAMETER
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
WdfRequestCompleteWithInformation(Request, status, length);
|
||||
}
|
||||
}
|
||||
|
||||
VOID Bus_EvtIoInternalDeviceControl(
|
||||
_In_ WDFQUEUE Queue,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ size_t OutputBufferLength,
|
||||
_In_ size_t InputBufferLength,
|
||||
_In_ ULONG IoControlCode
|
||||
)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE Device;
|
||||
size_t length = 0;
|
||||
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
UNREFERENCED_PARAMETER(InputBufferLength);
|
||||
|
||||
Device = WdfIoQueueGetDevice(Queue);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtIoInternalDeviceControl: 0x%p\n", Device));
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
case IOCTL_VIGEM_PLUGIN_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_PLUGIN_TARGET\n"));
|
||||
|
||||
status = Bus_PlugInDevice(Device, Request, TRUE, &length);
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_VIGEM_UNPLUG_TARGET:
|
||||
|
||||
KdPrint((DRIVERNAME "IOCTL_VIGEM_UNPLUG_TARGET\n"));
|
||||
|
||||
status = Bus_UnPlugDevice(Device, Request, TRUE, &length);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
WdfRequestCompleteWithInformation(Request, status, length);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Catches unsupported requests.
|
||||
//
|
||||
VOID Bus_EvtIoDefault(
|
||||
_In_ WDFQUEUE Queue,
|
||||
_In_ WDFREQUEST Request
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Queue);
|
||||
UNREFERENCED_PARAMETER(Request);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtIoDefault called\n"));
|
||||
|
||||
WdfRequestComplete(Request, STATUS_INVALID_DEVICE_REQUEST);
|
||||
}
|
||||
30
sys/Queue.h
Normal file
30
sys/Queue.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
EVT_WDF_IO_QUEUE_IO_DEFAULT Bus_EvtIoDefault;
|
||||
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Bus_EvtIoDeviceControl;
|
||||
EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Bus_EvtIoInternalDeviceControl;
|
||||
17
sys/README.md
Normal file
17
sys/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# ViGEm Bus Driver
|
||||
|
||||
Currently supports emulation of the following USB gamepads:
|
||||
- [Microsoft Xbox 360 Controller](https://en.wikipedia.org/wiki/Xbox_360_controller)
|
||||
- [Sony DualShock 4 Controller](https://en.wikipedia.org/wiki/DualShock#DualShock_4)
|
||||
- [Microsoft Xbox One Controller](https://en.wikipedia.org/wiki/Xbox_One_Controller)
|
||||
- Experimental; not ready for stable release yet
|
||||
|
||||
## Necessary preparations for Windows 7
|
||||
Before installing the bus driver on Windows 7 (x86 or x64) the following 3rd party software has to be installed:
|
||||
* [Xbox 360 Accessories Software 1.2](https://www.microsoft.com/accessories/en-us/products/gaming/xbox-360-controller-for-windows/52a-00004#techspecs-connect) (contains the missing device drivers)
|
||||
* [Microsoft Security Advisory 3033929 Update](https://technet.microsoft.com/en-us/library/security/3033929) has to be installed to support the drivers signature. Download links:
|
||||
* [Security Update for Windows 7 (KB3033929)](https://www.microsoft.com/en-us/download/details.aspx?id=46078)
|
||||
* [Security Update for Windows 7 for x64-based Systems (KB3033929)](https://www.microsoft.com/en-us/download/details.aspx?id=46148)
|
||||
|
||||
## Installation
|
||||
[Follow the installation instructions](https://github.com/nefarius/ViGEm/wiki/Driver-Installation).
|
||||
51
sys/UsbPdo.h
Normal file
51
sys/UsbPdo.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
BOOLEAN USB_BUSIFFN UsbPdo_IsDeviceHighSpeed(IN PVOID BusContext);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusInformation(
|
||||
IN PVOID BusContext,
|
||||
IN ULONG Level,
|
||||
IN OUT PVOID BusInformationBuffer,
|
||||
IN OUT PULONG BusInformationBufferLength,
|
||||
OUT PULONG BusInformationActualLength
|
||||
);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_SubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb);
|
||||
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame);
|
||||
VOID USB_BUSIFFN UsbPdo_GetUSBDIVersion(
|
||||
IN PVOID BusContext,
|
||||
IN OUT PUSBD_VERSION_INFORMATION VersionInformation,
|
||||
IN OUT PULONG HcdCapabilities
|
||||
);
|
||||
NTSTATUS UsbPdo_GetDeviceDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetConfigurationDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetStringDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_SelectConfiguration(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_SelectInterface(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST Request);
|
||||
NTSTATUS UsbPdo_AbortPipe(WDFDEVICE Device);
|
||||
NTSTATUS UsbPdo_ClassInterface(PURB urb, WDFDEVICE Device, PPDO_DEVICE_DATA pCommon);
|
||||
NTSTATUS UsbPdo_GetDescriptorFromInterface(PURB urb, PPDO_DEVICE_DATA pCommon);
|
||||
50
sys/Util.h
Normal file
50
sys/Util.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Returns the current caller process id.
|
||||
//
|
||||
#define CURRENT_PROCESS_ID() ((DWORD)((DWORD_PTR)PsGetCurrentProcessId() & 0xFFFFFFFF))
|
||||
|
||||
#define IS_OWNER(_pdo_) (_pdo_->OwnerProcessId == CURRENT_PROCESS_ID())
|
||||
|
||||
//
|
||||
// Represents a MAC address.
|
||||
//
|
||||
typedef struct _MAC_ADDRESS
|
||||
{
|
||||
UCHAR Vendor0;
|
||||
UCHAR Vendor1;
|
||||
UCHAR Vendor2;
|
||||
UCHAR Nic0;
|
||||
UCHAR Nic1;
|
||||
UCHAR Nic2;
|
||||
} MAC_ADDRESS, *PMAC_ADDRESS;
|
||||
|
||||
|
||||
VOID ReverseByteArray(PUCHAR Array, INT Length);
|
||||
VOID GenerateRandomMacAddress(PMAC_ADDRESS Address);
|
||||
78
sys/ViGEmBus.inf
Normal file
78
sys/ViGEmBus.inf
Normal file
@@ -0,0 +1,78 @@
|
||||
;
|
||||
; ViGEmBus.inf
|
||||
;
|
||||
|
||||
[Version]
|
||||
Signature="$WINDOWS NT$"
|
||||
Class=System
|
||||
ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318}
|
||||
Provider=%ManufacturerName%
|
||||
CatalogFile=ViGEmBus.cat
|
||||
DriverVer= ;
|
||||
|
||||
[DestinationDirs]
|
||||
DefaultDestDir = 12
|
||||
ViGEmBus_Device_CoInstaller_CopyFiles = 11
|
||||
|
||||
; ================= Class section =====================
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DiskName%,,,""
|
||||
|
||||
[SourceDisksFiles]
|
||||
ViGEmBus.sys = 1,,
|
||||
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1
|
||||
|
||||
;*****************************************
|
||||
; Install Section
|
||||
;*****************************************
|
||||
|
||||
[Manufacturer]
|
||||
%ManufacturerName%=Standard,NT$ARCH$
|
||||
|
||||
[Standard.NT$ARCH$]
|
||||
%ViGEmBus.DeviceDesc%=ViGEmBus_Device, Root\ViGEmBus
|
||||
|
||||
[ViGEmBus_Device.NT]
|
||||
CopyFiles=Drivers_Dir
|
||||
|
||||
[Drivers_Dir]
|
||||
ViGEmBus.sys
|
||||
|
||||
;-------------- Service installation
|
||||
[ViGEmBus_Device.NT.Services]
|
||||
AddService = ViGEmBus,%SPSVCINST_ASSOCSERVICE%, ViGEmBus_Service_Inst
|
||||
|
||||
; -------------- ViGEmBus driver install sections
|
||||
[ViGEmBus_Service_Inst]
|
||||
DisplayName = %ViGEmBus.SVCDESC%
|
||||
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
|
||||
StartType = 3 ; SERVICE_DEMAND_START
|
||||
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
|
||||
ServiceBinary = %12%\ViGEmBus.sys
|
||||
|
||||
;
|
||||
;--- ViGEmBus_Device Coinstaller installation ------
|
||||
;
|
||||
|
||||
[ViGEmBus_Device.NT.CoInstallers]
|
||||
AddReg=ViGEmBus_Device_CoInstaller_AddReg
|
||||
CopyFiles=ViGEmBus_Device_CoInstaller_CopyFiles
|
||||
|
||||
[ViGEmBus_Device_CoInstaller_AddReg]
|
||||
HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller"
|
||||
|
||||
[ViGEmBus_Device_CoInstaller_CopyFiles]
|
||||
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll
|
||||
|
||||
[ViGEmBus_Device.NT.Wdf]
|
||||
KmdfService = ViGEmBus, ViGEmBus_wdfsect
|
||||
[ViGEmBus_wdfsect]
|
||||
KmdfLibraryVersion = $KMDFVERSION$
|
||||
|
||||
[Strings]
|
||||
SPSVCINST_ASSOCSERVICE= 0x00000002
|
||||
ManufacturerName="Benjamin H<>glinger-Stelzer"
|
||||
DiskName = "ViGEmBus Installation Disk"
|
||||
ViGEmBus.DeviceDesc = "Virtual Gamepad Emulation Bus"
|
||||
ViGEmBus.SVCDESC = "Virtual Gamepad Emulation Service"
|
||||
100
sys/ViGEmBus.rc
Normal file
100
sys/ViGEmBus.rc
Normal file
@@ -0,0 +1,100 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// German (Austria) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEA)
|
||||
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,14,3,0
|
||||
PRODUCTVERSION 1,14,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0xe9L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "000904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Benjamin H<>glinger-Stelzer"
|
||||
VALUE "FileDescription", "Virtual Gamepad Emulation Bus Driver"
|
||||
VALUE "FileVersion", "1.14.3.0"
|
||||
VALUE "InternalName", "Virtual Gamepad Emulation Bus Driver"
|
||||
VALUE "LegalCopyright", "Copyright (C) Benjamin H<>glinger-Stelzer 2016"
|
||||
VALUE "OriginalFilename", "vigembus.sys"
|
||||
VALUE "ProductName", "Virtual Gamepad Emulation Bus Driver"
|
||||
VALUE "ProductVersion", "1.14.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x9, 1200
|
||||
END
|
||||
END
|
||||
|
||||
#endif // German (Austria) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
||||
286
sys/ViGEmBus.vcxproj
Normal file
286
sys/ViGEmBus.vcxproj
Normal file
@@ -0,0 +1,286 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{040101B0-EE5C-4EF1-99EE-9F81C795C001}</ProjectGuid>
|
||||
<TemplateGuid>{1bc93793-694f-48fe-9372-81e2b05556fd}</TemplateGuid>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
|
||||
<RootNamespace>ViGEmBus</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
||||
<TargetVersion>Windows7</TargetVersion>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
|
||||
<ConfigurationType>Driver</ConfigurationType>
|
||||
<DriverType>KMDF</DriverType>
|
||||
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
|
||||
<KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
|
||||
<KMDF_VERSION_MINOR>9</KMDF_VERSION_MINOR>
|
||||
<ALLOW_DATE_TIME>1</ALLOW_DATE_TIME>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
|
||||
<IncludePath>$(SolutionDir)Include;$(IncludePath)</IncludePath>
|
||||
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Inf>
|
||||
<TimeStamp>1.14.3.0</TimeStamp>
|
||||
</Inf>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)ntstrsafe.lib;$(DDK_LIB_PATH)wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="ViGEmBus.inf" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FilesToPackage Include="$(TargetPath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusDriver.h" />
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusShared.h" />
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmCommon.h" />
|
||||
<ClInclude Include="busenum.h" />
|
||||
<ClInclude Include="ByteArray.h" />
|
||||
<ClInclude Include="Context.h" />
|
||||
<ClInclude Include="Ds4.h" />
|
||||
<ClInclude Include="Queue.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="UsbPdo.h" />
|
||||
<ClInclude Include="Util.h" />
|
||||
<ClInclude Include="Xgip.h" />
|
||||
<ClInclude Include="Xusb.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmBus.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="busenum.c" />
|
||||
<ClCompile Include="buspdo.c" />
|
||||
<ClCompile Include="ByteArray.c" />
|
||||
<ClCompile Include="Driver.c" />
|
||||
<ClCompile Include="Ds4.c" />
|
||||
<ClCompile Include="Queue.c" />
|
||||
<ClCompile Include="UsbPdo.c" />
|
||||
<ClCompile Include="Util.c" />
|
||||
<ClCompile Include="xgip.c" />
|
||||
<ClCompile Include="xusb.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
107
sys/ViGEmBus.vcxproj.filters
Normal file
107
sys/ViGEmBus.vcxproj.filters
Normal file
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Driver Files">
|
||||
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
|
||||
<Extensions>inf;inv;inx;mof;mc;</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Common">
|
||||
<UniqueIdentifier>{bbf85b1d-5a75-4302-af4e-46627fcf0d78}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Inf Include="ViGEmBus.inf">
|
||||
<Filter>Driver Files</Filter>
|
||||
</Inf>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="busenum.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Queue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="UsbPdo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Context.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Xusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Ds4.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Xgip.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ByteArray.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\ViGEmCommon.h">
|
||||
<Filter>Header Files\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusShared.h">
|
||||
<Filter>Header Files\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(SolutionDir)\Include\ViGEmBusDriver.h">
|
||||
<Filter>Header Files\Common</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ViGEmBus.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="busenum.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="buspdo.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xusb.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="xgip.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Queue.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Ds4.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="UsbPdo.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Util.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Driver.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ByteArray.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
106
sys/Xgip.h
Normal file
106
sys/Xgip.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// For children emulating XGIP devices, the following dummy interfaces
|
||||
// have to be exposed by the PDO or else the child devices won't start
|
||||
//
|
||||
|
||||
// {70211B0E-0AFB-47DB-AFC1-410BF842497A}
|
||||
// ReSharper disable once CppMissingIncludeGuard
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XGIP_UNKNOWN_0,
|
||||
0x70211B0E, 0x0AFB, 0x47DB, 0xAF, 0xC1, 0x41, 0x0B, 0xF8, 0x42, 0x49, 0x7A);
|
||||
|
||||
// {B38290E5-3CD0-4F9D-9937-F5FE2B44D47A}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XGIP_UNKNOWN_1,
|
||||
0xB38290E5, 0x3CD0, 0x4F9D, 0x99, 0x37, 0xF5, 0xFE, 0x2B, 0x44, 0xD4, 0x7A);
|
||||
|
||||
// {2AEB0243-6A6E-486B-82FC-D815F6B97006}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XGIP_UNKNOWN_2,
|
||||
0x2AEB0243, 0x6A6E, 0x486B, 0x82, 0xFC, 0xD8, 0x15, 0xF6, 0xB9, 0x70, 0x06);
|
||||
|
||||
// {DC7A8E51-49B3-4A3A-9E81-625205E7D729}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XGIP_UNKNOWN_3,
|
||||
0xDC7A8E51, 0x49B3, 0x4A3A, 0x9E, 0x81, 0x62, 0x52, 0x05, 0xE7, 0xD7, 0x29);
|
||||
|
||||
// {DEEE98EA-C0A1-42C3-9738-A04606C84E93}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XGIP_UNKNOWN_4,
|
||||
0xDEEE98EA, 0xC0A1, 0x42C3, 0x97, 0x38, 0xA0, 0x46, 0x06, 0xC8, 0x4E, 0x93);
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#define XGIP_DESCRIPTOR_SIZE 0x0040
|
||||
#define XGIP_CONFIGURATION_SIZE 0x88
|
||||
#define XGIP_REPORT_SIZE 0x12
|
||||
#define XGIP_SYS_INIT_PACKETS 0x0F
|
||||
#define XGIP_SYS_INIT_PERIOD 0x32
|
||||
|
||||
typedef struct _XGIP_DEVICE_DATA
|
||||
{
|
||||
UCHAR Report[XGIP_REPORT_SIZE];
|
||||
|
||||
//
|
||||
// Queue for incoming interrupt transfer
|
||||
//
|
||||
WDFQUEUE PendingUsbInRequests;
|
||||
|
||||
//
|
||||
// Queue for inverted calls
|
||||
//
|
||||
WDFQUEUE PendingNotificationRequests;
|
||||
|
||||
WDFCOLLECTION XboxgipSysInitCollection;
|
||||
|
||||
BOOLEAN XboxgipSysInitReady;
|
||||
|
||||
WDFTIMER XboxgipSysInitTimer;
|
||||
} XGIP_DEVICE_DATA, *PXGIP_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XGIP_DEVICE_DATA, XgipGetData)
|
||||
|
||||
|
||||
NTSTATUS
|
||||
Bus_XgipSubmitInterrupt(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PXGIP_SUBMIT_INTERRUPT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
//
|
||||
// XGIP-specific functions
|
||||
//
|
||||
NTSTATUS Xgip_PreparePdo(
|
||||
PWDFDEVICE_INIT DeviceInit,
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription
|
||||
);
|
||||
NTSTATUS Xgip_PrepareHardware(WDFDEVICE Device);
|
||||
NTSTATUS Xgip_AssignPdoContext(WDFDEVICE Device);
|
||||
VOID Xgip_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length);
|
||||
VOID Xgip_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon);
|
||||
VOID Xgip_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo);
|
||||
|
||||
126
sys/Xusb.h
Normal file
126
sys/Xusb.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// For children emulating XUSB devices, the following dummy interfaces
|
||||
// have to be exposed by the PDO or else the child devices won't start
|
||||
//
|
||||
|
||||
// {70211B0E-0AFB-47DB-AFC1-410BF842497A}
|
||||
// ReSharper disable once CppMissingIncludeGuard
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XUSB_UNKNOWN_0,
|
||||
0x70211B0E, 0x0AFB, 0x47DB, 0xAF, 0xC1, 0x41, 0x0B, 0xF8, 0x42, 0x49, 0x7A);
|
||||
|
||||
// {B38290E5-3CD0-4F9D-9937-F5FE2B44D47A}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XUSB_UNKNOWN_1,
|
||||
0xB38290E5, 0x3CD0, 0x4F9D, 0x99, 0x37, 0xF5, 0xFE, 0x2B, 0x44, 0xD4, 0x7A);
|
||||
|
||||
// {2AEB0243-6A6E-486B-82FC-D815F6B97006}
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_XUSB_UNKNOWN_2,
|
||||
0x2AEB0243, 0x6A6E, 0x486B, 0x82, 0xFC, 0xD8, 0x15, 0xF6, 0xB9, 0x70, 0x06);
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_X86_)
|
||||
#define XUSB_CONFIGURATION_SIZE 0x00E4
|
||||
#else
|
||||
#define XUSB_CONFIGURATION_SIZE 0x0130
|
||||
#endif
|
||||
#define XUSB_DESCRIPTOR_SIZE 0x0099
|
||||
#define XUSB_RUMBLE_SIZE 0x08
|
||||
#define XUSB_LEDSET_SIZE 0x03
|
||||
#define XUSB_LEDNUM_SIZE 0x01
|
||||
#define XUSB_INIT_STAGE_SIZE 0x03
|
||||
|
||||
#define XUSB_IS_DATA_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0081))
|
||||
#define XUSB_IS_CONTROL_PIPE(_x_) ((BOOLEAN)(_x_->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0083))
|
||||
|
||||
typedef struct _XUSB_INTERRUPT_IN_PACKET
|
||||
{
|
||||
UCHAR Id;
|
||||
|
||||
UCHAR Size;
|
||||
|
||||
XUSB_REPORT Report;
|
||||
|
||||
} XUSB_INTERRUPT_IN_PACKET, *PXUSB_INTERRUPT_IN_PACKET;
|
||||
|
||||
//
|
||||
// XUSB-specific device context data.
|
||||
//
|
||||
typedef struct _XUSB_DEVICE_DATA
|
||||
{
|
||||
//
|
||||
// Rumble buffer
|
||||
//
|
||||
UCHAR Rumble[XUSB_RUMBLE_SIZE];
|
||||
|
||||
//
|
||||
// LED number (represents XInput slot index)
|
||||
//
|
||||
UCHAR LedNumber;
|
||||
|
||||
//
|
||||
// Report packet
|
||||
//
|
||||
XUSB_INTERRUPT_IN_PACKET Packet;
|
||||
|
||||
//
|
||||
// Queue for incoming control interrupt transfer
|
||||
//
|
||||
WDFQUEUE HoldingUsbInRequests;
|
||||
|
||||
//
|
||||
// Required for XInputGetCapabilities to work
|
||||
//
|
||||
BOOLEAN ReportedCapabilities;
|
||||
|
||||
//
|
||||
// Required for XInputGetCapabilities to work
|
||||
//
|
||||
ULONG InterruptInitStage;
|
||||
|
||||
} XUSB_DEVICE_DATA, *PXUSB_DEVICE_DATA;
|
||||
|
||||
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(XUSB_DEVICE_DATA, XusbGetData)
|
||||
|
||||
|
||||
NTSTATUS
|
||||
Bus_XusbSubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PXUSB_SUBMIT_REPORT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
//
|
||||
// XUSB-specific functions
|
||||
//
|
||||
NTSTATUS Xusb_PreparePdo(PWDFDEVICE_INIT DeviceInit, USHORT VendorId, USHORT ProductId, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription);
|
||||
NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device);
|
||||
NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description);
|
||||
VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length);
|
||||
VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon);
|
||||
VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo);
|
||||
638
sys/busenum.c
Normal file
638
sys/busenum.c
Normal file
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmguid.h>
|
||||
#include <usb.h>
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text (PAGE, Bus_PlugInDevice)
|
||||
#pragma alloc_text (PAGE, Bus_UnPlugDevice)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Simulates a device plug-in event.
|
||||
//
|
||||
NTSTATUS Bus_PlugInDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
NTSTATUS status;
|
||||
PVIGEM_PLUGIN_TARGET plugIn;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData;
|
||||
size_t length = 0;
|
||||
WDF_OBJECT_ATTRIBUTES requestAttribs;
|
||||
PFDO_PLUGIN_REQUEST_DATA pReqData;
|
||||
PFDO_DEVICE_DATA pFdoData;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
|
||||
pFdoData = FdoGetData(Device);
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_PLUGIN_TARGET), (PVOID)&plugIn, &length);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_PLUGIN_TARGET) != plugIn->Size) || (length != plugIn->Size))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Input buffer size mismatch"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (plugIn->SerialNo == 0)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Serial no. 0 not allowed"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "File object associated with request is null"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "File object context associated with request is null"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the description with the information about the newly
|
||||
// plugged in device.
|
||||
//
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = plugIn->SerialNo;
|
||||
description.TargetType = plugIn->TargetType;
|
||||
description.OwnerProcessId = CURRENT_PROCESS_ID();
|
||||
description.SessionId = pFileData->SessionId;
|
||||
description.OwnerIsDriver = IsInternal;
|
||||
|
||||
// Set default IDs if supplied values are invalid
|
||||
if (plugIn->VendorId == 0 || plugIn->ProductId == 0)
|
||||
{
|
||||
switch (plugIn->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
description.VendorId = 0x045E;
|
||||
description.ProductId = 0x028E;
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
description.VendorId = 0x054C;
|
||||
description.ProductId = 0x05C4;
|
||||
|
||||
break;
|
||||
case XboxOneWired:
|
||||
|
||||
description.VendorId = 0x0E6F;
|
||||
description.ProductId = 0x0139;
|
||||
|
||||
#if !DBG
|
||||
// TODO: implement and remove!
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
description.VendorId = plugIn->VendorId;
|
||||
description.ProductId = plugIn->ProductId;
|
||||
}
|
||||
|
||||
WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock);
|
||||
|
||||
KdPrint((DRIVERNAME "Items count: %d\n", WdfCollectionGetCount(pFdoData->PendingPluginRequests)));
|
||||
|
||||
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(WdfFdoGetDefaultChildList(Device), &description.Header, NULL);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfChildListAddOrUpdateChildDescriptionAsPresent failed with 0x%X\n", status));
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
if (status == STATUS_OBJECT_NAME_EXISTS)
|
||||
{
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&requestAttribs, FDO_PLUGIN_REQUEST_DATA);
|
||||
|
||||
//
|
||||
// Allocate context data to request
|
||||
//
|
||||
status = WdfObjectAllocateContext(Request, &requestAttribs, (PVOID)&pReqData);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfCollectionAdd failed with 0x%X\n", status));
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
//
|
||||
// Glue current serial to request
|
||||
//
|
||||
pReqData->Serial = plugIn->SerialNo;
|
||||
|
||||
//
|
||||
// Keep track of pending request in collection
|
||||
//
|
||||
status = WdfCollectionAdd(pFdoData->PendingPluginRequests, Request);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfCollectionAdd failed with 0x%X\n", status));
|
||||
|
||||
goto pluginEnd;
|
||||
}
|
||||
|
||||
KdPrint((DRIVERNAME "Added item with serial: %d\n", plugIn->SerialNo));
|
||||
|
||||
status = NT_SUCCESS(status) ? STATUS_PENDING : status;
|
||||
|
||||
pluginEnd:
|
||||
|
||||
WdfSpinLockRelease(pFdoData->PendingPluginRequestsLock);
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Simulates a device unplug event.
|
||||
//
|
||||
NTSTATUS Bus_UnPlugDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFDEVICE hChild;
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_LIST_ITERATOR iterator;
|
||||
WDF_CHILD_RETRIEVE_INFO childInfo;
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
BOOLEAN unplugAll;
|
||||
PVIGEM_UNPLUG_TARGET unPlug;
|
||||
WDFFILEOBJECT fileObject;
|
||||
PFDO_FILE_DATA pFileData = NULL;
|
||||
size_t length = 0;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
KdPrint((DRIVERNAME "Entered Bus_UnPlugDevice\n"));
|
||||
|
||||
status = WdfRequestRetrieveInputBuffer(Request, sizeof(VIGEM_UNPLUG_TARGET), (PVOID)&unPlug, &length);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_UnPlugDevice: WdfRequestRetrieveInputBuffer failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((sizeof(VIGEM_UNPLUG_TARGET) != unPlug->Size) || (length != unPlug->Size))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_UnPlugDevice: Input buffer size mismatch"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*Transferred = length;
|
||||
unplugAll = (unPlug->SerialNo == 0);
|
||||
|
||||
if (!IsInternal)
|
||||
{
|
||||
fileObject = WdfRequestGetFileObject(Request);
|
||||
if (fileObject == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_UnPlugDevice: File object associated with request is null"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pFileData = FileObjectGetData(fileObject);
|
||||
if (pFileData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_UnPlugDevice: File object context associated with request is null"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
list = WdfFdoGetDefaultChildList(Device);
|
||||
|
||||
WDF_CHILD_LIST_ITERATOR_INIT(&iterator, WdfRetrievePresentChildren);
|
||||
|
||||
WdfChildListBeginIteration(list, &iterator);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&childInfo, &description.Header);
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
status = WdfChildListRetrieveNextDevice(list, &iterator, &hChild, &childInfo);
|
||||
|
||||
// Error or no more children, end loop
|
||||
if (!NT_SUCCESS(status) || status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// If unable to retrieve device
|
||||
if (childInfo.Status != WdfChildListRetrieveDeviceSuccess)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Child isn't the one we looked for, skip
|
||||
if (!unplugAll && description.SerialNo != unPlug->SerialNo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only unplug owned children
|
||||
if (IsInternal || description.SessionId == pFileData->SessionId)
|
||||
{
|
||||
// Unplug child
|
||||
status = WdfChildListUpdateChildDescriptionAsMissing(list, &description.Header);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_UnPlugDevice: WdfChildListUpdateChildDescriptionAsMissing failed with status 0x%X\n", status));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WdfChildListEndIteration(list, &iterator);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to an XUSB PDO.
|
||||
//
|
||||
NTSTATUS Bus_XusbSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXUSB_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
//
|
||||
// Queues an inverted call to receive XUSB-specific updates.
|
||||
//
|
||||
NTSTATUS Bus_QueueNotification(WDFDEVICE Device, ULONG SerialNo, WDFREQUEST Request)
|
||||
{
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE hChild;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
PXUSB_DEVICE_DATA xusbData;
|
||||
PDS4_DEVICE_DATA ds4Data;
|
||||
|
||||
|
||||
KdPrint((DRIVERNAME "Entered Bus_QueueNotification\n"));
|
||||
|
||||
hChild = Bus_GetPdo(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (hChild == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_QueueNotification: PDO with serial %d not found\n", SerialNo));
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
// Check common context
|
||||
pdoData = PdoGetData(hChild);
|
||||
if (pdoData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_QueueNotification: PDO context not found\n"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Check if caller owns this PDO
|
||||
if (!IS_OWNER(pdoData))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_QueueNotification: PID mismatch: %d != %d\n", pdoData->OwnerProcessId, CURRENT_PROCESS_ID()));
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Queue the request for later completion by the PDO and return STATUS_PENDING
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
xusbData = XusbGetData(hChild);
|
||||
|
||||
if (xusbData == NULL) break;
|
||||
|
||||
status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
ds4Data = Ds4GetData(hChild);
|
||||
|
||||
if (ds4Data == NULL) break;
|
||||
|
||||
status = WdfRequestForwardToIoQueue(Request, pdoData->PendingNotificationRequests);
|
||||
|
||||
break;
|
||||
default:
|
||||
status = STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfRequestForwardToIoQueue failed with status 0x%X\n", status));
|
||||
}
|
||||
|
||||
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
||||
}
|
||||
|
||||
//
|
||||
// Sends a report update to a DS4 PDO.
|
||||
//
|
||||
NTSTATUS Bus_Ds4SubmitReport(WDFDEVICE Device, ULONG SerialNo, PDS4_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
NTSTATUS Bus_XgipSubmitReport(WDFDEVICE Device, ULONG SerialNo, PXGIP_SUBMIT_REPORT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
NTSTATUS Bus_XgipSubmitInterrupt(WDFDEVICE Device, ULONG SerialNo, PXGIP_SUBMIT_INTERRUPT Report, BOOLEAN FromInterface)
|
||||
{
|
||||
return Bus_SubmitReport(Device, SerialNo, Report, FromInterface);
|
||||
}
|
||||
|
||||
WDFDEVICE Bus_GetPdo(IN WDFDEVICE Device, IN ULONG SerialNo)
|
||||
{
|
||||
WDFCHILDLIST list;
|
||||
WDF_CHILD_RETRIEVE_INFO info;
|
||||
|
||||
list = WdfFdoGetDefaultChildList(Device);
|
||||
|
||||
PDO_IDENTIFICATION_DESCRIPTION description;
|
||||
|
||||
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(&description.Header, sizeof(description));
|
||||
|
||||
description.SerialNo = SerialNo;
|
||||
|
||||
WDF_CHILD_RETRIEVE_INFO_INIT(&info, &description.Header);
|
||||
|
||||
return WdfChildListRetrievePdo(list, &info);
|
||||
}
|
||||
|
||||
NTSTATUS Bus_SubmitReport(WDFDEVICE Device, ULONG SerialNo, PVOID Report, BOOLEAN FromInterface)
|
||||
{
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
WDFDEVICE hChild;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
WDFREQUEST usbRequest;
|
||||
PIRP pendingIrp;
|
||||
BOOLEAN changed;
|
||||
|
||||
|
||||
KdPrint((DRIVERNAME "Entered Bus_SubmitReport\n"));
|
||||
|
||||
hChild = Bus_GetPdo(Device, SerialNo);
|
||||
|
||||
// Validate child
|
||||
if (hChild == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_SubmitReport: PDO with serial %d not found\n", SerialNo));
|
||||
return STATUS_NO_SUCH_DEVICE;
|
||||
}
|
||||
|
||||
// Check common context
|
||||
pdoData = PdoGetData(hChild);
|
||||
if (pdoData == NULL)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_SubmitReport: PDO context not found\n"));
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// Check if caller owns this PDO
|
||||
if (!FromInterface && !IS_OWNER(pdoData))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Bus_SubmitReport: PID mismatch: %d != %d\n", pdoData->OwnerProcessId, CURRENT_PROCESS_ID()));
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Check if input is different from previous value
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
changed = (RtlCompareMemory(&XusbGetData(hChild)->Packet.Report,
|
||||
&((PXUSB_SUBMIT_REPORT)Report)->Report,
|
||||
sizeof(XUSB_REPORT)) != sizeof(XUSB_REPORT));
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
changed = TRUE;
|
||||
|
||||
break;
|
||||
case XboxOneWired:
|
||||
|
||||
// TODO: necessary?
|
||||
changed = TRUE;
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
changed = FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't waste pending IRP if input hasn't changed
|
||||
if (!changed)
|
||||
return status;
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_SubmitReport: received new report\n"));
|
||||
|
||||
// Get pending USB request
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
break;
|
||||
case XboxOneWired:
|
||||
|
||||
// Request is control data
|
||||
if (((PXGIP_SUBMIT_INTERRUPT)Report)->Size == sizeof(XGIP_SUBMIT_INTERRUPT))
|
||||
{
|
||||
PXGIP_DEVICE_DATA xgip = XgipGetData(hChild);
|
||||
PXGIP_SUBMIT_INTERRUPT interrupt = (PXGIP_SUBMIT_INTERRUPT)Report;
|
||||
WDFMEMORY memory;
|
||||
WDF_OBJECT_ATTRIBUTES memAttribs;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&memAttribs);
|
||||
|
||||
memAttribs.ParentObject = hChild;
|
||||
|
||||
// Allocate kernel memory
|
||||
status = WdfMemoryCreate(&memAttribs, NonPagedPool, VIGEM_POOL_TAG,
|
||||
interrupt->InterruptLength, &memory, NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfMemoryCreate failed with status 0x%X\n", status));
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
// Copy interrupt buffer to memory object
|
||||
status = WdfMemoryCopyFromBuffer(memory, 0, interrupt->Interrupt, interrupt->InterruptLength);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfMemoryCopyFromBuffer failed with status 0x%X\n", status));
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
// Add memory object to collection
|
||||
status = WdfCollectionAdd(xgip->XboxgipSysInitCollection, memory);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfCollectionAdd failed with status 0x%X\n", status));
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
// Check if all packets have been received
|
||||
xgip->XboxgipSysInitReady =
|
||||
WdfCollectionGetCount(xgip->XboxgipSysInitCollection) == XGIP_SYS_INIT_PACKETS;
|
||||
|
||||
// If all packets are cached, start initialization timer
|
||||
if (xgip->XboxgipSysInitReady)
|
||||
{
|
||||
WdfTimerStart(xgip->XboxgipSysInitTimer, XGIP_SYS_INIT_PERIOD);
|
||||
}
|
||||
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
status = WdfIoQueueRetrieveNextRequest(XgipGetData(hChild)->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
status = STATUS_NOT_SUPPORTED;
|
||||
goto endSubmitReport;
|
||||
}
|
||||
|
||||
if (status == STATUS_PENDING)
|
||||
goto endSubmitReport;
|
||||
else if (!NT_SUCCESS(status))
|
||||
goto endSubmitReport;
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_SubmitReport: pending IRP found\n"));
|
||||
|
||||
// Get pending IRP
|
||||
pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = (PURB)URB_FROM_IRP(pendingIrp);
|
||||
|
||||
// Get transfer buffer
|
||||
PUCHAR Buffer = (PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer;
|
||||
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET);
|
||||
|
||||
// Copy submitted report to cache
|
||||
RtlCopyBytes(&XusbGetData(hChild)->Packet.Report, &((PXUSB_SUBMIT_REPORT)Report)->Report, sizeof(XUSB_REPORT));
|
||||
// Copy cached report to URB transfer buffer
|
||||
RtlCopyBytes(Buffer, &XusbGetData(hChild)->Packet, sizeof(XUSB_INTERRUPT_IN_PACKET));
|
||||
|
||||
break;
|
||||
case DualShock4Wired:
|
||||
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = DS4_REPORT_SIZE;
|
||||
|
||||
/* Copy report to cache and transfer buffer
|
||||
* Skip first byte as it contains the never changing report id */
|
||||
RtlCopyBytes(Ds4GetData(hChild)->Report + 1, &((PDS4_SUBMIT_REPORT)Report)->Report, sizeof(DS4_REPORT));
|
||||
|
||||
if (Buffer)
|
||||
RtlCopyBytes(Buffer, Ds4GetData(hChild)->Report, DS4_REPORT_SIZE);
|
||||
|
||||
break;
|
||||
case XboxOneWired:
|
||||
|
||||
// Request is input report
|
||||
if (((PXGIP_SUBMIT_REPORT)Report)->Size == sizeof(XGIP_SUBMIT_REPORT))
|
||||
{
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = XGIP_REPORT_SIZE;
|
||||
|
||||
// Increase event counter on every call (can roll-over)
|
||||
XgipGetData(hChild)->Report[2]++;
|
||||
|
||||
/* Copy report to cache and transfer buffer
|
||||
* Skip first four bytes as they are not part of the report */
|
||||
RtlCopyBytes(XgipGetData(hChild)->Report + 4, &((PXGIP_SUBMIT_REPORT)Report)->Report, sizeof(XGIP_REPORT));
|
||||
RtlCopyBytes(Buffer, XgipGetData(hChild)->Report, XGIP_REPORT_SIZE);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
|
||||
endSubmitReport:
|
||||
return status;
|
||||
}
|
||||
|
||||
166
sys/busenum.h
Normal file
166
sys/busenum.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ntddk.h>
|
||||
#include <wdf.h>
|
||||
#define NTSTRSAFE_LIB
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntintsafe.h>
|
||||
#include <initguid.h>
|
||||
#include "ViGEmBusDriver.h"
|
||||
#include "ViGEmBusShared.h"
|
||||
#include "Queue.h"
|
||||
#include <usb.h>
|
||||
#include <usbbusif.h>
|
||||
#include "Context.h"
|
||||
#include "Util.h"
|
||||
#include "UsbPdo.h"
|
||||
#include "Xusb.h"
|
||||
#include "Ds4.h"
|
||||
#include "Xgip.h"
|
||||
|
||||
|
||||
#pragma region Macros
|
||||
|
||||
#define MAX_INSTANCE_ID_LEN 80
|
||||
#define HID_LANGUAGE_ID_LENGTH 0x04
|
||||
|
||||
#define HID_REQUEST_GET_REPORT 0x01
|
||||
#define HID_REQUEST_SET_REPORT 0x09
|
||||
#define HID_REPORT_TYPE_FEATURE 0x03
|
||||
|
||||
#define VIGEM_POOL_TAG 0x45476956 // "EGiV"
|
||||
#define DRIVERNAME "ViGEm: "
|
||||
#define MAX_HARDWARE_ID_LENGTH 0xFF
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Helpers
|
||||
|
||||
//
|
||||
// Extracts the HID Report ID from the supplied class request.
|
||||
//
|
||||
#define HID_GET_REPORT_ID(_req_) ((_req_->Value) & 0xFF)
|
||||
|
||||
//
|
||||
// Extracts the HID Report type from the supplied class request.
|
||||
//
|
||||
#define HID_GET_REPORT_TYPE(_req_) ((_req_->Value >> 8) & 0xFF)
|
||||
|
||||
//
|
||||
// Some insane macro-magic =3
|
||||
//
|
||||
#define P99_PROTECT(...) __VA_ARGS__
|
||||
#define COPY_BYTE_ARRAY(_dst_, _bytes_) do {BYTE b[] = _bytes_; \
|
||||
RtlCopyMemory(_dst_, b, RTL_NUMBER_OF_V1(b)); } while (0)
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#pragma region WDF callback prototypes
|
||||
|
||||
DRIVER_INITIALIZE DriverEntry;
|
||||
|
||||
EVT_WDF_DRIVER_DEVICE_ADD Bus_EvtDeviceAdd;
|
||||
EVT_WDF_DEVICE_FILE_CREATE Bus_DeviceFileCreate;
|
||||
EVT_WDF_FILE_CLOSE Bus_FileClose;
|
||||
|
||||
EVT_WDF_CHILD_LIST_CREATE_DEVICE Bus_EvtDeviceListCreatePdo;
|
||||
|
||||
EVT_WDF_CHILD_LIST_IDENTIFICATION_DESCRIPTION_COMPARE Bus_EvtChildListIdentificationDescriptionCompare;
|
||||
|
||||
EVT_WDF_DEVICE_PREPARE_HARDWARE Bus_EvtDevicePrepareHardware;
|
||||
|
||||
EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL Pdo_EvtIoInternalDeviceControl;
|
||||
|
||||
EVT_WDF_TIMER Xgip_SysInitTimerFunc;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Bus enumeration-specific functions
|
||||
|
||||
NTSTATUS
|
||||
Bus_PlugInDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_UnPlugDevice(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ BOOLEAN IsInternal,
|
||||
_Out_ size_t* Transferred
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_CreatePdo(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ PWDFDEVICE_INIT ChildInit,
|
||||
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_QueueNotification(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
WDFREQUEST Request
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_XgipSubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PXGIP_SUBMIT_REPORT Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
Bus_SubmitReport(
|
||||
WDFDEVICE Device,
|
||||
ULONG SerialNo,
|
||||
PVOID Report,
|
||||
_In_ BOOLEAN FromInterface
|
||||
);
|
||||
|
||||
WDFDEVICE
|
||||
Bus_GetPdo(
|
||||
IN WDFDEVICE Device,
|
||||
IN ULONG SerialNo);
|
||||
|
||||
VOID
|
||||
Bus_PdoStageResult(
|
||||
_In_ PINTERFACE InterfaceHeader,
|
||||
_In_ VIGEM_PDO_STAGE Stage,
|
||||
_In_ ULONG Serial,
|
||||
_In_ NTSTATUS Status
|
||||
);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
755
sys/buspdo.c
Normal file
755
sys/buspdo.c
Normal file
@@ -0,0 +1,755 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
#include <wdmsec.h>
|
||||
#include <usbioctl.h>
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(PAGE, Bus_CreatePdo)
|
||||
#pragma alloc_text(PAGE, Bus_EvtDeviceListCreatePdo)
|
||||
#pragma alloc_text(PAGE, Bus_EvtDevicePrepareHardware)
|
||||
#endif
|
||||
|
||||
NTSTATUS Bus_EvtDeviceListCreatePdo(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
|
||||
PWDFDEVICE_INIT ChildInit)
|
||||
{
|
||||
PPDO_IDENTIFICATION_DESCRIPTION pDesc;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
pDesc = CONTAINING_RECORD(IdentificationDescription, PDO_IDENTIFICATION_DESCRIPTION, Header);
|
||||
|
||||
return Bus_CreatePdo(WdfChildListGetDevice(DeviceList), ChildInit, pDesc);
|
||||
}
|
||||
|
||||
//
|
||||
// Compares two children on the bus based on their serial numbers.
|
||||
//
|
||||
BOOLEAN Bus_EvtChildListIdentificationDescriptionCompare(
|
||||
WDFCHILDLIST DeviceList,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
|
||||
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription)
|
||||
{
|
||||
PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs;
|
||||
|
||||
UNREFERENCED_PARAMETER(DeviceList);
|
||||
|
||||
lhs = CONTAINING_RECORD(FirstIdentificationDescription,
|
||||
PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
rhs = CONTAINING_RECORD(SecondIdentificationDescription,
|
||||
PDO_IDENTIFICATION_DESCRIPTION,
|
||||
Header);
|
||||
|
||||
return (lhs->SerialNo == rhs->SerialNo) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Creates and initializes a PDO (child).
|
||||
//
|
||||
NTSTATUS Bus_CreatePdo(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ PWDFDEVICE_INIT DeviceInit,
|
||||
_In_ PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
{
|
||||
NTSTATUS status;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
WDFDEVICE hChild = NULL;
|
||||
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
|
||||
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
|
||||
WDF_OBJECT_ATTRIBUTES pdoAttributes;
|
||||
WDF_IO_QUEUE_CONFIG defaultPdoQueueConfig;
|
||||
WDFQUEUE defaultPdoQueue;
|
||||
UNICODE_STRING deviceDescription;
|
||||
VIGEM_BUS_INTERFACE busInterface;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
WDF_IO_QUEUE_CONFIG usbInQueueConfig;
|
||||
WDF_IO_QUEUE_CONFIG notificationsQueueConfig;
|
||||
|
||||
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"Virtual Gamepad Emulation Bus");
|
||||
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN);
|
||||
// reserve space for device id
|
||||
DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN);
|
||||
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
|
||||
KdPrint((DRIVERNAME "Entered Bus_CreatePdo\n"));
|
||||
|
||||
//
|
||||
// Get the FDO interface ASAP to report progress to bus
|
||||
//
|
||||
status = WdfFdoQueryForInterface(Device,
|
||||
&GUID_VIGEM_INTERFACE_PDO,
|
||||
(PINTERFACE)&busInterface,
|
||||
sizeof(VIGEM_BUS_INTERFACE),
|
||||
VIGEM_BUS_INTERFACE_VERSION,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfFdoQueryForInterface failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
// set device type
|
||||
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
|
||||
// Bus is power policy owner
|
||||
WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);
|
||||
|
||||
#pragma region Enter RAW device mode
|
||||
|
||||
status = WdfPdoInitAssignRawDevice(DeviceInit, &GUID_DEVCLASS_VIGEM_RAWPDO);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfPdoInitAssignRawDevice failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
WdfDeviceInitSetCharacteristics(DeviceInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
|
||||
|
||||
status = WdfDeviceInitAssignSDDLString(DeviceInit, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceInitAssignSDDLString failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Prepare PDO
|
||||
|
||||
// set parameters matching desired target device
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
//
|
||||
// A Xbox 360 device was requested
|
||||
//
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_PreparePdo(
|
||||
DeviceInit,
|
||||
Description->VendorId,
|
||||
Description->ProductId,
|
||||
&deviceId,
|
||||
&deviceDescription);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
// A Sony DualShock 4 device was requested
|
||||
//
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_PreparePdo(DeviceInit, &deviceId, &deviceDescription);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
break;
|
||||
|
||||
//
|
||||
// A Xbox One device was requested
|
||||
//
|
||||
case XboxOneWired:
|
||||
|
||||
status = Xgip_PreparePdo(DeviceInit, &deviceId, &deviceDescription);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
KdPrint((DRIVERNAME "Unsupported target type\n"));
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// set device id
|
||||
status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
// prepare instance id
|
||||
status = RtlUnicodeStringPrintf(&buffer, L"%02d", Description->SerialNo);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
// set instance id
|
||||
status = WdfPdoInitAssignInstanceID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
// set device description (for English operating systems)
|
||||
status = WdfPdoInitAddDeviceText(DeviceInit, &deviceDescription, &deviceLocation, 0x409);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
// default locale is English
|
||||
// TODO: add more locales
|
||||
WdfPdoInitSetDefaultLocale(DeviceInit, 0x409);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region PNP/Power event callbacks
|
||||
|
||||
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
|
||||
|
||||
pnpPowerCallbacks.EvtDevicePrepareHardware = Bus_EvtDevicePrepareHardware;
|
||||
|
||||
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
// NOTE: not utilized at the moment
|
||||
WdfPdoInitAllowForwardingRequestToParent(DeviceInit);
|
||||
|
||||
#pragma region Create PDO
|
||||
|
||||
// Add common device data context
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_DEVICE_DATA);
|
||||
|
||||
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild);
|
||||
if (!NT_SUCCESS(status))
|
||||
goto endCreatePdo;
|
||||
|
||||
KdPrint((DRIVERNAME "Created PDO: 0x%X\n", hChild));
|
||||
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
// Add XUSB-specific device data context
|
||||
case Xbox360Wired:
|
||||
{
|
||||
PXUSB_DEVICE_DATA xusbData = NULL;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XUSB_DEVICE_DATA);
|
||||
|
||||
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xusbData);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DualShock4Wired:
|
||||
{
|
||||
PDS4_DEVICE_DATA ds4Data = NULL;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, DS4_DEVICE_DATA);
|
||||
|
||||
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&ds4Data);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case XboxOneWired:
|
||||
{
|
||||
PXGIP_DEVICE_DATA xgipData = NULL;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, XGIP_DEVICE_DATA);
|
||||
|
||||
status = WdfObjectAllocateContext(hChild, &pdoAttributes, (PVOID)&xgipData);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfObjectAllocateContext failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Expose USB Interface
|
||||
|
||||
status = WdfDeviceCreateDeviceInterface(Device, (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE, NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceCreateDeviceInterface failed status 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Set PDO contexts
|
||||
|
||||
pdoData = PdoGetData(hChild);
|
||||
|
||||
pdoData->BusInterface = busInterface;
|
||||
|
||||
pdoData->SerialNo = Description->SerialNo;
|
||||
pdoData->TargetType = Description->TargetType;
|
||||
pdoData->OwnerProcessId = Description->OwnerProcessId;
|
||||
pdoData->VendorId = Description->VendorId;
|
||||
pdoData->ProductId = Description->ProductId;
|
||||
|
||||
// Initialize additional contexts (if available)
|
||||
switch (Description->TargetType)
|
||||
{
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_AssignPdoContext(hChild, Description);
|
||||
|
||||
break;
|
||||
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_AssignPdoContext(hChild, Description);
|
||||
|
||||
break;
|
||||
|
||||
case XboxOneWired:
|
||||
|
||||
status = Xgip_AssignPdoContext(hChild);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Couldn't initialize additional contexts\n"));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Create Queues & Locks
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = hChild;
|
||||
|
||||
// Create and assign queue for incoming interrupt transfer
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&usbInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, &usbInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingUsbInRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate (PendingUsbInRequests) failed 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
// Create and assign queue for user-land notification requests
|
||||
WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pdoData->PendingNotificationRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate (PendingNotificationRequests) failed 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Default I/O queue setup
|
||||
|
||||
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&defaultPdoQueueConfig, WdfIoQueueDispatchParallel);
|
||||
|
||||
defaultPdoQueueConfig.EvtIoInternalDeviceControl = Pdo_EvtIoInternalDeviceControl;
|
||||
|
||||
status = WdfIoQueueCreate(hChild, &defaultPdoQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &defaultPdoQueue);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate failed 0x%x\n", status));
|
||||
goto endCreatePdo;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region PNP capabilities
|
||||
|
||||
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
|
||||
|
||||
pnpCaps.Removable = WdfTrue;
|
||||
pnpCaps.EjectSupported = WdfTrue;
|
||||
pnpCaps.SurpriseRemovalOK = WdfTrue;
|
||||
|
||||
pnpCaps.Address = Description->SerialNo;
|
||||
pnpCaps.UINumber = Description->SerialNo;
|
||||
|
||||
WdfDeviceSetPnpCapabilities(hChild, &pnpCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Power capabilities
|
||||
|
||||
WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps);
|
||||
|
||||
powerCaps.DeviceD1 = WdfTrue;
|
||||
powerCaps.WakeFromD1 = WdfTrue;
|
||||
powerCaps.DeviceWake = PowerDeviceD1;
|
||||
|
||||
powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
|
||||
powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
|
||||
powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
|
||||
powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
|
||||
|
||||
WdfDeviceSetPowerCapabilities(hChild, &powerCaps);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
endCreatePdo:
|
||||
KdPrint((DRIVERNAME "BUS_PDO_REPORT_STAGE_RESULT Stage: ViGEmPdoCreate, Serial: 0x%X, Status: 0x%X (%d)\n",
|
||||
Description->SerialNo, status, NT_SUCCESS(status)));
|
||||
|
||||
BUS_PDO_REPORT_STAGE_RESULT(busInterface, ViGEmPdoCreate, Description->SerialNo, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Exposes necessary interfaces on PDO power-up.
|
||||
//
|
||||
NTSTATUS Bus_EvtDevicePrepareHardware(
|
||||
_In_ WDFDEVICE Device,
|
||||
_In_ WDFCMRESLIST ResourcesRaw,
|
||||
_In_ WDFCMRESLIST ResourcesTranslated
|
||||
)
|
||||
{
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
UNREFERENCED_PARAMETER(ResourcesRaw);
|
||||
UNREFERENCED_PARAMETER(ResourcesTranslated);
|
||||
|
||||
KdPrint((DRIVERNAME "Bus_EvtDevicePrepareHardware: 0x%p\n", Device));
|
||||
|
||||
pdoData = PdoGetData(Device);
|
||||
|
||||
switch (pdoData->TargetType)
|
||||
{
|
||||
// Expose XUSB interfaces
|
||||
case Xbox360Wired:
|
||||
|
||||
status = Xusb_PrepareHardware(Device);
|
||||
|
||||
break;
|
||||
|
||||
case DualShock4Wired:
|
||||
|
||||
status = Ds4_PrepareHardware(Device);
|
||||
|
||||
break;
|
||||
|
||||
case XboxOneWired:
|
||||
|
||||
status = Xgip_PrepareHardware(Device);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoPrepareHardware, pdoData->SerialNo, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//
|
||||
// Responds to IRP_MJ_INTERNAL_DEVICE_CONTROL requests sent to PDO.
|
||||
//
|
||||
VOID Pdo_EvtIoInternalDeviceControl(
|
||||
_In_ WDFQUEUE Queue,
|
||||
_In_ WDFREQUEST Request,
|
||||
_In_ size_t OutputBufferLength,
|
||||
_In_ size_t InputBufferLength,
|
||||
_In_ ULONG IoControlCode
|
||||
)
|
||||
{
|
||||
// Regular buffers not used in USB communication
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
UNREFERENCED_PARAMETER(InputBufferLength);
|
||||
|
||||
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
||||
WDFDEVICE hDevice;
|
||||
PIRP irp;
|
||||
PURB urb;
|
||||
PPDO_DEVICE_DATA pdoData;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
|
||||
hDevice = WdfIoQueueGetDevice(Queue);
|
||||
pdoData = PdoGetData(hDevice);
|
||||
// No help from the framework available from here on
|
||||
irp = WdfRequestWdmGetIrp(Request);
|
||||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||||
|
||||
switch (IoControlCode)
|
||||
{
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_URB:
|
||||
|
||||
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_SUBMIT_URB\n"));
|
||||
|
||||
urb = (PURB)URB_FROM_IRP(irp);
|
||||
|
||||
switch (urb->UrbHeader.Function)
|
||||
{
|
||||
case URB_FUNCTION_CONTROL_TRANSFER:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CONTROL_TRANSFER\n"));
|
||||
|
||||
switch (urb->UrbControlTransfer.SetupPacket[6])
|
||||
{
|
||||
case 0x04:
|
||||
//
|
||||
// Xenon magic
|
||||
//
|
||||
COPY_BYTE_ARRAY(urb->UrbControlTransfer.TransferBuffer, P99_PROTECT({
|
||||
0x31, 0x3F, 0xCF, 0xDC
|
||||
}));
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
case 0x14:
|
||||
//
|
||||
// This is some weird USB 1.0 condition and _must fail_
|
||||
//
|
||||
urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID;
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
case 0x08:
|
||||
//
|
||||
// This is some weird USB 1.0 condition and _must fail_
|
||||
//
|
||||
urb->UrbControlTransfer.Hdr.Status = USBD_STATUS_STALL_PID;
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
break;
|
||||
default:
|
||||
status = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_CONTROL_TRANSFER_EX:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CONTROL_TRANSFER_EX\n"));
|
||||
|
||||
status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER\n"));
|
||||
|
||||
status = UsbPdo_BulkOrInterruptTransfer(urb, hDevice, Request);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_SELECT_CONFIGURATION:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_SELECT_CONFIGURATION\n"));
|
||||
|
||||
status = UsbPdo_SelectConfiguration(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_SELECT_INTERFACE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_SELECT_INTERFACE\n"));
|
||||
|
||||
status = UsbPdo_SelectInterface(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n"));
|
||||
|
||||
switch (urb->UrbControlDescriptorRequest.DescriptorType)
|
||||
{
|
||||
case USB_DEVICE_DESCRIPTOR_TYPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> USB_DEVICE_DESCRIPTOR_TYPE\n"));
|
||||
|
||||
status = UsbPdo_GetDeviceDescriptorType(urb, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> USB_CONFIGURATION_DESCRIPTOR_TYPE\n"));
|
||||
|
||||
status = UsbPdo_GetConfigurationDescriptorType(urb, pdoData);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_STRING_DESCRIPTOR_TYPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> USB_STRING_DESCRIPTOR_TYPE\n"));
|
||||
|
||||
status = UsbPdo_GetStringDescriptorType(urb, pdoData);
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
||||
}
|
||||
|
||||
break;
|
||||
case USB_INTERFACE_DESCRIPTOR_TYPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> USB_INTERFACE_DESCRIPTOR_TYPE\n"));
|
||||
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_DESCRIPTOR_TYPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> USB_ENDPOINT_DESCRIPTOR_TYPE\n"));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
KdPrint((DRIVERNAME ">> >> >> Unknown descriptor type\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
KdPrint((DRIVERNAME "<< <<\n"));
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_STATUS_FROM_DEVICE\n"));
|
||||
|
||||
// Defaults always succeed
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
//
|
||||
// This IOCTL code is a nice indicator that the virtual XUSB device is
|
||||
// "powered up" and ready to get interacted with so we can report
|
||||
// success to the parent bus.
|
||||
//
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_ABORT_PIPE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_ABORT_PIPE\n"));
|
||||
|
||||
status = UsbPdo_AbortPipe(hDevice);
|
||||
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_CLASS_INTERFACE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_CLASS_INTERFACE\n"));
|
||||
|
||||
status = UsbPdo_ClassInterface(urb, hDevice, pdoData);
|
||||
|
||||
break;
|
||||
|
||||
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE\n"));
|
||||
|
||||
status = UsbPdo_GetDescriptorFromInterface(urb, pdoData);
|
||||
|
||||
//
|
||||
// This IOCTL code is a nice indicator that the virtual HIDUSB device is
|
||||
// "powered up" and ready to get interacted with so we can report
|
||||
// success to the parent bus.
|
||||
//
|
||||
BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
KdPrint((DRIVERNAME ">> >> Unknown function: 0x%X\n", urb->UrbHeader.Function));
|
||||
break;
|
||||
}
|
||||
|
||||
KdPrint((DRIVERNAME "<<\n"));
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
|
||||
|
||||
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_GET_PORT_STATUS\n"));
|
||||
|
||||
// We report the (virtual) port as always active
|
||||
*(unsigned long *)irpStack->Parameters.Others.Argument1 = USBD_PORT_ENABLED | USBD_PORT_CONNECTED;
|
||||
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_RESET_PORT:
|
||||
|
||||
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_RESET_PORT\n"));
|
||||
|
||||
// Sure, why not ;)
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
|
||||
|
||||
KdPrint((DRIVERNAME ">> IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION\n"));
|
||||
|
||||
// TODO: implement
|
||||
// This happens if the I/O latency is too high so HIDUSB aborts communication.
|
||||
status = STATUS_SUCCESS;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
KdPrint((DRIVERNAME ">> Unknown I/O control code 0x%X\n", IoControlCode));
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
WdfRequestComplete(Request, status);
|
||||
}
|
||||
}
|
||||
|
||||
14
sys/resource.h
Normal file
14
sys/resource.h
Normal file
@@ -0,0 +1,14 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by ViGEmBus.rc
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
1208
sys/usbpdo.c
Normal file
1208
sys/usbpdo.c
Normal file
File diff suppressed because it is too large
Load Diff
59
sys/util.c
Normal file
59
sys/util.c
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <ntifs.h>
|
||||
#include "busenum.h"
|
||||
|
||||
|
||||
VOID ReverseByteArray(PUCHAR Array, INT Length)
|
||||
{
|
||||
PUCHAR s = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, sizeof(UCHAR) * Length, VIGEM_POOL_TAG);
|
||||
INT c, d;
|
||||
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
for (c = Length - 1, d = 0; c >= 0; c--, d++)
|
||||
*(s + d) = *(Array + c);
|
||||
|
||||
for (c = 0; c < Length; c++)
|
||||
*(Array + c) = *(s + c);
|
||||
|
||||
ExFreePoolWithTag(s, VIGEM_POOL_TAG);
|
||||
}
|
||||
|
||||
VOID GenerateRandomMacAddress(PMAC_ADDRESS Address)
|
||||
{
|
||||
// Vendor "C0:13:37"
|
||||
Address->Vendor0 = 0xC0;
|
||||
Address->Vendor1 = 0x13;
|
||||
Address->Vendor2 = 0x37;
|
||||
|
||||
ULONG seed = KeQueryPerformanceCounter(NULL).LowPart;
|
||||
|
||||
Address->Nic0 = RtlRandomEx(&seed) % 0xFF;
|
||||
Address->Nic1 = RtlRandomEx(&seed) % 0xFF;
|
||||
Address->Nic2 = RtlRandomEx(&seed) % 0xFF;
|
||||
}
|
||||
405
sys/xgip.c
Normal file
405
sys/xgip.c
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
|
||||
NTSTATUS Xgip_PreparePdo(PWDFDEVICE_INIT DeviceInit, PUNICODE_STRING DeviceId, PUNICODE_STRING DeviceDescription)
|
||||
{
|
||||
NTSTATUS status;
|
||||
UNICODE_STRING buffer;
|
||||
|
||||
// prepare device description
|
||||
status = RtlUnicodeStringInit(DeviceDescription, L"Virtual Xbox One Controller");
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set hardware IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_0E6F&PID_0139&REV_0650");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\VID_0E6F&PID_0139");
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set compatible IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\MS_COMP_XGIP10");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_47&Prot_D0");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_47");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xgip_PrepareHardware(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_QUERY_INTERFACE_CONFIG ifaceCfg;
|
||||
|
||||
// Expose USB_BUS_INTERFACE_USBDI_GUID
|
||||
USB_BUS_INTERFACE_USBDI_V1 xgipInterface;
|
||||
|
||||
xgipInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1);
|
||||
xgipInterface.Version = USB_BUSIF_USBDI_VERSION_1;
|
||||
xgipInterface.BusContext = (PVOID)Device;
|
||||
|
||||
xgipInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
xgipInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
xgipInterface.SubmitIsoOutUrb = UsbPdo_SubmitIsoOutUrb;
|
||||
xgipInterface.GetUSBDIVersion = UsbPdo_GetUSBDIVersion;
|
||||
xgipInterface.QueryBusTime = UsbPdo_QueryBusTime;
|
||||
xgipInterface.QueryBusInformation = UsbPdo_QueryBusInformation;
|
||||
xgipInterface.IsDeviceHighSpeed = UsbPdo_IsDeviceHighSpeed;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&xgipInterface, &USB_BUS_INTERFACE_USBDI_GUID, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceAddQueryInterface failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
// Default button states
|
||||
UCHAR DefaultReport[XGIP_REPORT_SIZE] =
|
||||
{
|
||||
0x20, 0x00, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xa3, 0xfd, 0xed, 0x05, 0x5b, 0x03,
|
||||
0x6f, 0x02
|
||||
};
|
||||
|
||||
RtlCopyBytes(XgipGetData(Device)->Report, DefaultReport, XGIP_REPORT_SIZE);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xgip_AssignPdoContext(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PXGIP_DEVICE_DATA xgip = XgipGetData(Device);
|
||||
|
||||
KdPrint((DRIVERNAME "Initializing XGIP context...\n"));
|
||||
|
||||
RtlZeroMemory(xgip, sizeof(XGIP_DEVICE_DATA));
|
||||
|
||||
// Set fixed report id
|
||||
xgip->Report[0] = 0x20;
|
||||
xgip->Report[3] = 0x0E;
|
||||
|
||||
// I/O Queue for pending IRPs
|
||||
WDF_IO_QUEUE_CONFIG pendingUsbQueueConfig, notificationsQueueConfig;
|
||||
|
||||
// Create and assign queue for incoming interrupt transfer
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&pendingUsbQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, &pendingUsbQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &xgip->PendingUsbInRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
// Create and assign queue for user-land notification requests
|
||||
WDF_IO_QUEUE_CONFIG_INIT(¬ificationsQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, ¬ificationsQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &xgip->PendingNotificationRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES collectionAttribs;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&collectionAttribs);
|
||||
|
||||
collectionAttribs.ParentObject = Device;
|
||||
|
||||
status = WdfCollectionCreate(&collectionAttribs, &xgip->XboxgipSysInitCollection);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfCollectionCreate failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
// Initialize periodic timer
|
||||
WDF_TIMER_CONFIG timerConfig;
|
||||
WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, Xgip_SysInitTimerFunc, XGIP_SYS_INIT_PERIOD);
|
||||
|
||||
// Timer object attributes
|
||||
WDF_OBJECT_ATTRIBUTES timerAttribs;
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&timerAttribs);
|
||||
|
||||
// PDO is parent
|
||||
timerAttribs.ParentObject = Device;
|
||||
|
||||
// Create timer
|
||||
status = WdfTimerCreate(&timerConfig, &timerAttribs, &xgip->XboxgipSysInitTimer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfTimerCreate failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID Xgip_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length)
|
||||
{
|
||||
UCHAR XgipDescriptorData[XGIP_DESCRIPTOR_SIZE] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x40, 0x00, // wTotalLength 64
|
||||
0x02, // bNumInterfaces 2
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xC0, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0xFF, // bInterfaceClass
|
||||
0x47, // bInterfaceSubClass
|
||||
0xD0, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x01, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x00, // bAlternateSetting
|
||||
0x00, // bNumEndpoints 0
|
||||
0xFF, // bInterfaceClass
|
||||
0x47, // bInterfaceSubClass
|
||||
0xD0, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x01, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0xFF, // bInterfaceClass
|
||||
0x47, // bInterfaceSubClass
|
||||
0xD0, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x01, // bmAttributes (Isochronous, No Sync, Data EP)
|
||||
0xE0, 0x00, // wMaxPacketSize 224
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x83, // bEndpointAddress (IN/D2H)
|
||||
0x01, // bmAttributes (Isochronous, No Sync, Data EP)
|
||||
0x80, 0x00, // wMaxPacketSize 128
|
||||
0x01, // bInterval 1 (unit depends on device speed)
|
||||
|
||||
// 64 bytes
|
||||
|
||||
// best guess: USB Standard Descriptor
|
||||
};
|
||||
|
||||
RtlCopyBytes(Buffer, XgipDescriptorData, Length);
|
||||
}
|
||||
|
||||
VOID Xgip_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon)
|
||||
{
|
||||
pDescriptor->bLength = 0x12;
|
||||
pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
|
||||
pDescriptor->bcdUSB = 0x0200; // USB v2.0
|
||||
pDescriptor->bDeviceClass = 0xFF;
|
||||
pDescriptor->bDeviceSubClass = 0x47;
|
||||
pDescriptor->bDeviceProtocol = 0xD0;
|
||||
pDescriptor->bMaxPacketSize0 = 0x40;
|
||||
pDescriptor->idVendor = pCommon->VendorId;
|
||||
pDescriptor->idProduct = pCommon->ProductId;
|
||||
pDescriptor->bcdDevice = 0x0650;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
pDescriptor->iSerialNumber = 0x03;
|
||||
pDescriptor->bNumConfigurations = 0x01;
|
||||
}
|
||||
|
||||
VOID Xgip_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo)
|
||||
{
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x47;
|
||||
pInfo->Protocol = 0xD0;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x81;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = UsbdPipeTypeInterrupt;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0081;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x40;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x01;
|
||||
pInfo->Pipes[1].Interval = 0x04;
|
||||
pInfo->Pipes[1].PipeType = UsbdPipeTypeInterrupt;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0001;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x47;
|
||||
pInfo->Protocol = 0xD0;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
}
|
||||
|
||||
VOID Xgip_SysInitTimerFunc(
|
||||
_In_ WDFTIMER Timer
|
||||
)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFDEVICE hChild;
|
||||
PXGIP_DEVICE_DATA xgip;
|
||||
WDFREQUEST usbRequest;
|
||||
PIRP pendingIrp;
|
||||
PIO_STACK_LOCATION irpStack;
|
||||
WDFMEMORY mem;
|
||||
|
||||
hChild = WdfTimerGetParentObject(Timer);
|
||||
xgip = XgipGetData(hChild);
|
||||
|
||||
if (xgip == NULL) return;
|
||||
|
||||
// Is TRUE when collection is filled up
|
||||
if (xgip->XboxgipSysInitReady)
|
||||
{
|
||||
KdPrint((DRIVERNAME "XBOXGIP ready, completing requests...\n"));
|
||||
|
||||
// Get pending IN request
|
||||
status = WdfIoQueueRetrieveNextRequest(xgip->PendingUsbInRequests, &usbRequest);
|
||||
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Request found\n"));
|
||||
|
||||
// Get top memory object
|
||||
mem = (WDFMEMORY)WdfCollectionGetFirstItem(xgip->XboxgipSysInitCollection);
|
||||
|
||||
// Get pending IRP
|
||||
pendingIrp = WdfRequestWdmGetIrp(usbRequest);
|
||||
irpStack = IoGetCurrentIrpStackLocation(pendingIrp);
|
||||
|
||||
// Get USB request block
|
||||
PURB urb = (PURB)irpStack->Parameters.Others.Argument1;
|
||||
|
||||
// Get buffer size and content
|
||||
size_t size;
|
||||
PUCHAR Buffer = WdfMemoryGetBuffer(mem, &size);
|
||||
|
||||
// Assign buffer size and content to URB
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength = (ULONG)size;
|
||||
RtlCopyBytes(urb->UrbBulkOrInterruptTransfer.TransferBuffer, Buffer, size);
|
||||
|
||||
KdPrint((DRIVERNAME "[%X] Buffer length: %d\n",
|
||||
((PUCHAR)urb->UrbBulkOrInterruptTransfer.TransferBuffer)[0],
|
||||
urb->UrbBulkOrInterruptTransfer.TransferBufferLength));
|
||||
|
||||
// Complete pending request
|
||||
WdfRequestComplete(usbRequest, status);
|
||||
|
||||
// Free memory from collection
|
||||
WdfCollectionRemoveItem(xgip->XboxgipSysInitCollection, 0);
|
||||
WdfObjectDelete(mem);
|
||||
}
|
||||
|
||||
// Stop timer when collection is purged
|
||||
if (WdfCollectionGetCount(xgip->XboxgipSysInitCollection) == 0)
|
||||
{
|
||||
KdPrint((DRIVERNAME "Collection finished\n"));
|
||||
|
||||
WdfTimerStop(xgip->XboxgipSysInitTimer, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
506
sys/xusb.c
Normal file
506
sys/xusb.c
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Benjamin "Nefarius" H<>glinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "busenum.h"
|
||||
|
||||
NTSTATUS Xusb_PreparePdo(
|
||||
PWDFDEVICE_INIT DeviceInit,
|
||||
USHORT VendorId,
|
||||
USHORT ProductId,
|
||||
PUNICODE_STRING DeviceId,
|
||||
PUNICODE_STRING DeviceDescription)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_HARDWARE_ID_LENGTH);
|
||||
|
||||
// prepare device description
|
||||
status = RtlUnicodeStringInit(DeviceDescription, L"Virtual Xbox 360 Controller");
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set hardware ID
|
||||
RtlUnicodeStringPrintf(&buffer, L"USB\\VID_%04X&PID_%04X", VendorId, ProductId);
|
||||
|
||||
RtlUnicodeStringCopy(DeviceId, &buffer);
|
||||
|
||||
status = WdfPdoInitAddHardwareID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
// Set compatible IDs
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\MS_COMP_XUSB10");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D&Prot_01");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF&SubClass_5D");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
RtlUnicodeStringInit(&buffer, L"USB\\Class_FF");
|
||||
|
||||
status = WdfPdoInitAddCompatibleID(DeviceInit, &buffer);
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xusb_PrepareHardware(WDFDEVICE Device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_QUERY_INTERFACE_CONFIG ifaceCfg;
|
||||
|
||||
INTERFACE dummyIface;
|
||||
|
||||
dummyIface.Size = sizeof(INTERFACE);
|
||||
dummyIface.Version = 1;
|
||||
dummyIface.Context = (PVOID)Device;
|
||||
|
||||
dummyIface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
dummyIface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
/* XUSB.sys will query for the following three (unknown) interfaces
|
||||
* BUT WONT USE IT so we just expose them to satisfy initialization. */
|
||||
|
||||
// Dummy 0
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_DEVINTERFACE_XUSB_UNKNOWN_0, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Couldn't register unknown interface GUID: %08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X (status 0x%x)\n",
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data1,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data2,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data3,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[0],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[1],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[2],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[3],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[4],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[5],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[6],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_0.Data4[7],
|
||||
status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Dummy 1
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_DEVINTERFACE_XUSB_UNKNOWN_1, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Couldn't register unknown interface GUID: %08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X (status 0x%x)\n",
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data1,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data2,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data3,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[0],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[1],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[2],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[3],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[4],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[5],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[6],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_1.Data4[7],
|
||||
status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Dummy 2
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&dummyIface, &GUID_DEVINTERFACE_XUSB_UNKNOWN_2, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "Couldn't register unknown interface GUID: %08X-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X (status 0x%x)\n",
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data1,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data2,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data3,
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[0],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[1],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[2],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[3],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[4],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[5],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[6],
|
||||
GUID_DEVINTERFACE_XUSB_UNKNOWN_2.Data4[7],
|
||||
status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Expose USB_BUS_INTERFACE_USBDI_GUID
|
||||
|
||||
// This interface actually IS used
|
||||
USB_BUS_INTERFACE_USBDI_V1 xusbInterface;
|
||||
|
||||
xusbInterface.Size = sizeof(USB_BUS_INTERFACE_USBDI_V1);
|
||||
xusbInterface.Version = USB_BUSIF_USBDI_VERSION_1;
|
||||
xusbInterface.BusContext = (PVOID)Device;
|
||||
|
||||
xusbInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
|
||||
xusbInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
|
||||
|
||||
xusbInterface.SubmitIsoOutUrb = UsbPdo_SubmitIsoOutUrb;
|
||||
xusbInterface.GetUSBDIVersion = UsbPdo_GetUSBDIVersion;
|
||||
xusbInterface.QueryBusTime = UsbPdo_QueryBusTime;
|
||||
xusbInterface.QueryBusInformation = UsbPdo_QueryBusInformation;
|
||||
xusbInterface.IsDeviceHighSpeed = UsbPdo_IsDeviceHighSpeed;
|
||||
|
||||
WDF_QUERY_INTERFACE_CONFIG_INIT(&ifaceCfg, (PINTERFACE)&xusbInterface, &USB_BUS_INTERFACE_USBDI_GUID, NULL);
|
||||
|
||||
status = WdfDeviceAddQueryInterface(Device, &ifaceCfg);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfDeviceAddQueryInterface failed status 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION Description)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.ParentObject = Device;
|
||||
|
||||
KdPrint((DRIVERNAME "Initializing XUSB context...\n"));
|
||||
|
||||
PXUSB_DEVICE_DATA xusb = XusbGetData(Device);
|
||||
|
||||
RtlZeroMemory(xusb, sizeof(XUSB_DEVICE_DATA));
|
||||
|
||||
// Is later overwritten by actual XInput slot
|
||||
xusb->LedNumber = (UCHAR)Description->SerialNo;
|
||||
// Packet size (20 bytes = 0x14)
|
||||
xusb->Packet.Size = 0x14;
|
||||
|
||||
// I/O Queue for pending IRPs
|
||||
WDF_IO_QUEUE_CONFIG holdingInQueueConfig;
|
||||
|
||||
// Create and assign queue for unhandled interrupt requests
|
||||
WDF_IO_QUEUE_CONFIG_INIT(&holdingInQueueConfig, WdfIoQueueDispatchManual);
|
||||
|
||||
status = WdfIoQueueCreate(Device, &holdingInQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &xusb->HoldingUsbInRequests);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
KdPrint((DRIVERNAME "WdfIoQueueCreate (HoldingUsbInRequests) failed 0x%x\n", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID Xusb_GetConfigurationDescriptorType(PUCHAR Buffer, ULONG Length)
|
||||
{
|
||||
UCHAR XusbDescriptorData[XUSB_DESCRIPTOR_SIZE] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x99, 0x00, // wTotalLength 153
|
||||
0x04, // bNumInterfaces 4
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0xA0, // bmAttributes Remote Wakeup
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x01, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x11, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x25, // bNumDescriptors
|
||||
0x81, // bDescriptorType[0] (Unknown 0x81)
|
||||
0x14, 0x00, // wDescriptorLength[0] 20
|
||||
0x00, // bDescriptorType[1] (Unknown 0x00)
|
||||
0x00, 0x00, // wDescriptorLength[1] 0
|
||||
0x13, // bDescriptorType[2] (Unknown 0x13)
|
||||
0x01, 0x08, // wDescriptorLength[2] 2049
|
||||
0x00, // bDescriptorType[3] (Unknown 0x00)
|
||||
0x00,
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x01, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x08, // bInterval 8 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x01, // bInterfaceNumber 1
|
||||
0x00, // bAlternateSetting
|
||||
0x04, // bNumEndpoints 4
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x03, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x1B, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x82, // bDescriptorType[0] (Unknown 0x82)
|
||||
0x40, 0x01, // wDescriptorLength[0] 320
|
||||
0x02, 0x20, 0x16, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x82, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x02, // bInterval 2 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x04, // bInterval 4 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x83, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x40, // bInterval 64 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x03, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x10, // bInterval 16 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x02, // bInterfaceNumber 2
|
||||
0x00, // bAlternateSetting
|
||||
0x01, // bNumEndpoints 1
|
||||
0xFF, // bInterfaceClass
|
||||
0x5D, // bInterfaceSubClass
|
||||
0x02, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x00, 0x01, // bcdHID 1.00
|
||||
0x01, // bCountryCode
|
||||
0x22, // bNumDescriptors
|
||||
0x84, // bDescriptorType[0] (Unknown 0x84)
|
||||
0x07, 0x00, // wDescriptorLength[0] 7
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x84, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x10, // bInterval 16 (unit depends on device speed)
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x03, // bInterfaceNumber 3
|
||||
0x00, // bAlternateSetting
|
||||
0x00, // bNumEndpoints 0
|
||||
0xFF, // bInterfaceClass
|
||||
0xFD, // bInterfaceSubClass
|
||||
0x13, // bInterfaceProtocol
|
||||
0x04, // iInterface (String Index)
|
||||
|
||||
0x06, // bLength
|
||||
0x41, // bDescriptorType (Unknown)
|
||||
0x00, 0x01, 0x01, 0x03,
|
||||
// 153 bytes
|
||||
|
||||
// best guess: USB Standard Descriptor
|
||||
};
|
||||
|
||||
RtlCopyBytes(Buffer, XusbDescriptorData, Length);
|
||||
}
|
||||
|
||||
VOID Xusb_GetDeviceDescriptorType(PUSB_DEVICE_DESCRIPTOR pDescriptor, PPDO_DEVICE_DATA pCommon)
|
||||
{
|
||||
pDescriptor->bLength = 0x12;
|
||||
pDescriptor->bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
|
||||
pDescriptor->bcdUSB = 0x0200; // USB v2.0
|
||||
pDescriptor->bDeviceClass = 0xFF;
|
||||
pDescriptor->bDeviceSubClass = 0xFF;
|
||||
pDescriptor->bDeviceProtocol = 0xFF;
|
||||
pDescriptor->bMaxPacketSize0 = 0x08;
|
||||
pDescriptor->idVendor = pCommon->VendorId;
|
||||
pDescriptor->idProduct = pCommon->ProductId;
|
||||
pDescriptor->bcdDevice = 0x0114;
|
||||
pDescriptor->iManufacturer = 0x01;
|
||||
pDescriptor->iProduct = 0x02;
|
||||
pDescriptor->iSerialNumber = 0x03;
|
||||
pDescriptor->bNumConfigurations = 0x01;
|
||||
}
|
||||
|
||||
VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo)
|
||||
{
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x01;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x81;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0081;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x01;
|
||||
pInfo->Pipes[1].Interval = 0x08;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0001;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x03;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x82;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0082;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[1].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[1].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[1].EndpointAddress = 0x02;
|
||||
pInfo->Pipes[1].Interval = 0x08;
|
||||
pInfo->Pipes[1].PipeType = 0x03;
|
||||
pInfo->Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0002;
|
||||
pInfo->Pipes[1].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[2].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[2].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[2].EndpointAddress = 0x83;
|
||||
pInfo->Pipes[2].Interval = 0x08;
|
||||
pInfo->Pipes[2].PipeType = 0x03;
|
||||
pInfo->Pipes[2].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0083;
|
||||
pInfo->Pipes[2].PipeFlags = 0x00;
|
||||
|
||||
pInfo->Pipes[3].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[3].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[3].EndpointAddress = 0x03;
|
||||
pInfo->Pipes[3].Interval = 0x08;
|
||||
pInfo->Pipes[3].PipeType = 0x03;
|
||||
pInfo->Pipes[3].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003;
|
||||
pInfo->Pipes[3].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0x5D;
|
||||
pInfo->Protocol = 0x02;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
|
||||
pInfo->Pipes[0].MaximumTransferSize = 0x00400000;
|
||||
pInfo->Pipes[0].MaximumPacketSize = 0x20;
|
||||
pInfo->Pipes[0].EndpointAddress = 0x84;
|
||||
pInfo->Pipes[0].Interval = 0x04;
|
||||
pInfo->Pipes[0].PipeType = 0x03;
|
||||
pInfo->Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084;
|
||||
pInfo->Pipes[0].PipeFlags = 0x00;
|
||||
|
||||
pInfo = (PUSBD_INTERFACE_INFORMATION)((PCHAR)pInfo + pInfo->Length);
|
||||
|
||||
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Length %d, Interface %d, Alternate %d, Pipes %d\n",
|
||||
(int)pInfo->Length,
|
||||
(int)pInfo->InterfaceNumber,
|
||||
(int)pInfo->AlternateSetting,
|
||||
pInfo->NumberOfPipes));
|
||||
|
||||
pInfo->Class = 0xFF;
|
||||
pInfo->SubClass = 0xFD;
|
||||
pInfo->Protocol = 0x13;
|
||||
|
||||
pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user