{*******************************************************}
{                                                       }
{       nrCommLib Communication Library for Delphi      }
{       USB definitions and routines for Win32          }
{       Copyright (c) 1997-2011 Roman Novgorodov        }
{       v9.27                                           }
{       last update: 07 Apr 2009                        }
{                                                       }
{       e-mail:noro@deepsoftware.com                    }
{       DeepSoftware                                    }
{       http://www.deepsoftware.com/                    }
{                                                       }
{*******************************************************}
{$I nrcommlib.inc}
{$MINENUMSIZE 4}
unit nrusbw32;
interface
uses windows, nrwin32;

Const

  MAX_CountUsbPorts:integer = 16;

  FILE_DEVICE_USB           = FILE_DEVICE_UNKNOWN;
  USB_IOCTL_INTERNAL_INDEX  = 0;
  USB_IOCTL_INDEX           = $00ff;

  FILE_ANY_ACCESS           = 0;
  FILE_SPECIAL_ACCESS       = FILE_ANY_ACCESS;
  FILE_READ_ACCESS          = 1;
  FILE_WRITE_ACCESS         = 2;
  METHOD_BUFFERED           = 0;
  METHOD_IN_DIRECT          = 1;
  METHOD_OUT_DIRECT         = 2;
  METHOD_NEITHER            = 3;

  {$IFNDEF VER100}
  {$EXTERNALSYM FILE_ANY_ACCESS}
  {$EXTERNALSYM FILE_SPECIAL_ACCESS}
  {$EXTERNALSYM FILE_READ_ACCESS}
  {$EXTERNALSYM FILE_WRITE_ACCESS}
  {$EXTERNALSYM METHOD_BUFFERED}
  {$EXTERNALSYM METHOD_IN_DIRECT}
  {$EXTERNALSYM METHOD_OUT_DIRECT}
  {$EXTERNALSYM METHOD_NEITHER}
  {$ENDIF}

  USB_DEVICE_DESCRIPTOR_TYPE                = $01;
  USB_CONFIGURATION_DESCRIPTOR_TYPE         = $02;
  USB_STRING_DESCRIPTOR_TYPE                = $03;
  USB_INTERFACE_DESCRIPTOR_TYPE             = $04;
  USB_ENDPOINT_DESCRIPTOR_TYPE              = $05;
  USB_RESERVED_DESCRIPTOR_TYPE              = $06;
  USB_CONFIG_POWER_DESCRIPTOR_TYPE          = $07;
  USB_INTERFACE_POWER_DESCRIPTOR_TYPE       = $08;

  IOCTL_GET_HCD_DRIVERKEY_NAME=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+10) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_ROOT_HUB_NAME=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+3) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_NODE_INFORMATION=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+3) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_NODE_CONNECTION_INFORMATION=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+4) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((274) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+9) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_NODE_CONNECTION_NAME=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+6) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+5) shl 2) or
       METHOD_BUFFERED);

  IOCTL_INTERNAL_USB_SUBMIT_URB =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INTERNAL_INDEX+5) shl 2) or
       METHOD_NEITHER);

  IOCTL_INTERNAL_USB_RESET_PORT=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INTERNAL_INDEX+1) shl 2) or
       METHOD_NEITHER);
                                               
  IOCTL_INTERNAL_USB_ENABLE_PORT=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INTERNAL_INDEX+5) shl 2) or
       METHOD_NEITHER);

  IOCTL_INTERNAL_USB_GET_PORT_STATUS=(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INTERNAL_INDEX+4) shl 2) or
       METHOD_NEITHER);

  IOCTL_USB_HCD_DISABLE_PORT =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+13) shl 2) or
       METHOD_BUFFERED);

  IOCTL_USB_HCD_ENABLE_PORT =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((USB_IOCTL_INDEX+14) shl 2) or
       METHOD_BUFFERED);

  HCD_USER_REQUEST = 270;

  IOCTL_USB_USER_REQUEST  =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       ((HCD_USER_REQUEST) shl 2) or
       METHOD_BUFFERED);

  IOCTL_NRUSB_TEST  =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       (($801) shl 2) or
       METHOD_BUFFERED);

  IOCTL_NRUSB_OPEN  =(
       (FILE_DEVICE_USB shl 16) or
       (FILE_ANY_ACCESS shl 14)or
       (($802) shl 2) or
       METHOD_BUFFERED);

  //
  //  URB request codes
  //

  URB_FUNCTION_SELECT_CONFIGURATION         =   $0000;
  URB_FUNCTION_SELECT_INTERFACE             =   $0001;
  URB_FUNCTION_ABORT_PIPE                   =   $0002;
  URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL    =   $0003;
  URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL =   $0004;
  URB_FUNCTION_GET_FRAME_LENGTH             =   $0005;
  URB_FUNCTION_SET_FRAME_LENGTH             =   $0006;
  URB_FUNCTION_GET_CURRENT_FRAME_NUMBER     =   $0007;
  URB_FUNCTION_CONTROL_TRANSFER             =   $0008;
  URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER   =   $0009;
  URB_FUNCTION_ISOCH_TRANSFER               =   $000A;
  URB_FUNCTION_RESET_PIPE                   =   $001E;

Type

  TnrUsbTransferType=(uttUnknown,uttControl,uttIsochronous,
                      uttBulk,uttInterrupt);
  TnrUSBSpeed=(usLow,usFull,usHigh);


{$IFDEF GEN3}
  Int64=array[0..1] of integer;
{$ENDIF}

  PHYSICAL_ADDRESS = int64;

  USB_HCD_DRIVERKEY_NAME = packed record
    ActualLength:cardinal;// OUTPUT
    DriverKeyName:WChar;  // NULL terminated unicode driverkeyname for hcd
  end;
  PUSB_HCD_DRIVERKEY_NAME=^USB_HCD_DRIVERKEY_NAME;

  USB_NODE_CONNECTION_NAME = packed record
    ConnectionIndex:cardinal; // INPUT
    ActualLength:cardinal;    // OUTPUT
    NodeName:PWChar;
  end;
  PUSB_NODE_CONNECTION_NAME=^USB_NODE_CONNECTION_NAME;

  USB_HUB_DESCRIPTOR = packed record
    bDescriptorLength:byte; // Length of this descriptor
    bDescriptorType:byte;   // Hub configuration type
    bNumberOfPorts:byte;       // number of ports on this hub
    wHubCharacteristics:word;  // Hub Charateristics
    bPowerOnToPowerGood:byte;  // port power on till power good in 2ms
    bHubControlCurrent:byte;   // max current in mA
    // room for 255 ports power control and removable bitmask
    bRemoveAndPowerMask:array [0..63]of byte;
  end;
  PUSB_HUB_DESCRIPTOR=^USB_HUB_DESCRIPTOR;


  USB_HUB_INFORMATION = packed record
    HubDescriptor:USB_HUB_DESCRIPTOR;
    HubIsBusPowered:BOOL;
  end;
  PUSB_HUB_INFORMATION=^USB_HUB_INFORMATION;

  USB_MI_PARENT_INFORMATION = packed record
    NumberOfInterfaces:cardinal;
  end;
  PUSB_MI_PARENT_INFORMATION=^USB_MI_PARENT_INFORMATION;


  USB_HUB_NODE=(UsbHub,UsbMIParent);

  USB_NODE_INFORMATION =packed record
    NodeType:integer;//USB_HUB_NODE; //hub, mi parent
    case Integer of
      0:(HubInformation:USB_HUB_INFORMATION);
      1:(MiParentInformation:USB_MI_PARENT_INFORMATION);
  end;
  PUSB_NODE_INFORMATION=^USB_NODE_INFORMATION;

  USB_DEVICE_DESCRIPTOR = packed record
    bLength:byte;
    bDescriptorType:byte;
    bcdUSB:word;
    bDeviceClass:byte;
    bDeviceSubClass:byte;
    bDeviceProtocol:byte;
    bMaxPacketSize0:byte;
    idVendor:word;
    idProduct:word;
    bcdDevice:word;
    iManufacturer:byte;
    iProduct:byte;
    iSerialNumber:byte;
    bNumConfigurations:byte;
  end;
  PUSB_DEVICE_DESCRIPTOR=^USB_DEVICE_DESCRIPTOR;

  USB_ENDPOINT_DESCRIPTOR = packed record
    bLength:byte;
    bDescriptorType:byte;
    bEndpointAddress:byte;
    bmAttributes:byte;
    wMaxPacketSize:word;
    bInterval:byte;
  end;
  PUSB_ENDPOINT_DESCRIPTOR=^USB_ENDPOINT_DESCRIPTOR;

  USB_PIPE_INFO = packed record
    EndpointDescriptor:USB_ENDPOINT_DESCRIPTOR;
    ScheduleOffset:cardinal;
  end;
  PUSB_PIPE_INFO=^USB_PIPE_INFO;

  USB_CONNECTION_STATUS = (NoDeviceConnected,
    DeviceConnected,DeviceFailedEnumeration,
    DeviceGeneralFailure, DeviceCausedOvercurrent,
    DeviceNotEnoughPower,DeviceNotEnoughBandwidth);
  PUSB_CONNECTION_STATUS=^USB_CONNECTION_STATUS;


  USB_NODE_CONNECTION_INFORMATION= packed record
    ConnectionIndex:cardinal;
    DeviceDescriptor:USB_DEVICE_DESCRIPTOR;
    CurrentConfigurationValue:byte;
    Speed:byte;
    DeviceIsHub:BOOLEAN;
    DeviceAddress:word;
    NumberOfOpenPipes:cardinal;
    ConnectionStatus:USB_CONNECTION_STATUS;
    PipeList:array [0..29] of USB_PIPE_INFO;
  end;
  PUSB_NODE_CONNECTION_INFORMATION=^USB_NODE_CONNECTION_INFORMATION;

  USB_DESCRIPTOR_REQUEST = packed record
    ConnectionIndex:cardinal;
    SetupPacket:packed record
                  bmRequest:byte;
                  bRequest:byte;
                  wValue:word;
                  wIndex:word;
                  wLength:word;
                end;
    //Data:array [0..0] of byte;
  end;
  PUSB_DESCRIPTOR_REQUEST=^USB_DESCRIPTOR_REQUEST;

  USB_COMMON_DESCRIPTOR  = packed record
    bLength:byte;
    bDescriptorType:byte;
  end;
  PUSB_COMMON_DESCRIPTOR = ^USB_COMMON_DESCRIPTOR;

  USB_CONFIGURATION_DESCRIPTOR = packed record
    bLength:byte;
    bDescriptorType:byte;
    wTotalLength:word;
    bNumInterfaces:byte;
    bConfigurationValue:byte;
    iConfiguration:byte;
    bmAttributes:byte;
    MaxPower:byte;
  end;
  PUSB_CONFIGURATION_DESCRIPTOR=^USB_CONFIGURATION_DESCRIPTOR;

  USB_STRING_DESCRIPTOR = packed record
    bLength : byte;
    bDescriptorType :byte;
    //bString : byte;
  end;
  PUSB_STRING_DESCRIPTOR = ^USB_STRING_DESCRIPTOR;

  URB_HEADER = packed record
    //
    // Fields filled in by client driver
    //
    Length:word;
    Func:word;
    Status:integer;
    //
    // Fields used only by USBD
    //
    UsbdDeviceHandle:pointer; // device handle assigned to this device
                              // by USBD
    UsbdFlags:cardinal;        // flags field reserved for USBD use.
  end;

  URB_PIPE_REQUEST = packed record
    Hdr:URB_HEADER;
    PipeHandle:THandle;
  end;

  USBUSER_REQUEST_HEADER =packed record
    UsbUserRequest:cardinal;
    UsbUserStatusCode:cardinal;
    RequestBufferLength:cardinal;
    ActualBufferLength:cardinal;
  end;

  USB_CONTROLLER_INFO_0=packed record
    PciVendorId:cardinal;
    PciDeviceId:cardinal;
    PciRevision:cardinal;
    NumberOfRootPorts:cardinal;
    ControllerFlavor:cardinal;
    HcFeatureFlags:cardinal;
  end;

  USBUSER_CONTROLLER_INFO_0 = packed record
    Header:USBUSER_REQUEST_HEADER;
    Info0:USB_CONTROLLER_INFO_0;
  end;

  WINUSB_SETUP_PACKET = packed record
    RequestType : byte;
    Request     : byte;
    Value       : word;
    Index       : word;
    Length      : word;
  end;

  TnrUsbRequestDirection = ( urdHostToDevice, urdDeviceToHost );
  TnrUsbRequestType = ( urtStandard, urtClass, urtVendor );
  TnrUsbRequestRecipient = (urrDevice, urrInterface, urrEndpoint, urrOther);

  TnrUsbRequest = packed record
    ReqType:TnrUsbRequestType;
    ReqDirection:TnrUsbRequestDirection;
    ReqRecipient:TnrUsbRequestRecipient;
    Request:byte;
    Value:word;
    Index:word;
    Length:word;
    Data:pointer;
  end;

  function GetKeyName(hHCD: THandle;IO_Code:cardinal): string;
  function GetConnectionKeyName(hHCD: THandle;IO_Code:cardinal;Index:integer): string;
  function GetDescription(Key: string): string;
//  function GetConfigDescriptor(hHub:THandle;ConnectionIndex:integer;DescriptorIndex:byte):PUSB_DESCRIPTOR_REQUEST;
  function GetDescriptor(hHub:THandle; ConnectionIndex:integer; DescriptorType, DescriptorIndex:byte):PUSB_COMMON_DESCRIPTOR;



implementation
uses nrwin32di;

function GetDescription(Key: string): string;
label exit_ok;
var devInst:cardinal;
    devInstNext:cardinal;
    Rslt:cardinal;
    walkDone:boolean;
    Len:cardinal;
    Buff:PChar;
begin
  Result:='';
  if not IniSetupDI then exit;

  walkDone:=True;
  // Get Root DevNode
  Rslt:=CmLocate(devInst,nil,0);
  if Rslt<>0 then exit;

  Buff:=Pointer(LocalAlloc(LPTR,512));
  while walkDone do begin
    Len := 512;
    Rslt:=CmRegProperty(devInst,
          CM_DRP_DRIVER, nil,Buff,Len,0);
    if (Rslt=0)and(Key=String(Buff))then begin
      Len:=512;
      Rslt:=CmRegProperty(devInst,
            CM_DRP_DEVICEDESC,nil,Buff,Len,0);
      if Rslt=0 then Result:=String(Buff);
      goto exit_ok;
    end;

    Rslt:=CmGetChild(devInstNext,devInst,0);
    if(Rslt=0)then begin
      devInst := devInstNext;
      continue;
    end;

    while True do begin
      Rslt:=CmGetSubling(devInstNext, devInst,0);
      if Rslt=0 then begin
        devInst:=devInstNext;
        break;
      end;
      Rslt:=CmGetParent(devInstNext, devInst,0);
      if(Rslt=0)
        then devInst:=devInstNext
        else begin
          walkDone:=False;
          break;
        end;
    end;
  end;
exit_ok:
  LocalFree(Cardinal(Buff));
end;

//=========================================================

function GetKeyName(hHCD: THandle;IO_Code:cardinal): string;
var rOK:boolean;
    nBytes:dword;
    pcKeyName:USB_HCD_DRIVERKEY_NAME;
    pcKeyNameW:PUSB_HCD_DRIVERKEY_NAME;
begin
  Result:='';
  rOK:=DeviceIoControl(hHCD,IO_Code,nil,0,
                      @pcKeyName,SizeOf(USB_HCD_DRIVERKEY_NAME),
                      nBytes,nil);
  if rOK then begin
     nBytes:=pcKeyName.ActualLength;
     pcKeyNameW:=Pointer(LocalAlloc(LPTR,pcKeyName.ActualLength));
     rOK:=DeviceIoControl(hHCD,IO_Code,
                      nil,0,pcKeyNameW,nBytes,
                      nBytes,nil);
     if rOK then Result:=String(PWChar(@pcKeyNameW.DriverKeyName));
     LocalFree(Cardinal(pcKeyNameW));
  end;
end;

function GetConnectionKeyName(hHCD: THandle;IO_Code:cardinal;Index:integer): string;
var rOK:boolean;
    nBytes:dword;
    pcKeyName:USB_NODE_CONNECTION_NAME;
    pcKeyNameW:PUSB_NODE_CONNECTION_NAME;
begin
  Result:='';
  pcKeyName.ConnectionIndex:=Index;
  rOK:=DeviceIoControl(hHCD,IO_Code,
       @pcKeyName,SizeOf(USB_NODE_CONNECTION_NAME),
       @pcKeyName,SizeOf(USB_NODE_CONNECTION_NAME),
       nBytes,nil);
  if rOK then begin
     nBytes:=pcKeyName.ActualLength;
     pcKeyNameW:=Pointer(LocalAlloc(LPTR,pcKeyName.ActualLength));
     pcKeyNameW^.ConnectionIndex:=Index;
     rOK:=DeviceIoControl(hHCD,IO_Code,
          pcKeyNameW,nBytes,pcKeyNameW,nBytes,
          nBytes,nil);
     if rOK then Result:=String(PWChar(@pcKeyNameW.NodeName));
     LocalFree(Cardinal(pcKeyNameW));
  end;
end;

function GetDescriptor(hHub:THandle; ConnectionIndex:integer; DescriptorType, DescriptorIndex:byte):PUSB_COMMON_DESCRIPTOR;
var rOK:boolean;
    sz:cardinal;
    nBytes:dword;
    reqDesc: PUSB_DESCRIPTOR_REQUEST;
    descCom: PUSB_COMMON_DESCRIPTOR;
begin
  Result:=nil;
  sz := SizeOf(USB_DESCRIPTOR_REQUEST) + SizeOf(USB_COMMON_DESCRIPTOR);
  reqDesc := Pointer(LocalAlloc(LPTR, sz));
  descCom := Pointer(Cardinal(reqDesc) + SizeOf(USB_DESCRIPTOR_REQUEST));
  reqDesc^.ConnectionIndex := ConnectionIndex;
  reqDesc^.SetupPacket.wValue := (DescriptorType shl 8) or DescriptorIndex;
  reqDesc^.SetupPacket.wLength:= SizeOf(USB_COMMON_DESCRIPTOR);
  rOK := DeviceIoControl(hHub,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
        reqDesc, sz, reqDesc, sz, nBytes, nil);
  if rOK and (nBytes >= Sz) then begin

    sz := SizeOf(USB_DESCRIPTOR_REQUEST) + descCom^.bLength;
    LocalFree(Dword(reqDesc));
    reqDesc := Pointer(LocalAlloc(LPTR,sz + 2));
    reqDesc^.ConnectionIndex := ConnectionIndex;
    reqDesc^.SetupPacket.wValue:= (DescriptorType shl 8) or DescriptorIndex;
    reqDesc^.SetupPacket.wLength:= Sz - sizeof(USB_DESCRIPTOR_REQUEST);
    descCom := Pointer(Cardinal(reqDesc) + SizeOf(USB_DESCRIPTOR_REQUEST));
    rOK := DeviceIoControl(hHub,IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                       reqDesc, sz, reqDesc, sz, nBytes,nil);
  end;

  if not rOK
    then LocalFree(DWORD(reqDesc))
    else Result := descCom;
end;

end.


