ScpControl/Driver/WdiWrapper.cs (273 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using ScpControl.ScpCore;
namespace ScpControl.Driver
{
#region Public enums
public enum WdiErrorCode
{
WDI_SUCCESS = 0,
WDI_ERROR_IO = -1,
WDI_ERROR_INVALID_PARAM = -2,
WDI_ERROR_ACCESS = -3,
WDI_ERROR_NO_DEVICE = -4,
WDI_ERROR_NOT_FOUND = -5,
WDI_ERROR_BUSY = -6,
WDI_ERROR_TIMEOUT = -7,
WDI_ERROR_OVERFLOW = -8,
WDI_ERROR_PENDING_INSTALLATION = -9,
WDI_ERROR_INTERRUPTED = -10,
WDI_ERROR_RESOURCE = -11,
WDI_ERROR_NOT_SUPPORTED = -12,
WDI_ERROR_EXISTS = -13,
WDI_ERROR_USER_CANCEL = -14,
WDI_ERROR_NEEDS_ADMIN = -15,
WDI_ERROR_WOW64 = -16,
WDI_ERROR_INF_SYNTAX = -17,
WDI_ERROR_CAT_MISSING = -18,
WDI_ERROR_UNSIGNED = -19,
WDI_ERROR_OTHER = -99
}
public enum WdiLogLevel
{
WDI_LOG_LEVEL_DEBUG,
WDI_LOG_LEVEL_INFO,
WDI_LOG_LEVEL_WARNING,
WDI_LOG_LEVEL_ERROR,
WDI_LOG_LEVEL_NONE
}
#endregion
/// <summary>
/// Managed wrapper class for <see href="https://github.com/pbatard/libwdi">libwdi</see>.
/// </summary>
public class WdiWrapper : NativeLibraryWrapper<WdiWrapper>
{
#region Ctor
/// <summary>
/// Automatically loads the correct native library.
/// </summary>
private WdiWrapper()
{
LoadNativeLibrary("libwdi", @"libwdi\x86\libwdi.dll", @"libwdi\amd64\libwdi.dll");
}
#endregion
#region Public properties
public IEnumerable<WdiDeviceInfo> UsbDeviceList
{
get
{
var wdiDevices = new List<WdiDeviceInfo>();
// pointer to write device list to
var pList = IntPtr.Zero;
// list all Usb devices, not only driverless ones
var listOpts = new wdi_options_create_list
{
list_all = true,
list_hubs = false,
trim_whitespaces = true
};
// receive Usb device list
wdi_create_list(ref pList, ref listOpts);
// save original pointer to free list
var devices = pList;
// loop through linked list until last element
while (pList != IntPtr.Zero)
{
// translate device info to managed object
var info = (wdi_device_info)Marshal.PtrToStructure(pList, typeof(wdi_device_info));
var wdiDevice = NativeToManagedWdiUsbDevice(info);
wdiDevices.Add(wdiDevice);
// continue with next device
pList = info.next;
}
// free used memory
wdi_destroy_list(devices);
return wdiDevices;
}
}
#endregion
#region Private methods
private static WdiDeviceInfo NativeToManagedWdiUsbDevice(wdi_device_info info)
{
// get raw bytes from description pointer
var descSize = 0;
while (Marshal.ReadByte(info.desc, descSize) != 0) ++descSize;
var descBytes = new byte[descSize];
Marshal.Copy(info.desc, descBytes, 0, descSize);
// put info in managed object
var wdiDevice = new WdiDeviceInfo
{
VendorId = info.vid,
ProductId = info.pid,
InterfaceId = (byte)info.mi,
Description = Encoding.UTF8.GetString(descBytes),
DeviceId = info.device_id,
HardwareId = info.hardware_id,
CurrentDriver = Marshal.PtrToStringAnsi(info.driver)
};
return wdiDevice;
}
#endregion
#region Enums
/// <summary>
/// The Usb driver solution to install.
/// </summary>
private enum WdiDriverType
{
[Description("WinUSB")]
WDI_WINUSB,
WDI_LIBUSB0,
[Description("libusbK")]
WDI_LIBUSBK,
WDI_USER,
WDI_NB_DRIVERS
}
#endregion
#region Public methods
/// <summary>
/// Equipes a given device with the WinUSB driver.
/// </summary>
/// <param name="device">The device to perform the driver installation on.</param>
/// <param name="deviceGuid">The device class GUID of the driver.</param>
/// <param name="driverPath">The filesystem path to extract the driver and helper files to.</param>
/// <param name="infName">The name of the *.INF file to create.</param>
/// <param name="hwnd">The handle of the parent window to relate the progress dialog to.</param>
/// <returns>The error code (0 if succeeded).</returns>
public static WdiErrorCode InstallWinUsbDriver(WdiDeviceInfo device, Guid deviceGuid, string driverPath,
string infName, IntPtr hwnd = default(IntPtr))
{
// build CLI args
var cliArgs = new StringBuilder();
switch (device.DeviceType)
{
case WdiUsbDeviceType.BluetoothHost:
cliArgs.AppendFormat("--name \"Bluetooth Host (ScpToolkit)\" ");
break;
case WdiUsbDeviceType.DualShock3:
cliArgs.AppendFormat("--name \"DualShock 3 Controller (ScpToolkit)\" ");
break;
case WdiUsbDeviceType.DualShock4:
cliArgs.AppendFormat("--name \"DualShock 4 Controller (ScpToolkit)\" ");
break;
}
cliArgs.AppendFormat("--inf \"{0}\" ", infName);
cliArgs.AppendFormat("--manufacturer \"ScpToolkit compatible device\" ");
cliArgs.AppendFormat("--vid 0x{0:X4} --pid 0x{1:X4} ", device.VendorId, device.ProductId);
cliArgs.AppendFormat("--type 0 ");
cliArgs.AppendFormat("--dest \"{0}\" ", driverPath);
cliArgs.AppendFormat("--stealth-cert ");
if (hwnd != default(IntPtr)) cliArgs.AppendFormat("--progressbar={0:D} ", hwnd.ToInt64());
cliArgs.AppendFormat("--timeout 120000 ");
cliArgs.AppendFormat("--device-guid \"{0}\" ", deviceGuid.ToString("B"));
// build path to install helper
var wdiSimplePath = Path.Combine(GlobalConfiguration.AppDirectory, "libwdi",
Environment.Is64BitProcess ? "amd64" : "x86", "wdi-simple.exe");
// set-up installer process
var wdiProc = new Process
{
StartInfo = new ProcessStartInfo(wdiSimplePath, cliArgs.ToString())
{
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
// start & wait
wdiProc.Start();
wdiProc.WaitForExit();
// return code of application is possible error code
return (WdiErrorCode)wdiProc.ExitCode;
}
public string GetErrorMessage(WdiErrorCode errcode)
{
var msgPtr = wdi_strerror((int)errcode);
return Marshal.PtrToStringAnsi(msgPtr);
}
public string GetVendorName(ushort vendorId)
{
var namePtr = wdi_get_vendor_name(vendorId);
return Marshal.PtrToStringAnsi(namePtr);
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct wdi_device_info
{
public readonly IntPtr next;
public readonly ushort vid;
public readonly ushort pid;
public readonly bool is_composite;
public readonly char mi;
public readonly IntPtr desc;
public readonly IntPtr driver;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string device_id;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string hardware_id;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string compatible_id;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string upper_filter;
public readonly ulong driver_version;
}
[StructLayout(LayoutKind.Sequential)]
private struct wdi_options_create_list
{
public bool list_all;
public bool list_hubs;
public bool trim_whitespaces;
}
[StructLayout(LayoutKind.Sequential)]
private struct wdi_options_prepare_driver
{
[MarshalAs(UnmanagedType.I4)]
public readonly WdiDriverType driver_type;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string vendor_name;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string device_guid;
public readonly bool disable_cat;
public readonly bool disable_signing;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string cert_subject;
public readonly bool use_wcid_driver;
}
[StructLayout(LayoutKind.Sequential)]
private struct wdi_options_install_driver
{
public readonly IntPtr hWnd;
public readonly bool install_filter_driver;
public readonly uint pending_install_timeout;
}
#endregion
#region P/Invoke
[DllImport("libwdi.dll", EntryPoint = "wdi_strerror", ExactSpelling = false)]
private static extern IntPtr wdi_strerror(int errcode);
[DllImport("libwdi.dll", EntryPoint = "wdi_get_vendor_name", ExactSpelling = false)]
private static extern IntPtr wdi_get_vendor_name(ushort vid);
[DllImport("libwdi.dll", EntryPoint = "wdi_create_list", ExactSpelling = false)]
private static extern int wdi_create_list(ref IntPtr list,
ref wdi_options_create_list options);
[DllImport("libwdi.dll", EntryPoint = "wdi_destroy_list", ExactSpelling = false)]
private static extern WdiErrorCode wdi_destroy_list(IntPtr list);
#endregion
}
/// <summary>
/// Managed wrapper for Usb device properties.
/// </summary>
public class WdiDeviceInfo
{
public ushort VendorId { get; set; }
public ushort ProductId { get; set; }
public byte InterfaceId { get; set; }
public string Description { get; set; }
public string DeviceId { get; set; }
public string HardwareId { get; set; }
public string CurrentDriver { get; set; }
public WdiUsbDeviceType DeviceType { get; set; }
public string InfFile { get; set; }
public override string ToString()
{
return string.Format("{0} (VID: {1:X4}, PID: {2:X4})", Description, VendorId, ProductId);
}
}
public enum WdiUsbDeviceType
{
Unknown,
BluetoothHost,
DualShock3,
DualShock4
}
}