Files
Aaru/Aaru.Devices/Windows/UsbFunctions.cs

317 lines
13 KiB
C#
Raw Normal View History

2019-10-27 20:47:29 +00:00
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : UsbFunctions.cs
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// Component : Windows direct device access.
//
// --[ Description ] ----------------------------------------------------------
//
// Contains code to access USB device information under Windows.
//
// --[ License ] --------------------------------------------------------------
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General internal License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General internal License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
2020-01-03 17:51:30 +00:00
// Copyright © 2011-2020 Natalia Portillo
// Copyright © 2007 Fort Hood TX, herethen, Public Domain
// ****************************************************************************/
2017-12-19 19:33:46 +00:00
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
2020-02-27 00:33:26 +00:00
namespace Aaru.Devices.Windows
{
2018-12-29 17:34:38 +00:00
//
// A place for "higher level" related functions
// You might not want to keep these in the USB class... your choice
2018-12-29 17:34:38 +00:00
//
// TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love
/// <summary>
/// Implements functions for getting and accesing information from the USB bus
/// </summary>
2019-10-27 20:47:29 +00:00
internal static partial class Usb
{
private const int IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080;
internal const string GuidDevinterfaceDisk = "53f56307-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceCdrom = "53f56308-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceFloppy = "53f56311-b6bf-11d0-94f2-00a0c91efb8b";
internal const string GuidDevinterfaceTape = "53f5630b-b6bf-11d0-94f2-00a0c91efb8b";
/// <summary>
/// Get a list of all connected devices
/// </summary>
/// <returns>List of usb devices</returns>
internal static List<UsbDevice> GetConnectedDevices()
{
2019-10-27 20:47:29 +00:00
var devList = new List<UsbDevice>();
2019-10-27 20:47:29 +00:00
foreach (var controller in GetHostControllers()) ListHub(controller.GetRootHub(), devList);
2017-12-19 20:33:03 +00:00
return devList;
}
/// <summary>
/// private routine for enumerating a hub
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="devList">Device list</param>
2019-10-27 20:47:29 +00:00
private static void ListHub(UsbHub hub, ICollection<UsbDevice> devList)
{
2019-10-27 20:47:29 +00:00
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
2018-06-22 08:08:38 +01:00
ListHub(port.GetHub(), devList);
2019-10-27 20:47:29 +00:00
}
2018-06-22 08:08:38 +01:00
else
{
2019-10-27 20:47:29 +00:00
if (port.IsDeviceConnected) devList.Add(port.GetDevice());
2018-06-22 08:08:38 +01:00
}
}
/// <summary>
/// Find a device based upon it's DriverKeyName
/// </summary>
/// <param name="driverKeyName">DriverKeyName</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDeviceByDriverKeyName(string driverKeyName)
{
UsbDevice foundDevice = null;
2019-10-27 20:47:29 +00:00
foreach (var controller in GetHostControllers())
{
SearchHubDriverKeyName(controller.GetRootHub(), ref foundDevice, driverKeyName);
2019-10-27 20:47:29 +00:00
if (foundDevice != null) break;
}
2017-12-19 20:33:03 +00:00
return foundDevice;
}
/// <summary>
/// Finds a device connected to a specified hub by it's DriverKeyName
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="foundDevice">UsbDevice</param>
/// <param name="driverKeyName">DriverKeyName</param>
2019-10-27 20:47:29 +00:00
private static void SearchHubDriverKeyName(UsbHub hub, ref UsbDevice foundDevice, string driverKeyName)
{
2019-10-27 20:47:29 +00:00
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
2018-06-22 08:08:38 +01:00
SearchHubDriverKeyName(port.GetHub(), ref foundDevice, driverKeyName);
2019-10-27 20:47:29 +00:00
}
else
{
2019-10-27 20:47:29 +00:00
if (!port.IsDeviceConnected) continue;
2019-10-27 20:47:29 +00:00
var device = port.GetDevice();
if (device.DeviceDriverKey != driverKeyName) continue;
foundDevice = device;
break;
}
}
/// <summary>
/// Find a device based upon it's Instance ID
/// </summary>
/// <param name="instanceId">Device instance ID</param>
/// <returns>USB device</returns>
2019-10-27 20:47:29 +00:00
private static UsbDevice FindDeviceByInstanceId(string instanceId)
{
UsbDevice foundDevice = null;
2019-10-27 20:47:29 +00:00
foreach (var controller in GetHostControllers())
{
SearchHubInstanceId(controller.GetRootHub(), ref foundDevice, instanceId);
2019-10-27 20:47:29 +00:00
if (foundDevice != null) break;
}
2017-12-19 20:33:03 +00:00
return foundDevice;
}
/// <summary>
/// private routine for enumerating a hub
/// </summary>
/// <param name="hub">Hub</param>
/// <param name="foundDevice">USB device</param>
/// <param name="instanceId">Device instance ID</param>
2019-10-27 20:47:29 +00:00
private static void SearchHubInstanceId(UsbHub hub, ref UsbDevice foundDevice, string instanceId)
{
2019-10-27 20:47:29 +00:00
foreach (var port in hub.GetPorts())
if (port.IsHub)
{
2018-06-22 08:08:38 +01:00
SearchHubInstanceId(port.GetHub(), ref foundDevice, instanceId);
2019-10-27 20:47:29 +00:00
}
else
{
2019-10-27 20:47:29 +00:00
if (!port.IsDeviceConnected) continue;
2019-10-27 20:47:29 +00:00
var device = port.GetDevice();
if (device.InstanceId != instanceId) continue;
foundDevice = device;
break;
}
}
[DllImport("setupapi.dll")]
2019-10-27 20:47:29 +00:00
private static extern int CM_Get_Parent(out IntPtr pdnDevInst, IntPtr dnDevInst, int ulFlags);
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
2019-10-27 20:47:29 +00:00
private static extern int CM_Get_Device_ID(IntPtr dnDevInst, IntPtr buffer, int bufferLen, int ulFlags);
/// <summary>
/// Find a device based upon a Drive Letter
/// </summary>
/// <param name="driveLetter">Drive letter</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDriveLetter(string driveLetter, string deviceGuid)
{
// We start by getting the unique DeviceNumber of the given
// DriveLetter. We'll use this later to find a matching
// DevicePath "symbolic name"
2019-10-27 20:47:29 +00:00
var devNum = GetDeviceNumber(@"\\.\" + driveLetter.TrimEnd('\\'));
return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid);
}
/// <summary>
/// Find a device based upon a Drive Path
/// </summary>
/// <param name="drivePath">Drive path</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
internal static UsbDevice FindDrivePath(string drivePath, string deviceGuid)
{
// We start by getting the unique DeviceNumber of the given
// DriveLetter. We'll use this later to find a matching
// DevicePath "symbolic name"
2019-10-27 20:47:29 +00:00
var devNum = GetDeviceNumber(drivePath);
return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid);
}
/// <summary>
/// Find a device based upon a Device Number
/// </summary>
/// <param name="devNum">Device Number</param>
/// <param name="deviceGuid">Device GUID</param>
/// <returns>USB device</returns>
2019-10-27 20:47:29 +00:00
private static UsbDevice FindDeviceNumber(int devNum, string deviceGuid)
{
UsbDevice foundDevice = null;
2019-10-27 20:47:29 +00:00
var instanceId = "";
2019-10-27 20:47:29 +00:00
var diskGuid = new Guid(deviceGuid);
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
2019-10-27 20:47:29 +00:00
var h = SetupDiGetClassDevs(ref diskGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (h != INVALID_HANDLE_VALUE)
{
2017-12-21 16:07:20 +00:00
bool success;
2019-10-27 20:47:29 +00:00
var i = 0;
do
{
// create a Device Interface Data structure
2019-10-27 20:47:29 +00:00
var dia = new SpDeviceInterfaceData();
dia.cbSize = Marshal.SizeOf(dia);
// start the enumeration
success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref diskGuid, i, ref dia);
2019-10-27 20:47:29 +00:00
if (success)
{
// build a DevInfo Data structure
2019-10-27 20:47:29 +00:00
var da = new SpDevinfoData();
da.cbSize = Marshal.SizeOf(da);
// build a Device Interface Detail Data structure
2019-10-27 20:47:29 +00:00
var didd =
new SpDeviceInterfaceDetailData {cbSize = 4 + Marshal.SystemDefaultCharSize}; // trust me :)
// now we can get some more detailed information
2019-10-27 20:47:29 +00:00
var nRequiredSize = 0;
const int N_BYTES = BUFFER_SIZE;
if (SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da))
if (GetDeviceNumber(didd.DevicePath) == devNum)
{
// current InstanceID is at the "USBSTOR" level, so we
// need up "move up" one level to get to the "USB" level
2019-10-27 20:47:29 +00:00
CM_Get_Parent(out var ptrPrevious, da.DevInst, 0);
// Now we get the InstanceID of the USB level device
2019-10-27 20:47:29 +00:00
var ptrInstanceBuf = Marshal.AllocHGlobal(N_BYTES);
CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, N_BYTES, 0);
instanceId = Marshal.PtrToStringAuto(ptrInstanceBuf);
Marshal.FreeHGlobal(ptrInstanceBuf);
2018-07-01 22:51:06 +01:00
//System.Console.WriteLine("InstanceId: {0}", instanceId);
//break;
}
}
2018-06-22 08:08:38 +01:00
i++;
2019-10-27 20:47:29 +00:00
} while (success);
2017-12-19 20:33:03 +00:00
SetupDiDestroyDeviceInfoList(h);
}
// Did we find an InterfaceID of a USB device?
2019-10-27 20:47:29 +00:00
if (instanceId?.StartsWith("USB\\", StringComparison.Ordinal) == true)
foundDevice = FindDeviceByInstanceId(instanceId);
return foundDevice;
}
/// <summary>
/// return a unique device number for the given device path
/// </summary>
/// <param name="devicePath">Device path</param>
/// <returns>Device number</returns>
2019-10-27 20:47:29 +00:00
private static int GetDeviceNumber(string devicePath)
{
2019-10-27 20:47:29 +00:00
var ans = -1;
2019-10-27 20:47:29 +00:00
var h = CreateFile(devicePath.TrimEnd('\\'), 0, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (h == INVALID_HANDLE_VALUE) return ans;
2019-10-27 20:47:29 +00:00
var sdn = new StorageDeviceNumber();
var nBytes = Marshal.SizeOf(sdn);
var ptrSdn = Marshal.AllocHGlobal(nBytes);
2019-10-27 20:47:29 +00:00
if (DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ptrSdn, nBytes, out _, IntPtr.Zero))
{
2019-10-27 20:47:29 +00:00
sdn = (StorageDeviceNumber) Marshal.PtrToStructure(ptrSdn, typeof(StorageDeviceNumber));
// just my way of combining the relevant parts of the
// STORAGE_DEVICE_NUMBER into a single number
ans = (sdn.DeviceType << 8) + sdn.DeviceNumber;
}
2018-06-22 08:08:38 +01:00
Marshal.FreeHGlobal(ptrSdn);
CloseHandle(h);
return ans;
}
[StructLayout(LayoutKind.Sequential)]
2019-10-27 20:47:29 +00:00
private struct StorageDeviceNumber
{
2019-10-27 20:47:29 +00:00
internal readonly int DeviceType;
internal readonly int DeviceNumber;
internal readonly int PartitionNumber;
}
}
2017-12-19 20:33:03 +00:00
}