2017-12-19 03:50:57 +00:00
// /***************************************************************************
// The Disc Image Chef
// ----------------------------------------------------------------------------
//
// Filename : Usb.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
2017-12-20 02:08:37 +00:00
// it under the terms of the GNU Lesser General internal License as
2017-12-19 03:50:57 +00:00
// 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
2017-12-20 02:08:37 +00:00
// Lesser General internal License for more details.
2017-12-19 03:50:57 +00:00
//
// 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/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2011-2018 Natalia Portillo
2017-12-22 03:13:43 +00:00
// Copyright © 2007 Fort Hood TX, herethen, Public Domain
2017-12-19 03:50:57 +00:00
// ****************************************************************************/
2017-12-19 19:33:46 +00:00
using System ;
using System.Collections.Generic ;
2017-12-21 14:30:38 +00:00
using System.Collections.ObjectModel ;
2017-12-22 03:13:43 +00:00
using System.Diagnostics.CodeAnalysis ;
2017-12-19 19:33:46 +00:00
using System.Runtime.InteropServices ;
using System.Text ;
2017-12-06 19:30:03 +00:00
namespace DiscImageChef.Devices.Windows
{
2017-12-22 03:13:43 +00:00
// TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love
2017-12-23 02:32:02 +00:00
/// <summary>
/// Implements functions for getting and accesing information from the USB bus
/// </summary>
2017-12-22 03:13:43 +00:00
static partial class Usb
2017-12-06 19:30:03 +00:00
{
#region "API Region"
2017-12-21 07:36:47 +00:00
// ********************** Constants ************************
2017-12-06 19:30:03 +00:00
const int GENERIC_WRITE = 0x40000000 ;
const int FILE_SHARE_READ = 0x1 ;
const int FILE_SHARE_WRITE = 0x2 ;
const int OPEN_EXISTING = 0x3 ;
const int INVALID_HANDLE_VALUE = - 1 ;
const int IOCTL_GET_HCD_DRIVERKEY_NAME = 0x220424 ;
const int IOCTL_USB_GET_ROOT_HUB_NAME = 0x220408 ;
2017-12-21 07:36:47 +00:00
const int IOCTL_USB_GET_NODE_INFORMATION = 0x220408 ; // same as above... strange, eh?
2017-12-06 19:30:03 +00:00
const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448 ;
const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410 ;
const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414 ;
const int IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = 0x220420 ;
const int USB_DEVICE_DESCRIPTOR_TYPE = 0x1 ;
2017-12-06 20:38:10 +00:00
const int USB_CONFIGURATION_DESCRIPTOR_TYPE = 0x2 ;
2017-12-06 19:30:03 +00:00
const int USB_STRING_DESCRIPTOR_TYPE = 0x3 ;
const int BUFFER_SIZE = 2048 ;
const int MAXIMUM_USB_STRING_LENGTH = 255 ;
const string GUID_DEVINTERFACE_HUBCONTROLLER = "3abf6f2d-71c4-462a-8a92-1e6861e6af27" ;
const string REGSTR_KEY_USB = "USB" ;
const int DIGCF_PRESENT = 0x2 ;
const int DIGCF_ALLCLASSES = 0x4 ;
const int DIGCF_DEVICEINTERFACE = 0x10 ;
const int SPDRP_DRIVER = 0x9 ;
const int SPDRP_DEVICEDESC = 0x0 ;
const int REG_SZ = 1 ;
2017-12-21 07:36:47 +00:00
// ********************** Enumerations ************************
2017-12-06 19:30:03 +00:00
2017-12-20 17:15:26 +00:00
enum UsbHubNode
2017-12-06 19:30:03 +00:00
{
UsbHub ,
2017-12-20 17:15:26 +00:00
UsbMiParent
2017-12-06 19:30:03 +00:00
}
2017-12-20 17:15:26 +00:00
enum UsbConnectionStatus
2017-12-06 19:30:03 +00:00
{
NoDeviceConnected ,
DeviceConnected ,
DeviceFailedEnumeration ,
DeviceGeneralFailure ,
DeviceCausedOvercurrent ,
DeviceNotEnoughPower ,
DeviceNotEnoughBandwidth ,
DeviceHubNestedTooDeeply ,
DeviceInLegacyHub
}
2017-12-20 17:15:26 +00:00
enum UsbDeviceSpeed : byte
2017-12-06 19:30:03 +00:00
{
UsbLowSpeed ,
UsbFullSpeed ,
UsbHighSpeed
}
2017-12-21 07:36:47 +00:00
// ********************** Stuctures ************************
2017-12-06 19:30:03 +00:00
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct SpDevinfoData
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int cbSize ;
internal Guid ClassGuid ;
internal IntPtr DevInst ;
internal IntPtr Reserved ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct SpDeviceInterfaceData
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int cbSize ;
internal Guid InterfaceClassGuid ;
internal int Flags ;
internal IntPtr Reserved ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
struct SpDeviceInterfaceDetailData
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int cbSize ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] internal string DevicePath ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
struct UsbHcdDriverkeyName
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ActualLength ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] internal string DriverKeyName ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
struct UsbRootHubName
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ActualLength ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] internal string RootHubName ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
2017-12-20 17:15:26 +00:00
struct UsbHubDescriptor
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal byte bDescriptorLength ;
internal byte bDescriptorType ;
internal byte bNumberOfPorts ;
internal short wHubCharacteristics ;
internal byte bPowerOnToPowerGood ;
internal byte bHubControlCurrent ;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] internal byte [ ] bRemoveAndPowerMask ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct UsbHubInformation
2017-12-06 19:30:03 +00:00
{
2017-12-20 17:15:26 +00:00
internal UsbHubDescriptor HubDescriptor ;
2017-12-20 02:08:37 +00:00
internal byte HubIsBusPowered ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct UsbNodeInformation
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int NodeType ;
2017-12-21 07:36:47 +00:00
internal UsbHubInformation HubInformation ; // Yeah, I'm assuming we'll just use the first form
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
2017-12-20 17:15:26 +00:00
struct UsbNodeConnectionInformationEx
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ConnectionIndex ;
2017-12-20 17:15:26 +00:00
internal UsbDeviceDescriptor DeviceDescriptor ;
2017-12-20 02:08:37 +00:00
internal byte CurrentConfigurationValue ;
internal byte Speed ;
internal byte DeviceIsHub ;
internal short DeviceAddress ;
internal int NumberOfOpenPipes ;
internal int ConnectionStatus ;
2017-12-21 07:36:47 +00:00
//internal IntPtr PipeList;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
2017-12-22 03:13:43 +00:00
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
2017-12-20 17:15:26 +00:00
internal struct UsbDeviceDescriptor
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal byte bLength ;
internal byte bDescriptorType ;
internal short bcdUSB ;
internal byte bDeviceClass ;
internal byte bDeviceSubClass ;
internal byte bDeviceProtocol ;
internal byte bMaxPacketSize0 ;
internal short idVendor ;
internal short idProduct ;
internal short bcdDevice ;
internal byte iManufacturer ;
internal byte iProduct ;
internal byte iSerialNumber ;
internal byte bNumConfigurations ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
struct UsbStringDescriptor
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal byte bLength ;
internal byte bDescriptorType ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXIMUM_USB_STRING_LENGTH)] internal string bString ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct UsbSetupPacket
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal byte bmRequest ;
internal byte bRequest ;
internal short wValue ;
internal short wIndex ;
internal short wLength ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential)]
2017-12-20 17:15:26 +00:00
struct UsbDescriptorRequest
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ConnectionIndex ;
2017-12-06 19:30:03 +00:00
2017-12-20 17:15:26 +00:00
internal UsbSetupPacket SetupPacket ;
2017-12-21 07:36:47 +00:00
//internal byte[] Data;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
struct UsbNodeConnectionName
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ConnectionIndex ;
internal int ActualLength ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] internal string NodeName ;
2017-12-06 19:30:03 +00:00
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
2017-12-21 07:36:47 +00:00
struct UsbNodeConnectionDriverkeyName // Yes, this is the same as the structure above...
2017-12-06 19:30:03 +00:00
{
2017-12-20 02:08:37 +00:00
internal int ConnectionIndex ;
internal int ActualLength ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] internal string DriverKeyName ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 07:36:47 +00:00
// ********************** API Definitions ************************
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
2017-12-21 07:36:47 +00:00
static extern IntPtr SetupDiGetClassDevs ( // 1st form using a ClassGUID
2017-12-20 17:15:26 +00:00
ref Guid classGuid , int enumerator , IntPtr hwndParent , int flags ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
[DllImport("setupapi.dll", CharSet = CharSet.Auto)] // 2nd form uses an Enumerator
2017-12-20 17:15:26 +00:00
static extern IntPtr SetupDiGetClassDevs ( int classGuid , string enumerator , IntPtr hwndParent , int flags ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiEnumDeviceInterfaces ( IntPtr deviceInfoSet , IntPtr deviceInfoData ,
ref Guid interfaceClassGuid , int memberIndex ,
ref SpDeviceInterfaceData deviceInterfaceData ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiGetDeviceInterfaceDetail ( IntPtr deviceInfoSet ,
ref SpDeviceInterfaceData deviceInterfaceData ,
ref SpDeviceInterfaceDetailData
deviceInterfaceDetailData ,
int deviceInterfaceDetailDataSize , ref int requiredSize ,
ref SpDevinfoData deviceInfoData ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiGetDeviceRegistryProperty ( IntPtr deviceInfoSet , ref SpDevinfoData deviceInfoData ,
int iProperty , ref int propertyRegDataType ,
IntPtr propertyBuffer , int propertyBufferSize ,
ref int requiredSize ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiEnumDeviceInfo ( IntPtr deviceInfoSet , int memberIndex ,
ref SpDevinfoData deviceInfoData ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiDestroyDeviceInfoList ( IntPtr deviceInfoSet ) ;
2017-12-06 19:30:03 +00:00
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-20 17:15:26 +00:00
static extern bool SetupDiGetDeviceInstanceId ( IntPtr deviceInfoSet , ref SpDevinfoData deviceInfoData ,
StringBuilder deviceInstanceId , int deviceInstanceIdSize ,
out int requiredSize ) ;
2017-12-06 19:30:03 +00:00
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-19 20:33:03 +00:00
static extern bool DeviceIoControl ( IntPtr hDevice , int dwIoControlCode , IntPtr lpInBuffer , int nInBufferSize ,
IntPtr lpOutBuffer , int nOutBufferSize , out int lpBytesReturned ,
IntPtr lpOverlapped ) ;
2017-12-06 19:30:03 +00:00
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-19 20:33:03 +00:00
static extern IntPtr CreateFile ( string lpFileName , int dwDesiredAccess , int dwShareMode ,
IntPtr lpSecurityAttributes , int dwCreationDisposition ,
int dwFlagsAndAttributes , IntPtr hTemplateFile ) ;
2017-12-06 19:30:03 +00:00
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
2017-12-19 20:33:03 +00:00
static extern bool CloseHandle ( IntPtr hObject ) ;
2017-12-06 19:30:03 +00:00
#endregion
2017-12-23 02:32:02 +00:00
/// <summary>
/// Return a list of USB Host Controllers
/// </summary>
/// <returns>List of USB Host Controllers</returns>
2017-12-22 03:13:43 +00:00
static IEnumerable < UsbController > GetHostControllers ( )
2017-12-06 19:30:03 +00:00
{
2017-12-20 17:15:26 +00:00
List < UsbController > hostList = new List < UsbController > ( ) ;
Guid hostGuid = new Guid ( GUID_DEVINTERFACE_HUBCONTROLLER ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a Hub Controller
2017-12-20 17:15:26 +00:00
IntPtr h = SetupDiGetClassDevs ( ref hostGuid , 0 , IntPtr . Zero , DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE )
2017-12-21 14:30:38 +00:00
return new ReadOnlyCollection < UsbController > ( hostList ) ;
2017-12-21 06:06:19 +00:00
IntPtr ptrBuf = Marshal . AllocHGlobal ( BUFFER_SIZE ) ;
bool success ;
int i = 0 ;
do
2017-12-06 19:30:03 +00:00
{
2017-12-22 03:13:43 +00:00
UsbController host = new UsbController { ControllerIndex = i } ;
2017-12-21 06:06:19 +00:00
2017-12-21 07:36:47 +00:00
// create a Device Interface Data structure
2017-12-21 06:06:19 +00:00
SpDeviceInterfaceData dia = new SpDeviceInterfaceData ( ) ;
dia . cbSize = Marshal . SizeOf ( dia ) ;
2017-12-21 07:36:47 +00:00
// start the enumeration
2017-12-21 06:06:19 +00:00
success = SetupDiEnumDeviceInterfaces ( h , IntPtr . Zero , ref hostGuid , i , ref dia ) ;
if ( success )
2017-12-06 19:30:03 +00:00
{
2017-12-21 07:36:47 +00:00
// build a DevInfo Data structure
2017-12-21 06:06:19 +00:00
SpDevinfoData da = new SpDevinfoData ( ) ;
da . cbSize = Marshal . SizeOf ( da ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// build a Device Interface Detail Data structure
2017-12-22 03:13:43 +00:00
SpDeviceInterfaceDetailData didd =
new SpDeviceInterfaceDetailData { cbSize = 4 + Marshal . SystemDefaultCharSize } ;
// trust me :)
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// now we can get some more detailed information
2017-12-21 06:06:19 +00:00
int nRequiredSize = 0 ;
2017-12-22 03:13:43 +00:00
const int N_BYTES = BUFFER_SIZE ;
if ( SetupDiGetDeviceInterfaceDetail ( h , ref dia , ref didd , N_BYTES , ref nRequiredSize , ref da ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
host . ControllerDevicePath = didd . DevicePath ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// get the Device Description and DriverKeyName
2017-12-21 06:06:19 +00:00
int requiredSize = 0 ;
int regType = REG_SZ ;
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
if ( SetupDiGetDeviceRegistryProperty ( h , ref da , SPDRP_DEVICEDESC , ref regType , ptrBuf ,
BUFFER_SIZE , ref requiredSize ) ) host . ControllerDeviceDesc = Marshal . PtrToStringAuto ( ptrBuf ) ;
if ( SetupDiGetDeviceRegistryProperty ( h , ref da , SPDRP_DRIVER , ref regType , ptrBuf ,
BUFFER_SIZE , ref requiredSize ) ) host . ControllerDriverKeyName = Marshal . PtrToStringAuto ( ptrBuf ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
hostList . Add ( host ) ;
2017-12-19 20:33:03 +00:00
}
2017-12-21 06:06:19 +00:00
i + + ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
while ( success ) ;
Marshal . FreeHGlobal ( ptrBuf ) ;
SetupDiDestroyDeviceInfoList ( h ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// convert it into a Collection
2017-12-21 14:30:38 +00:00
return new ReadOnlyCollection < UsbController > ( hostList ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// Represents a USB Host Controller
/// </summary>
2017-12-22 03:13:43 +00:00
class UsbController
2017-12-06 19:30:03 +00:00
{
internal int ControllerIndex ;
internal string ControllerDriverKeyName , ControllerDevicePath , ControllerDeviceDesc ;
2017-12-23 02:32:02 +00:00
/// <summary>
/// A simple default constructor
/// </summary>
2017-12-20 17:15:26 +00:00
internal UsbController ( )
2017-12-06 19:30:03 +00:00
{
ControllerIndex = 0 ;
ControllerDevicePath = "" ;
ControllerDeviceDesc = "" ;
ControllerDriverKeyName = "" ;
}
2017-12-23 02:32:02 +00:00
/// <summary>Return the index of the instance</summary>
2017-12-22 03:13:43 +00:00
internal int Index = > ControllerIndex ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>Return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0&58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}"</summary>
2017-12-22 03:13:43 +00:00
internal string DevicePath = > ControllerDevicePath ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>The DriverKeyName may be useful as a search key</summary>
2017-12-22 03:13:43 +00:00
internal string DriverKeyName = > ControllerDriverKeyName ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>Return the Friendly Name, such as "VIA USB Enhanced Host Controller"</summary>
2017-12-22 03:13:43 +00:00
internal string Name = > ControllerDeviceDesc ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>Return Root Hub for this Controller</summary>
2017-12-20 17:15:26 +00:00
internal UsbHub GetRootHub ( )
2017-12-06 19:30:03 +00:00
{
IntPtr h , h2 ;
2017-12-22 03:13:43 +00:00
UsbHub root = new UsbHub { HubIsRootHub = true , HubDeviceDesc = "Root Hub" } ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Open a handle to the Host Controller
2017-12-06 19:30:03 +00:00
h = CreateFile ( ControllerDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
2017-12-19 20:33:03 +00:00
IntPtr . Zero ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE ) return root ;
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
UsbRootHubName hubName = new UsbRootHubName ( ) ;
int nBytes = Marshal . SizeOf ( hubName ) ;
IntPtr ptrHubName = Marshal . AllocHGlobal ( nBytes ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// get the Hub Name
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_ROOT_HUB_NAME , ptrHubName , nBytes , ptrHubName , nBytes ,
2017-12-22 03:13:43 +00:00
out _ , IntPtr . Zero ) )
2017-12-21 06:06:19 +00:00
{
hubName = ( UsbRootHubName ) Marshal . PtrToStructure ( ptrHubName , typeof ( UsbRootHubName ) ) ;
root . HubDevicePath = @"\\.\" + hubName . RootHubName ;
}
2017-12-21 07:36:47 +00:00
// TODO: Get DriverKeyName for Root Hub
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Now let's open the Hub (based upon the HubName we got above)
2017-12-21 06:06:19 +00:00
h2 = CreateFile ( root . HubDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
IntPtr . Zero ) ;
if ( h2 . ToInt32 ( ) ! = INVALID_HANDLE_VALUE )
{
2017-12-22 03:13:43 +00:00
UsbNodeInformation nodeInfo = new UsbNodeInformation { NodeType = ( int ) UsbHubNode . UsbHub } ;
2017-12-21 06:06:19 +00:00
nBytes = Marshal . SizeOf ( nodeInfo ) ;
IntPtr ptrNodeInfo = Marshal . AllocHGlobal ( nBytes ) ;
Marshal . StructureToPtr ( nodeInfo , ptrNodeInfo , true ) ;
2017-12-21 07:36:47 +00:00
// get the Hub Information
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h2 , IOCTL_USB_GET_NODE_INFORMATION , ptrNodeInfo , nBytes , ptrNodeInfo , nBytes ,
2017-12-22 03:13:43 +00:00
out _ , IntPtr . Zero ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
nodeInfo = ( UsbNodeInformation ) Marshal . PtrToStructure ( ptrNodeInfo ,
typeof ( UsbNodeInformation ) ) ;
root . HubIsBusPowered = Convert . ToBoolean ( nodeInfo . HubInformation . HubIsBusPowered ) ;
root . HubPortCount = nodeInfo . HubInformation . HubDescriptor . bNumberOfPorts ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrNodeInfo ) ;
CloseHandle ( h2 ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrHubName ) ;
CloseHandle ( h ) ;
2017-12-20 17:15:26 +00:00
return root ;
2017-12-06 19:30:03 +00:00
}
}
2017-12-23 02:32:02 +00:00
/// <summary>The Hub class</summary>
2017-12-20 17:15:26 +00:00
internal class UsbHub
2017-12-06 19:30:03 +00:00
{
internal int HubPortCount ;
internal string HubDriverKey , HubDevicePath , HubDeviceDesc ;
2017-12-20 17:15:26 +00:00
internal string HubManufacturer , HubProduct , HubSerialNumber , HubInstanceId ;
2017-12-06 19:30:03 +00:00
internal bool HubIsBusPowered , HubIsRootHub ;
2017-12-23 02:32:02 +00:00
/// <summary>a simple default constructor</summary>
2017-12-20 17:15:26 +00:00
internal UsbHub ( )
2017-12-06 19:30:03 +00:00
{
HubPortCount = 0 ;
HubDevicePath = "" ;
HubDeviceDesc = "" ;
HubDriverKey = "" ;
HubIsBusPowered = false ;
HubIsRootHub = false ;
HubManufacturer = "" ;
HubProduct = "" ;
HubSerialNumber = "" ;
2017-12-20 17:15:26 +00:00
HubInstanceId = "" ;
2017-12-06 19:30:03 +00:00
}
2017-12-23 02:32:02 +00:00
/// <summary>return Port Count</summary>
2017-12-22 03:13:43 +00:00
internal int PortCount = > HubPortCount ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0&58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}"</summary>
2017-12-22 03:13:43 +00:00
internal string DevicePath = > HubDevicePath ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>The DriverKey may be useful as a search key</summary>
2017-12-22 03:13:43 +00:00
internal string DriverKey = > HubDriverKey ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>return the Friendly Name, such as "VIA USB Enhanced Host Controller"</summary>
2017-12-22 03:13:43 +00:00
internal string Name = > HubDeviceDesc ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>the device path of this device</summary>
2017-12-22 03:13:43 +00:00
internal string InstanceId = > HubInstanceId ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>is is this a self-powered hub?</summary>
2017-12-22 03:13:43 +00:00
internal bool IsBusPowered = > HubIsBusPowered ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>is this a root hub?</summary>
2017-12-22 03:13:43 +00:00
internal bool IsRootHub = > HubIsRootHub ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string Manufacturer = > HubManufacturer ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string Product = > HubProduct ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string SerialNumber = > HubSerialNumber ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>
/// return a list of the down stream ports
/// </summary>
/// <returns>List of downstream ports</returns>
2017-12-22 03:13:43 +00:00
internal IEnumerable < UsbPort > GetPorts ( )
2017-12-06 19:30:03 +00:00
{
2017-12-20 17:15:26 +00:00
List < UsbPort > portList = new List < UsbPort > ( ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Open a handle to the Hub device
2017-12-06 19:30:03 +00:00
IntPtr h = CreateFile ( HubDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
2017-12-19 20:33:03 +00:00
IntPtr . Zero ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE )
2017-12-21 14:30:38 +00:00
return new ReadOnlyCollection < UsbPort > ( portList ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
int nBytes = Marshal . SizeOf ( typeof ( UsbNodeConnectionInformationEx ) ) ;
IntPtr ptrNodeConnection = Marshal . AllocHGlobal ( nBytes ) ;
2017-12-19 20:33:03 +00:00
2017-12-21 07:36:47 +00:00
// loop thru all of the ports on the hub
// BTW: Ports are numbered starting at 1
2017-12-21 06:06:19 +00:00
for ( int i = 1 ; i < = HubPortCount ; i + + )
{
2017-12-22 03:13:43 +00:00
UsbNodeConnectionInformationEx nodeConnection =
new UsbNodeConnectionInformationEx { ConnectionIndex = i } ;
2017-12-21 06:06:19 +00:00
Marshal . StructureToPtr ( nodeConnection , ptrNodeConnection , true ) ;
if ( ! DeviceIoControl ( h , IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX , ptrNodeConnection , nBytes ,
2017-12-22 03:13:43 +00:00
ptrNodeConnection , nBytes , out _ , IntPtr . Zero ) ) continue ;
2017-12-21 06:06:19 +00:00
nodeConnection =
( UsbNodeConnectionInformationEx ) Marshal . PtrToStructure ( ptrNodeConnection ,
typeof (
UsbNodeConnectionInformationEx
) ) ;
2017-12-21 07:36:47 +00:00
// load up the USBPort class
2017-12-22 03:13:43 +00:00
UsbPort port = new UsbPort
{
PortPortNumber = i ,
PortHubDevicePath = HubDevicePath ,
PortStatus = ( ( UsbConnectionStatus ) nodeConnection . ConnectionStatus ) . ToString ( ) ,
PortSpeed = ( ( UsbDeviceSpeed ) nodeConnection . Speed ) . ToString ( ) ,
PortIsDeviceConnected =
nodeConnection . ConnectionStatus = = ( int ) UsbConnectionStatus . DeviceConnected ,
PortIsHub = Convert . ToBoolean ( nodeConnection . DeviceIsHub ) ,
PortDeviceDescriptor = nodeConnection . DeviceDescriptor
} ;
2017-12-21 06:06:19 +00:00
2017-12-21 07:36:47 +00:00
// add it to the list
2017-12-21 06:06:19 +00:00
portList . Add ( port ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrNodeConnection ) ;
CloseHandle ( h ) ;
2017-12-21 07:36:47 +00:00
// convert it into a Collection
2017-12-21 14:30:38 +00:00
return new ReadOnlyCollection < UsbPort > ( portList ) ;
2017-12-06 19:30:03 +00:00
}
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// Represents an USB port
/// </summary>
2017-12-20 17:15:26 +00:00
internal class UsbPort
2017-12-06 19:30:03 +00:00
{
internal int PortPortNumber ;
internal string PortStatus , PortHubDevicePath , PortSpeed ;
internal bool PortIsHub , PortIsDeviceConnected ;
2017-12-20 17:15:26 +00:00
internal UsbDeviceDescriptor PortDeviceDescriptor ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>a simple default constructor</summary>
2017-12-20 17:15:26 +00:00
internal UsbPort ( )
2017-12-06 19:30:03 +00:00
{
PortPortNumber = 0 ;
PortStatus = "" ;
PortHubDevicePath = "" ;
PortSpeed = "" ;
PortIsHub = false ;
PortIsDeviceConnected = false ;
}
2017-12-23 02:32:02 +00:00
/// <summary>return Port Index of the Hub</summary>
2017-12-22 03:13:43 +00:00
internal int PortNumber = > PortPortNumber ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>return the Device Path of the Hub</summary>
2017-12-22 03:13:43 +00:00
internal string HubDevicePath = > PortHubDevicePath ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>the status (see USB_CONNECTION_STATUS above)</summary>
2017-12-22 03:13:43 +00:00
internal string Status = > PortStatus ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>the speed of the connection (see USB_DEVICE_SPEED above)</summary>
2017-12-22 03:13:43 +00:00
internal string Speed = > PortSpeed ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>is this a downstream external hub?</summary>
2017-12-22 03:13:43 +00:00
internal bool IsHub = > PortIsHub ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>is anybody home?</summary>
2017-12-22 03:13:43 +00:00
internal bool IsDeviceConnected = > PortIsDeviceConnected ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>
/// return a down stream external hub
/// </summary>
/// <returns>Downstream external hub</returns>
2017-12-20 17:15:26 +00:00
internal UsbDevice GetDevice ( )
2017-12-06 19:30:03 +00:00
{
2017-12-20 23:07:46 +00:00
if ( ! PortIsDeviceConnected ) return null ;
2017-12-19 20:33:03 +00:00
2017-12-21 07:36:47 +00:00
// Copy over some values from the Port class
// Ya know, I've given some thought about making Device a derived class...
2017-12-22 03:13:43 +00:00
UsbDevice device = new UsbDevice
{
DevicePortNumber = PortPortNumber ,
DeviceHubDevicePath = PortHubDevicePath ,
DeviceDescriptor = PortDeviceDescriptor
} ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Open a handle to the Hub device
2017-12-06 19:30:03 +00:00
IntPtr h = CreateFile ( PortHubDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
2017-12-19 20:33:03 +00:00
IntPtr . Zero ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE ) return device ;
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
int nBytesReturned ;
int nBytes = BUFFER_SIZE ;
2017-12-21 07:36:47 +00:00
// We use this to zero fill a buffer
2017-12-21 06:06:19 +00:00
string nullString = new string ( ( char ) 0 , BUFFER_SIZE / Marshal . SystemDefaultCharSize ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// The iManufacturer, iProduct and iSerialNumber entries in the
// Device Descriptor are really just indexes. So, we have to
// request a String Descriptor to get the values for those strings.
2017-12-21 06:06:19 +00:00
if ( PortDeviceDescriptor . iManufacturer > 0 )
{
2017-12-21 07:36:47 +00:00
// build a request for string descriptor
2017-12-22 03:13:43 +00:00
UsbDescriptorRequest request = new UsbDescriptorRequest
{
ConnectionIndex = PortPortNumber ,
SetupPacket =
{
// Language Code
wIndex = 0x409 ,
wValue = ( short ) ( ( USB_STRING_DESCRIPTOR_TYPE < < 8 ) + PortDeviceDescriptor . iManufacturer )
}
} ;
2017-12-21 06:06:19 +00:00
request . SetupPacket . wLength = ( short ) ( nBytes - Marshal . SizeOf ( request ) ) ;
2017-12-21 07:36:47 +00:00
// Geez, I wish C# had a Marshal.MemSet() method
2017-12-21 06:06:19 +00:00
IntPtr ptrRequest = Marshal . StringToHGlobalAuto ( nullString ) ;
Marshal . StructureToPtr ( request , ptrRequest , true ) ;
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the String Descriptor
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , ptrRequest , nBytes ,
ptrRequest , nBytes , out nBytesReturned , IntPtr . Zero ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 07:36:47 +00:00
// The location of the string descriptor is immediately after
// the Request structure. Because this location is not "covered"
// by the structure allocation, we're forced to zero out this
// chunk of memory by using the StringToHGlobalAuto() hack above
2017-12-21 06:06:19 +00:00
IntPtr ptrStringDesc = new IntPtr ( ptrRequest . ToInt32 ( ) + Marshal . SizeOf ( request ) ) ;
UsbStringDescriptor stringDesc =
( UsbStringDescriptor ) Marshal . PtrToStructure ( ptrStringDesc ,
typeof ( UsbStringDescriptor ) ) ;
device . DeviceManufacturer = stringDesc . bString ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrRequest ) ;
}
if ( PortDeviceDescriptor . iProduct > 0 )
{
2017-12-21 07:36:47 +00:00
// build a request for string descriptor
2017-12-22 03:13:43 +00:00
UsbDescriptorRequest request = new UsbDescriptorRequest
{
ConnectionIndex = PortPortNumber ,
SetupPacket =
{
// Language Code
wIndex = 0x409 ,
wValue = ( short ) ( ( USB_STRING_DESCRIPTOR_TYPE < < 8 ) + PortDeviceDescriptor . iProduct )
}
} ;
2017-12-21 06:06:19 +00:00
request . SetupPacket . wLength = ( short ) ( nBytes - Marshal . SizeOf ( request ) ) ;
2017-12-21 07:36:47 +00:00
// Geez, I wish C# had a Marshal.MemSet() method
2017-12-21 06:06:19 +00:00
IntPtr ptrRequest = Marshal . StringToHGlobalAuto ( nullString ) ;
Marshal . StructureToPtr ( request , ptrRequest , true ) ;
2017-12-06 20:38:10 +00:00
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the String Descriptor
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , ptrRequest , nBytes ,
ptrRequest , nBytes , out nBytesReturned , IntPtr . Zero ) )
2017-12-06 20:38:10 +00:00
{
2017-12-21 07:36:47 +00:00
// the location of the string descriptor is immediately after the Request structure
2017-12-21 06:06:19 +00:00
IntPtr ptrStringDesc = new IntPtr ( ptrRequest . ToInt32 ( ) + Marshal . SizeOf ( request ) ) ;
UsbStringDescriptor stringDesc =
( UsbStringDescriptor ) Marshal . PtrToStructure ( ptrStringDesc ,
typeof ( UsbStringDescriptor ) ) ;
device . DeviceProduct = stringDesc . bString ;
2017-12-06 20:38:10 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrRequest ) ;
}
if ( PortDeviceDescriptor . iSerialNumber > 0 )
{
2017-12-21 07:36:47 +00:00
// build a request for string descriptor
2017-12-22 03:13:43 +00:00
UsbDescriptorRequest request = new UsbDescriptorRequest
{
ConnectionIndex = PortPortNumber ,
SetupPacket =
{
// Language Code
wIndex = 0x409 ,
wValue = ( short ) ( ( USB_STRING_DESCRIPTOR_TYPE < < 8 ) + PortDeviceDescriptor . iSerialNumber )
}
} ;
2017-12-21 06:06:19 +00:00
request . SetupPacket . wLength = ( short ) ( nBytes - Marshal . SizeOf ( request ) ) ;
2017-12-21 07:36:47 +00:00
// Geez, I wish C# had a Marshal.MemSet() method
2017-12-21 06:06:19 +00:00
IntPtr ptrRequest = Marshal . StringToHGlobalAuto ( nullString ) ;
Marshal . StructureToPtr ( request , ptrRequest , true ) ;
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the String Descriptor
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , ptrRequest , nBytes ,
ptrRequest , nBytes , out nBytesReturned , IntPtr . Zero ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 07:36:47 +00:00
// the location of the string descriptor is immediately after the Request structure
2017-12-21 06:06:19 +00:00
IntPtr ptrStringDesc = new IntPtr ( ptrRequest . ToInt32 ( ) + Marshal . SizeOf ( request ) ) ;
UsbStringDescriptor stringDesc =
( UsbStringDescriptor ) Marshal . PtrToStructure ( ptrStringDesc ,
typeof ( UsbStringDescriptor ) ) ;
device . DeviceSerialNumber = stringDesc . bString ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrRequest ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
2017-12-21 07:36:47 +00:00
// build a request for configuration descriptor
2017-12-22 03:13:43 +00:00
UsbDescriptorRequest dcrRequest = new UsbDescriptorRequest
{
ConnectionIndex = PortPortNumber ,
SetupPacket = { wIndex = 0 , wValue = USB_CONFIGURATION_DESCRIPTOR_TYPE < < 8 }
} ;
2017-12-21 06:06:19 +00:00
dcrRequest . SetupPacket . wLength = ( short ) ( nBytes - Marshal . SizeOf ( dcrRequest ) ) ;
2017-12-21 07:36:47 +00:00
// Geez, I wish C# had a Marshal.MemSet() method
2017-12-21 06:06:19 +00:00
IntPtr dcrPtrRequest = Marshal . StringToHGlobalAuto ( nullString ) ;
Marshal . StructureToPtr ( dcrRequest , dcrPtrRequest , true ) ;
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the String Descriptor
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION , dcrPtrRequest , nBytes ,
dcrPtrRequest , nBytes , out nBytesReturned , IntPtr . Zero ) )
{
IntPtr ptrStringDesc = new IntPtr ( dcrPtrRequest . ToInt32 ( ) + Marshal . SizeOf ( dcrRequest ) ) ;
device . BinaryDeviceDescriptors = new byte [ nBytesReturned ] ;
Marshal . Copy ( ptrStringDesc , device . BinaryDeviceDescriptors , 0 , nBytesReturned ) ;
}
Marshal . FreeHGlobal ( dcrPtrRequest ) ;
2017-12-21 07:36:47 +00:00
// Get the Driver Key Name (usefull in locating a device)
2017-12-22 03:13:43 +00:00
UsbNodeConnectionDriverkeyName driverKey =
new UsbNodeConnectionDriverkeyName { ConnectionIndex = PortPortNumber } ;
2017-12-21 06:06:19 +00:00
nBytes = Marshal . SizeOf ( driverKey ) ;
IntPtr ptrDriverKey = Marshal . AllocHGlobal ( nBytes ) ;
Marshal . StructureToPtr ( driverKey , ptrDriverKey , true ) ;
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the Driver Key Name
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME , ptrDriverKey , nBytes ,
ptrDriverKey , nBytes , out nBytesReturned , IntPtr . Zero ) )
{
driverKey = ( UsbNodeConnectionDriverkeyName ) Marshal . PtrToStructure ( ptrDriverKey ,
typeof (
UsbNodeConnectionDriverkeyName
) ) ;
device . DeviceDriverKey = driverKey . DriverKeyName ;
2017-12-21 07:36:47 +00:00
// use the DriverKeyName to get the Device Description and Instance ID
2017-12-21 06:06:19 +00:00
device . DeviceName = GetDescriptionByKeyName ( device . DeviceDriverKey ) ;
device . DeviceInstanceId = GetInstanceIdByKeyName ( device . DeviceDriverKey ) ;
}
Marshal . FreeHGlobal ( ptrDriverKey ) ;
CloseHandle ( h ) ;
2017-12-20 17:15:26 +00:00
return device ;
2017-12-06 19:30:03 +00:00
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// return a down stream external hub
/// </summary>
/// <returns>Downstream external hub</returns>
2017-12-20 17:15:26 +00:00
internal UsbHub GetHub ( )
2017-12-06 19:30:03 +00:00
{
2017-12-20 23:07:46 +00:00
if ( ! PortIsHub ) return null ;
2017-12-19 20:33:03 +00:00
2017-12-20 17:15:26 +00:00
UsbHub hub = new UsbHub ( ) ;
2017-12-06 19:30:03 +00:00
IntPtr h , h2 ;
2017-12-20 17:15:26 +00:00
hub . HubIsRootHub = false ;
hub . HubDeviceDesc = "External Hub" ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Open a handle to the Host Controller
2017-12-06 19:30:03 +00:00
h = CreateFile ( PortHubDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
2017-12-19 20:33:03 +00:00
IntPtr . Zero ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE ) return hub ;
2017-12-21 07:36:47 +00:00
// Get the DevicePath for downstream hub
2017-12-22 03:13:43 +00:00
UsbNodeConnectionName nodeName = new UsbNodeConnectionName { ConnectionIndex = PortPortNumber } ;
2017-12-21 06:06:19 +00:00
int nBytes = Marshal . SizeOf ( nodeName ) ;
IntPtr ptrNodeName = Marshal . AllocHGlobal ( nBytes ) ;
Marshal . StructureToPtr ( nodeName , ptrNodeName , true ) ;
2017-12-21 07:36:47 +00:00
// Use an IOCTL call to request the Node Name
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h , IOCTL_USB_GET_NODE_CONNECTION_NAME , ptrNodeName , nBytes , ptrNodeName , nBytes ,
2017-12-22 03:13:43 +00:00
out _ , IntPtr . Zero ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
nodeName = ( UsbNodeConnectionName ) Marshal . PtrToStructure ( ptrNodeName ,
typeof ( UsbNodeConnectionName ) ) ;
hub . HubDevicePath = @"\\.\" + nodeName . NodeName ;
}
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Now let's open the Hub (based upon the HubName we got above)
2017-12-21 06:06:19 +00:00
h2 = CreateFile ( hub . HubDevicePath , GENERIC_WRITE , FILE_SHARE_WRITE , IntPtr . Zero , OPEN_EXISTING , 0 ,
IntPtr . Zero ) ;
if ( h2 . ToInt32 ( ) ! = INVALID_HANDLE_VALUE )
{
2017-12-22 03:13:43 +00:00
UsbNodeInformation nodeInfo = new UsbNodeInformation { NodeType = ( int ) UsbHubNode . UsbHub } ;
2017-12-21 06:06:19 +00:00
nBytes = Marshal . SizeOf ( nodeInfo ) ;
IntPtr ptrNodeInfo = Marshal . AllocHGlobal ( nBytes ) ;
Marshal . StructureToPtr ( nodeInfo , ptrNodeInfo , true ) ;
2017-12-21 07:36:47 +00:00
// get the Hub Information
2017-12-21 06:06:19 +00:00
if ( DeviceIoControl ( h2 , IOCTL_USB_GET_NODE_INFORMATION , ptrNodeInfo , nBytes , ptrNodeInfo , nBytes ,
2017-12-22 03:13:43 +00:00
out _ , IntPtr . Zero ) )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
nodeInfo = ( UsbNodeInformation ) Marshal . PtrToStructure ( ptrNodeInfo ,
typeof ( UsbNodeInformation ) ) ;
hub . HubIsBusPowered = Convert . ToBoolean ( nodeInfo . HubInformation . HubIsBusPowered ) ;
hub . HubPortCount = nodeInfo . HubInformation . HubDescriptor . bNumberOfPorts ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
Marshal . FreeHGlobal ( ptrNodeInfo ) ;
CloseHandle ( h2 ) ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
2017-12-21 07:36:47 +00:00
// Fill in the missing Manufacture, Product, and SerialNumber values
// values by just creating a Device instance and copying the values
2017-12-21 06:06:19 +00:00
UsbDevice device = GetDevice ( ) ;
hub . HubInstanceId = device . DeviceInstanceId ;
hub . HubManufacturer = device . Manufacturer ;
hub . HubProduct = device . Product ;
hub . HubSerialNumber = device . SerialNumber ;
hub . HubDriverKey = device . DriverKey ;
Marshal . FreeHGlobal ( ptrNodeName ) ;
CloseHandle ( h ) ;
2017-12-20 17:15:26 +00:00
return hub ;
2017-12-06 19:30:03 +00:00
}
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// Represents an USB device
/// </summary>
2017-12-20 17:15:26 +00:00
internal class UsbDevice
2017-12-06 19:30:03 +00:00
{
internal int DevicePortNumber ;
2017-12-20 17:15:26 +00:00
internal string DeviceDriverKey , DeviceHubDevicePath , DeviceInstanceId , DeviceName ;
2017-12-06 19:30:03 +00:00
internal string DeviceManufacturer , DeviceProduct , DeviceSerialNumber ;
2017-12-20 17:15:26 +00:00
internal UsbDeviceDescriptor DeviceDescriptor ;
2017-12-06 20:38:10 +00:00
internal byte [ ] BinaryDeviceDescriptors ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>a simple default constructor</summary>
2017-12-20 17:15:26 +00:00
internal UsbDevice ( )
2017-12-06 19:30:03 +00:00
{
DevicePortNumber = 0 ;
DeviceHubDevicePath = "" ;
DeviceDriverKey = "" ;
DeviceManufacturer = "" ;
DeviceProduct = "Unknown USB Device" ;
DeviceSerialNumber = "" ;
DeviceName = "" ;
2017-12-20 17:15:26 +00:00
DeviceInstanceId = "" ;
2017-12-06 20:38:10 +00:00
BinaryDeviceDescriptors = null ;
2017-12-06 19:30:03 +00:00
}
2017-12-23 02:32:02 +00:00
/// <summary>return Port Index of the Hub</summary>
2017-12-22 03:13:43 +00:00
internal int PortNumber = > DevicePortNumber ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>return the Device Path of the Hub (the parent device)</summary>
2017-12-22 03:13:43 +00:00
internal string HubDevicePath = > DeviceHubDevicePath ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>useful as a search key</summary>
2017-12-22 03:13:43 +00:00
internal string DriverKey = > DeviceDriverKey ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>the device path of this device</summary>
2017-12-22 03:13:43 +00:00
internal string InstanceId = > DeviceInstanceId ;
2017-12-06 19:30:03 +00:00
2017-12-23 02:32:02 +00:00
/// <summary>the friendly name</summary>
2017-12-22 03:13:43 +00:00
internal string Name = > DeviceName ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string Manufacturer = > DeviceManufacturer ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string Product = > DeviceProduct ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
internal string SerialNumber = > DeviceSerialNumber ;
2017-12-19 20:33:03 +00:00
2017-12-22 03:13:43 +00:00
internal byte [ ] BinaryDescriptors = > BinaryDeviceDescriptors ;
2017-12-06 19:30:03 +00:00
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// private function for finding a USB device's Description
/// </summary>
/// <param name="driverKeyName">Device driver key name</param>
/// <returns>USB device description</returns>
2017-12-20 17:15:26 +00:00
static string GetDescriptionByKeyName ( string driverKeyName )
2017-12-06 19:30:03 +00:00
{
string ans = "" ;
2017-12-22 03:13:43 +00:00
const string DEV_ENUM = REGSTR_KEY_USB ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Use the "enumerator form" of the SetupDiGetClassDevs API
// to generate a list of all USB devices
2017-12-22 03:13:43 +00:00
IntPtr h = SetupDiGetClassDevs ( 0 , DEV_ENUM , IntPtr . Zero , DIGCF_PRESENT | DIGCF_ALLCLASSES ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE ) return ans ;
IntPtr ptrBuf = Marshal . AllocHGlobal ( BUFFER_SIZE ) ;
bool success ;
int i = 0 ;
do
2017-12-06 19:30:03 +00:00
{
2017-12-21 07:36:47 +00:00
// create a Device Interface Data structure
2017-12-21 06:06:19 +00:00
SpDevinfoData da = new SpDevinfoData ( ) ;
da . cbSize = Marshal . SizeOf ( da ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// start the enumeration
2017-12-21 06:06:19 +00:00
success = SetupDiEnumDeviceInfo ( h , i , ref da ) ;
if ( success )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
int requiredSize = 0 ;
int regType = REG_SZ ;
2017-12-22 03:13:43 +00:00
string keyName = "" ;
2017-12-21 06:06:19 +00:00
if ( SetupDiGetDeviceRegistryProperty ( h , ref da , SPDRP_DRIVER , ref regType , ptrBuf , BUFFER_SIZE ,
ref requiredSize ) ) keyName = Marshal . PtrToStringAuto ( ptrBuf ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// is it a match?
2017-12-21 06:06:19 +00:00
if ( keyName = = driverKeyName )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
if ( SetupDiGetDeviceRegistryProperty ( h , ref da , SPDRP_DEVICEDESC , ref regType , ptrBuf ,
BUFFER_SIZE , ref requiredSize ) ) ans = Marshal . PtrToStringAuto ( ptrBuf ) ;
break ;
2017-12-06 19:30:03 +00:00
}
2017-12-19 20:33:03 +00:00
}
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
i + + ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
while ( success ) ;
Marshal . FreeHGlobal ( ptrBuf ) ;
SetupDiDestroyDeviceInfoList ( h ) ;
2017-12-19 20:33:03 +00:00
2017-12-06 19:30:03 +00:00
return ans ;
}
2017-12-23 02:32:02 +00:00
/// <summary>
/// private function for finding a USB device's Instance ID
/// </summary>
/// <param name="driverKeyName">Device driver key name</param>
/// <returns>Device instance ID</returns>
2017-12-20 17:15:26 +00:00
static string GetInstanceIdByKeyName ( string driverKeyName )
2017-12-06 19:30:03 +00:00
{
string ans = "" ;
2017-12-22 03:13:43 +00:00
const string DEV_ENUM = REGSTR_KEY_USB ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// Use the "enumerator form" of the SetupDiGetClassDevs API
// to generate a list of all USB devices
2017-12-22 03:13:43 +00:00
IntPtr h = SetupDiGetClassDevs ( 0 , DEV_ENUM , IntPtr . Zero , DIGCF_PRESENT | DIGCF_ALLCLASSES ) ;
2017-12-21 06:06:19 +00:00
if ( h . ToInt32 ( ) = = INVALID_HANDLE_VALUE ) return ans ;
IntPtr ptrBuf = Marshal . AllocHGlobal ( BUFFER_SIZE ) ;
bool success ;
int i = 0 ;
do
2017-12-06 19:30:03 +00:00
{
2017-12-21 07:36:47 +00:00
// create a Device Interface Data structure
2017-12-21 06:06:19 +00:00
SpDevinfoData da = new SpDevinfoData ( ) ;
da . cbSize = Marshal . SizeOf ( da ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// start the enumeration
2017-12-21 06:06:19 +00:00
success = SetupDiEnumDeviceInfo ( h , i , ref da ) ;
if ( success )
2017-12-06 19:30:03 +00:00
{
2017-12-21 06:06:19 +00:00
int requiredSize = 0 ;
int regType = REG_SZ ;
2017-12-06 19:30:03 +00:00
2017-12-22 03:13:43 +00:00
string keyName = "" ;
2017-12-21 06:06:19 +00:00
if ( SetupDiGetDeviceRegistryProperty ( h , ref da , SPDRP_DRIVER , ref regType , ptrBuf , BUFFER_SIZE ,
ref requiredSize ) ) keyName = Marshal . PtrToStringAuto ( ptrBuf ) ;
2017-12-06 19:30:03 +00:00
2017-12-21 07:36:47 +00:00
// is it a match?
2017-12-21 06:06:19 +00:00
if ( keyName = = driverKeyName )
{
2017-12-22 03:13:43 +00:00
const int N_BYTES = BUFFER_SIZE ;
StringBuilder sb = new StringBuilder ( N_BYTES ) ;
SetupDiGetDeviceInstanceId ( h , ref da , sb , N_BYTES , out requiredSize ) ;
2017-12-21 06:06:19 +00:00
ans = sb . ToString ( ) ;
break ;
2017-12-06 19:30:03 +00:00
}
2017-12-19 20:33:03 +00:00
}
2017-12-06 19:30:03 +00:00
2017-12-21 06:06:19 +00:00
i + + ;
2017-12-06 19:30:03 +00:00
}
2017-12-21 06:06:19 +00:00
while ( success ) ;
Marshal . FreeHGlobal ( ptrBuf ) ;
SetupDiDestroyDeviceInfoList ( h ) ;
2017-12-19 20:33:03 +00:00
2017-12-06 19:30:03 +00:00
return ans ;
}
}
2017-12-19 20:33:03 +00:00
}