{******************************************************************************}
{ FileName............: FlexCopInterface                                       }
{ Project.............: FLEXCOP                                                }
{ Author(s)...........: MM                                                     }
{ Version.............: 1.01                                                   }
{------------------------------------------------------------------------------}
{  Interface unit for FlexCop II.                                              }
{                                                                              }
{  Copyright (C) 2003-2004  M.Majoor                                           }
{                                                                              }
{  This program is free software; you can redistribute it and/or               }
{  modify it under the terms of the GNU General Public License                 }
{  as published by the Free Software Foundation; either version 2              }
{  of the License, or (at your option) any later version.                      }
{                                                                              }
{  This program 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 General Public License for more details.                                }
{                                                                              }
{  You should have received a copy of the GNU General Public License           }
{  along with this program; if not, write to the Free Software                 }
{  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. }
{                                                                              }
{------------------------------------------------------------------------------}
{                                                                              }
{ Version   Date    Comment                                                    }
{  1.00   20050414  - Initial release                                          }
{                   - Supports multiple cards                                  }
{  2.02   20050723  - Added SUBSYS identification, so a device can be          }
{                     recognized                                               }
{                     <GetSubsysOfHandle> and <GetSubsysOfCard> added for this }
{******************************************************************************}

unit FlexCopInterface;

interface
uses
  FlexCopIoControl, SysUtils, Windows;

const
  MEDIA_GUID : TGUID = (D1:$4d36e96c; D2:$e325; D3:$11ce; D4:($bf,$c1,$08,$00,$2b,$e1,$03,$18));
  DMASIZE  = 4096;                     // DMA page size
  MAXCARDS = 10;                       // Max number of cards

function  FlexCopGetNumberOfCards           : Integer; stdcall;
function  FlexCopCreateFile                 (const Card: Integer): THandle; stdcall;
procedure FlexCopCloseHandle                (const Handle: THandle); stdcall;
function  FlexCopGetDriverVersion           (const Handle: THandle;
  MajorVersion: PWord; Minorversion: PWord; Build: PDword;
  DeviceName: PChar): Boolean; stdcall;
function  FlexCopGetDmaStatus               (Handle: THandle;
  DmaBuffer: Dword; var Status: TFlexCopGetDmaStatus): Boolean;
function  FlexCopWaitForNotification        (Handle: THandle): Boolean;
function  FlexCopGenerateManualNotification (Handle: THandle): Boolean;
function  FlexCopReadFromFlexCopRegister    (Handle: THandle; Address: Dword;
  var Data: Dword): Boolean;
function  FlexCopWriteToFlexCopRegister     (Handle: THandle; Address: Dword;
  Data: Dword): Boolean;
function  FlexCopWriteWithMaskToFlexCopRegister(Handle: THandle; Address: Dword;
  Mask: Dword; Data: Dword): Boolean;
function  FlexCopAllocateDma                (Handle: THandle; DmaSize: Dword;
  var Status: TFlexCopDmaBuffer): Boolean;
function  FlexCopReleaseDma                 (Handle: THandle;
  DmaBuffer: Dword): Boolean;
function  FlexCopReadFromDma                (Handle: THandle;
  Transfer: TFlexCopTransferBuffer): Boolean;
function  FlexCopWriteToDma                 (Handle: THandle;
  Transfer: TFlexCopTransferBuffer): Boolean;
function  FlexCopAllocateFifo               (Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
function  FlexCopReleaseFifo                (Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
function  FlexCopReadFromFifo               (Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
function  FlexCopWriteToIrqHandling         (Handle: THandle;
  Transfer: TFlexCopIRQTransferBuffer): Boolean;
function  FlexCopReadFromIrqHandling        (Handle: THandle;
  var Transfer: TFlexCopIRQTransferBuffer): Boolean;
function  FlexCopReadFromFlexCopRegisterWord(Handle: THandle; Address: Dword;
  var Data: Word): Boolean;
function  FlexCopWriteToFlexCopRegisterWord (Handle: THandle; Address: Dword;
  Data: Word): Boolean;
function  FlexCopGetCardOfHandle                (const Handle: THandle): Integer; stdcall;
function  FlexCopGetHandleOfCard                (const Card: Integer): THandle; stdcall;
function  FlexCopGetSubsysOfHandle              (const Handle: THandle): Integer; stdcall;
function  FlexCopGetSubsysOfCard                (const Card  : Integer): Integer; stdcall;

{------------------------------------------------------------------------------
                             From SETUPAPI.PAS
 ------------------------------------------------------------------------------}
const
  ANYSIZE_ARRAY = 1;
  SetupAPIdll = 'SetupAPI.dll';
  SPDRP_HARDWAREID      = $00000001;
  DIGCF_PRESENT         = $00000002;
  DIGCF_DEVICEINTERFACE = $00000010;
  SPDRP_FRIENDLYNAME    = $0000000C;
  SPDRP_DEVICEDESC      = $00000000;
type
  ULONG_PTR = DWORD;
  HDEVINFO = Pointer;
  PSPDeviceInterfaceData = ^TSPDeviceInterfaceData;
  _SP_DEVICE_INTERFACE_DATA = packed record
    cbSize: DWORD;
    InterfaceClassGuid: TGUID;
    Flags: DWORD;
    Reserved: ULONG_PTR;
  end;
  TSPDeviceInterfaceData = _SP_DEVICE_INTERFACE_DATA;
  PSPDeviceInterfaceDetailDataA = ^TSPDeviceInterfaceDetailDataA;
  PSPDeviceInterfaceDetailData = PSPDeviceInterfaceDetailDataA;
  _SP_DEVICE_INTERFACE_DETAIL_DATA_A = packed record
    cbSize: DWORD;
    DevicePath: array[0..ANYSIZE_ARRAY - 1] of AnsiChar;
  end;
  _SP_DEVICE_INTERFACE_DETAIL_DATA_W = packed record
    cbSize: DWORD;
    DevicePath: array[0..ANYSIZE_ARRAY - 1] of WideChar;
  end;
  _SP_DEVICE_INTERFACE_DETAIL_DATA_ = _SP_DEVICE_INTERFACE_DETAIL_DATA_A;
  TSPDeviceInterfaceDetailDataA = _SP_DEVICE_INTERFACE_DETAIL_DATA_A;
  TSPDeviceInterfaceDetailData = TSPDeviceInterfaceDetailDataA;
  PSPDevInfoData = ^TSPDevInfoData;
  _SP_DEVINFO_DATA = packed record
    cbSize: DWORD;
    ClassGuid: TGUID;
    DevInst: DWORD;
    Reserved: ULONG_PTR;
  end;
  TSPDevInfoData = _SP_DEVINFO_DATA;
function SetupDiGetClassDevs(ClassGuid: PGUID; const Enumerator: PChar;
  hwndParent: HWND; Flags: DWORD): HDEVINFO; stdcall;
function SetupDiEnumDeviceInterfaces(DeviceInfoSet: HDEVINFO;
  DeviceInfoData: PSPDevInfoData; var InterfaceClassGuid: TGUID;
  MemberIndex: DWORD; var DeviceInterfaceData: TSPDeviceInterfaceData):
    LongBool; stdcall;
function SetupDiGetDeviceInterfaceDetail(DeviceInfoSet: HDEVINFO;
  DeviceInterfaceData: PSPDeviceInterfaceData;
  DeviceInterfaceDetailData: PSPDeviceInterfaceDetailDataA;
  DeviceInterfaceDetailDataSize: DWORD; RequiredSize: PDWORD;
  Device: PSPDevInfoData): LongBool; stdcall;
function SetupDiGetDeviceRegistryProperty(DeviceInfoSet: HDEVINFO;
  var DeviceInfoData: TSPDevInfoData; Property_: DWORD;
  PropertyRegDataType: PDWORD; PropertyBuffer: PBYTE; PropertyBufferSize: DWORD;
  RequiredSize: PDWORD): LongBool; stdcall;
function SetupDiDestroyDeviceInfoList(DeviceInfoSet: HDEVINFO): LongBool;
  stdcall;
function SetupDiGetClassDevs; external SetupAPIdll name 'SetupDiGetClassDevsA';
function SetupDiEnumDeviceInterfaces; external SetupAPIdll name
  'SetupDiEnumDeviceInterfaces';
function SetupDiGetDeviceInterfaceDetail; external SetupAPIdll name
  'SetupDiGetDeviceInterfaceDetailA';
function SetupDiGetDeviceRegistryProperty; external SetupAPIdll name
  'SetupDiGetDeviceRegistryPropertyA';
function SetupDiDestroyDeviceInfoList; external SetupAPIdll name
  'SetupDiDestroyDeviceInfoList';
{------------------------------------------------------------------------------
                            End of SETUPAPI.PAS
 ------------------------------------------------------------------------------}

implementation

type
  TCardHandles = record
    Card  : Integer;
    Handle: THandle;
    Subsys: Integer;
  end;

var
  Loop: Integer; // Loop variable
  FlexCopNumberOfCardsDetected: Integer;
  FlexCopNameForFileHandle    : array[0..MAXCARDS - 1] of string;
  FlexCopSubsysOfCard         : array[0..MAXCARDS - 1] of string;
  FlexCopDeviceFullNames      : array[0..MAXCARDS - 1] of string;
  FlexCopHandles              : array[0..255] of TCardHandles;

{------------------------------------------------------------------------------
  Params  : -
  Returns : <Result>        Number of cards detected
                            0 if no cards or no driver

  Descript: Get number of detected cards
  Notes   :
 ------------------------------------------------------------------------------}
function FlexCopGetNumberOfCards: Integer; stdcall;
begin
  Result := FlexCopNumberOfCardsDetected;
end;

{------------------------------------------------------------------------------
  Params  : <Handle>        CreateFile handle
  Returns : <Result>        True if valid results
            <MajorVersion>  Major version ID
            <MinorVersion>  Minor version ID
            <Build>         Build (date) in $YYYYMMDD notation
            <DeviceName>    Name of device (target buffer must have been allocated)

  Descript: Retrieve version information from driver.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetDriverVersion(const Handle: THandle;
  MajorVersion: PWord; Minorversion: PWord; Build: PDword;
  DeviceName: PChar): Boolean; stdcall;
var
  ABuffer   : TFlexCopGetDriverVersion;
  BytesRead : Dword;
  Loop      : Integer;
begin
  try
    Result := False;
    if not Assigned(MajorVersion) then
      Exit;
    if not Assigned(MinorVersion) then
      Exit;
    if not Assigned(Build) then
      Exit;
    if not Assigned(DeviceName) then
      Exit;
    if Handle = INVALID_HANDLE_VALUE then
      Exit;
    Result := DeviceIoControl(Handle,
      CIoctlFlexCopGetDriverVersion,
      nil,
      0,
      @ABuffer,
      Sizeof(ABuffer),
      BytesRead,
      nil);
    if Result then
    begin
      MajorVersion^ := ABuffer.MajorVersion;
      MinorVersion^ := ABuffer.MinorVersion;
      Build^        := ABuffer.Build;
    end
    else
    begin
      MajorVersion^ := 0;
      MinorVersion^ := 0;
      Build^        := 0;
    end;
    // Try to find the correct name
    StrPCopy(DeviceName, FlexCopDeviceFullNames[0]);
    for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    begin
      if FlexCopHandles[Loop].Handle = Handle then
        StrPCopy(DeviceName, FlexCopDeviceFullNames[Loop]);
    end;
  except
    Result := False;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <DmaBuffer>  DMA buffer to get status for
  Returns : <Result>     True if valid results
            <Status>     Returned status

  Descript: Retrieve DMA status information from driver.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetDmaStatus(Handle: THandle;
  DmaBuffer: Dword; var Status: TFlexCopGetDmaStatus): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopGetDmaStatus,
    @DmaBuffer,
    Sizeof(DmaBuffer),
    @Status,
    Sizeof(Status),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <DmaSize>    Size of the DMA buffer to allocate
  Returns : <Result>     True if valid results
            <Status>     Returned status

  Descript: Allocate DMA buffer.
  Notes   : Needs to release it through <FlexCopReleaseDma> using the returned
            <Identifier>
 ------------------------------------------------------------------------------}

function FlexCopAllocateDma(Handle: THandle;
  DmaSize: Dword; var Status: TFlexCopDmaBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopAllocateDma,
    @DmaSize,
    Sizeof(DmaSize),
    @Status,
    Sizeof(Status),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <DmaBuffer>  Buffer to release (0..31)
  Returns : <Result>     True if valid results

  Descript: Release DMA buffer.
  Notes   : The <DmaBuffer> is the returned <Identifier> of the status returned
            by <FlexCopAllocateDma>.
 ------------------------------------------------------------------------------}

function FlexCopReleaseDma(Handle: THandle; DmaBuffer: Dword): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReleaseDma,
    @DmaBuffer,
    Sizeof(DmaBuffer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Buffer/size and so on
  Returns : <Result>     True if valid results

  Descript: Read from DMA buffer.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReadFromDma(Handle: THandle;
  Transfer: TFlexCopTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReadFromDma,
    @Transfer,
    Sizeof(Transfer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Buffer/size and so on
  Returns : <Result>     True if valid results

  Descript: Write to DMA buffer.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopWriteToDma(Handle: THandle;
  Transfer: TFlexCopTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopWriteToDma,
    @Transfer,
    Sizeof(Transfer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Buffer/size and so on
  Returns : <Result>     True if valid results

  Descript: Allocate Fifo buffer(s).
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopAllocateFifo(Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopAllocateFifo,
    @Transfer,
    Sizeof(Transfer),
    @Transfer,
    Sizeof(Transfer),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Buffer/size and so on
  Returns : <Result>     True if valid results

  Descript: Release Fifo buffer(s).
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReleaseFifo(Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReleaseFifo,
    @Transfer,
    Sizeof(Transfer),
    @Transfer,
    Sizeof(Transfer),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Buffer/size and so on
  Returns : <Result>     True if valid results

  Descript: Read Fifo buffer.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReadFromFifo(Handle: THandle;
  var Transfer: TFlexCopFifoTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReadFromFifo,
    @Transfer,
    Sizeof(Transfer),
    @Transfer,
    Sizeof(Transfer),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Data
  Returns : <Result>     True if valid results

  Descript: Write IRQ handling.
  Notes   : You still need to enable the interrupt by means of the Interrupt
            enable register!!!
 ------------------------------------------------------------------------------}

function FlexCopWriteToIrqHandling(Handle: THandle;
  Transfer: TFlexCopIrqTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopWriteToIrqHandling,
    @Transfer,
    Sizeof(Transfer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
            <Transfer>   Data
  Returns : <Result>     True if valid results

  Descript: Read IRQ handling.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReadFromIrqHandling(Handle: THandle;
  var Transfer: TFlexCopIrqTransferBuffer): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReadFromIrqHandling,
    @Transfer,
    Sizeof(Transfer),
    @Transfer,
    Sizeof(Transfer),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>  CreateFile handle
  Returns : <Result>  True if success

  Descript: Wait for notification to occur.
  Notes   : The driver only support ONE notification at a time!!!
 ------------------------------------------------------------------------------}

function FlexCopWaitForNotification(Handle: THandle): Boolean;
var
  BytesRead : Dword;
  hEvent    : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopWaitForNotification,
    nil,
    0,
    @hEvent,
    Sizeof(hEvent),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>  CreateFile handle
  Returns : <Result>  True if success

  Descript: Manually activate the notification.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGenerateManualNotification(Handle: THandle): Boolean;
var
  BytesRead : Dword;
  Aux       : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopGenerateManualNotification,
    @Aux,                              // Required, but currently not used
    Sizeof(Aux),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>   CreateFile handle
            <Address>  Register of FlexCop to read (4 byte multiple)
  Returns : <Result>   True if valid results
            <Data>     Returned data

  Descript: Read data from FlexCop.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReadFromFlexCopRegister(Handle: THandle;
  Address: Dword; var Data: Dword): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReadFromFlexCopRegister,
    @Address,
    Sizeof(Address),
    @Data,
    Sizeof(Data),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>   CreateFile handle
            <Address>  Register of FlexCop to write to (4 byte multiple)
            <Data>     Data to write
  Returns : <Result>   True if success

  Descript: Write data to FlexCop.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopWriteToFlexCopRegister(Handle: THandle;
  Address: Dword; Data: Dword): Boolean;
var
  BytesRead : Dword;
  ABuffer   : TFlexCopWriteRegister;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  ABuffer.Address := Address;
  ABuffer.Data    := Data;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopWriteToFlexCopRegister,
    @ABuffer,
    Sizeof(ABuffer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>   CreateFile handle
            <Address>  Register of FlexCop to read (2 byte multiple)
  Returns : <Result>   True if valid results
            <Data>     Returned data

  Descript: Read data (word) from FlexCop.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopReadFromFlexCopRegisterWord(Handle: THandle;
  Address: Dword; var Data: Word): Boolean;
var
  BytesRead : Dword;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopReadFromFlexCopRegister2,
    @Address,
    Sizeof(Address),
    @Data,
    Sizeof(Data),
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>   CreateFile handle
            <Address>  Register of FlexCop to write to (2 byte multiple)
            <Data>     Data to write
  Returns : <Result>   True if success

  Descript: Write data to FlexCop.
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopWriteToFlexCopRegisterWord(Handle: THandle;
  Address: Dword; Data: Word): Boolean;
var
  BytesRead : Dword;
  ABuffer   : TFlexCopWriteRegisterWord;
begin
  Result := False;
  if Handle = INVALID_HANDLE_VALUE then
    Exit;
  ABuffer.Address := Address;
  ABuffer.Data    := Data;
  Result := DeviceIoControl(Handle,
    CIoctlFlexCopWriteToFlexCopRegister2,
    @ABuffer,
    Sizeof(ABuffer),
    nil,
    0,
    BytesRead,
    nil);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>   CreateFile handle
            <Address>  Register of SAA7146 to write to (4 byte multiple)
            <Mask>     Mask data. Only the bits set to '1' will be adjusted
                       according to the <Data>.
            <Data>     Data to write
  Returns : <Result>   True if success

  Descript: Write data to FlexCop, only changing the <Mask> bits.
  Notes   : This function requires both a READ and WRITE.
 ------------------------------------------------------------------------------}

function FlexCopWriteWithMaskToFlexCopRegister(Handle: THandle;
  Address: Dword; Mask: Dword; Data: Dword): Boolean;
var
  DataIn  : Dword;
  DataOut : Dword;
  DataOr  : Dword;
  DataAnd : Dword;
begin
  Result := False;
  if not FlexCopReadFromFlexCopRegister(Handle, Address, DataIn) then
    Exit;
  DataAnd := not Mask;                                     // Bits to reset
  DataOr  := Data and Mask;                                // Bits to set
  DataOut := (DataIn and DataAnd) or DataOr;
  Result := FlexCopWriteToFlexCopRegister(Handle, Address, DataOut);
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
  Returns : <Result>     Card number (<0 is none)

  Descript: Get card number which has the handle
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetCardOfHandle(const Handle: THandle): Integer; stdcall;
var
  Loop: Integer;
begin
  try
    Result := -1;
    for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    begin
      if FlexCopHandles[Loop].Handle = Handle then
      begin
        Result := FlexCopHandles[Loop].Card;
        Exit;
      end;
    end;
  except
    Result := -1;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Card>       Card number
  Returns : <Result>     CreateFile handle

  Descript: Get handle of card (first handle is returned)
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetHandleOfcard(const Card: Integer): THandle; stdcall;
var
  Loop: Integer;
begin
  try
    Result := INVALID_HANDLE_VALUE;
    for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    begin
      if FlexCopHandles[Loop].Card = Card then
      begin
        Result := FlexCopHandles[Loop].Handle;
        Exit;
      end;
    end;
  except
    Result := INVALID_HANDLE_VALUE;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Handle>     CreateFile handle
  Returns : <Result>     SUBSYS identifier

  Descript: Get SUBSYS identifier of the handle
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetSubsysOfHandle(const Handle: THandle): Integer; stdcall;
var
  Loop: Integer;
begin
  try
    Result := -1;
    for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    begin
      if FlexCopHandles[Loop].Handle = Handle then
      begin
        Result := FlexCopHandles[Loop].Subsys;
        Exit;
      end;
    end;
  except
    Result := -1;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Card>       Card number
  Returns : <Result>     Subsys identifier (0 if error)

  Descript: Get subsys of card
  Notes   :
 ------------------------------------------------------------------------------}

function FlexCopGetSubsysOfCard(const Card: Integer): Integer; stdcall;
var
  Error: Integer;
begin
  Result := 0;
  try
    if Card < Low(FlexCopSubsysOfCard) then
      Exit;
    if Card > High(FlexCopSubsysOfCard) then
      Exit;
    Val('$' + FlexCopSubsysOfCard[Card], Result, Error);
    if Error <> 0 then
      Result := 0;
  except
    Result := 0;
  end;
end;

{------------------------------------------------------------------------------
  Params  : -
  Returns : -

  Descript: Handle WDM support. WDM uses the class GUID and enumerating it. We
            get one driver at the time and have to check which card it is
            (the symbolic name consists of the class GUID so we can not use
            this to get which driver it is).
            We only get the symbolic links here because CreateFile and WDM
            access don't work well if mixed in the wrong sequence
            (i.e. CreateFile first and then doing WDM will fail -> First WDM
            and then CreateFile works).
 ------------------------------------------------------------------------------}

procedure WDMEnumeration;
var
  Info        : HDEVINFO;
  IfData      : TSPDeviceInterfaceData;
  AGuid       : TGUID;
  Detailed    : PSPDeviceInterfaceDetailData;
  DevInfo     : TSPDevInfoData;
  RLength     : Dword;
  DoAgain     : Boolean;                // Repeat enumerating until no more devices
  Member      : Dword;
  RegType     : Dword;
  ABuf        : array [0..255] of Char;
  DeviceString: string;
begin
  DoAgain := True;
  Member  := 0;                      // First device
  AGuid   := MEDIA_GUID;
  // Get handle to relevant device information set
  Info := SetupDiGetClassDevs(@AGuid, nil, 0, DIGCF_PRESENT or
    DIGCF_DEVICEINTERFACE);
  if (Info <> Pointer(INVALID_HANDLE_VALUE)) then
  begin
    try
      while DoAgain do
      begin
        DoAgain := False;
        // Get interface data for the requested instance
        IfData.cbSize := sizeof(IfData);
        if SetupDiEnumDeviceInterfaces(Info, nil, AGuid, Member, IfData) = TRUE
          then
        begin
          DoAgain := true;                                                // Keep trying next device
          inc(Member);
          RLength := 0;
          // First get size of required buffer for detailed information
          SetupDiGetDeviceInterfaceDetail( Info, @IfData,  nil, 0, @RLength, nil );
          if (RLength <> 0) and (RLength < 2048) then                     // 2048 just for security
          begin
            // We know the size, now create buffer
            GetMem(Detailed, RLength);
            try
              // Now get detailed information in buffer: i.e. symbolic link name
              Detailed.cbSize := sizeof(TSPDeviceInterfaceDetailData);
              DevInfo.cbSize := sizeof(TSPDevInfoData);
              if SetupDiGetDeviceInterfaceDetail(Info, @IfData, Detailed,
                RLength, @RLength, @DevInfo) = TRUE then
              begin
                // We now have all the information we need. The symbolic link will be something like:
                // W2000 : "\\?\root#unknown#000x#{4c....class GUID....}"  were 'x' is variable (enumerated)
                // W98/ME: "\\.\000000000000000x#{4c....class GUID....}"   were 'x' is variable (enumerated)
                // We do not know which driver we are looking at, so we have to investigate further.
                // Earlier we investigated the HardwareId but this does not always seem to work (at least not under ME).
                // We now use the friendly name of the driver. If that fails we try the description.
                if SetupDiGetDeviceRegistryProperty(Info, DevInfo,
                  SPDRP_FRIENDLYNAME, @RegType, @ABuf, sizeof(ABuf), @RLength) =
                  TRUE then
                begin
                  DeviceString := UpperCase(StrPas(ABuf));
                  if Pos('FLEXCOP', DeviceString)<>0 then
                  begin
                    FlexCopNameForFileHandle[FlexCopNumberOfCardsDetected] :=
                      StrPas(Detailed.DevicePath);
                    FlexCopDeviceFullNames[FlexCopNumberOfCardsDetected] :=
                      DeviceString;
                    FlexCopSubsysOfCard[FlexCopNumberOfCardsDetected] := '';
                    if SetupDiGetDeviceRegistryProperty(Info, DevInfo,
                      SPDRP_HARDWAREID, @RegType, @ABuf, sizeof(ABuf), @RLength) = True then
                    begin
                      DeviceString := UpperCase(StrPas(ABuf));
                      if Pos('SUBSYS', DeviceString) <> 0 then
                      begin
                        Delete(DeviceString, 1, Pos('SUBSYS', DeviceString) + 6);
                        Delete(DeviceString, 9, 255);
                        FlexCopSubsysOfCard[FlexCopNumberOfCardsDetected] := DeviceString;
                      end;
                    end;
                    Inc(FlexCopNumberOfCardsDetected);
                    if FlexCopNumberOfCardsDetected >
                      High(FlexCopNameForFileHandle) then
                      Exit;
                  end;
                end
                else if SetupDiGetDeviceRegistryProperty(Info, DevInfo,
                  SPDRP_DEVICEDESC, @RegType, @ABuf, sizeof(ABuf), @RLength) = TRUE
                  then
                  begin
                    DeviceString := UpperCase(StrPas(ABuf));
                    if Pos('FLEXCOP', DeviceString)<>0 then
                    begin
                      FlexCopNameForFileHandle[FlexCopNumberOfCardsDetected] :=
                        StrPas(Detailed.DevicePath);
                      FlexCopDeviceFullNames[FlexCopNumberOfCardsDetected] :=
                        DeviceString;
                      FlexCopSubsysOfCard[FlexCopNumberOfCardsDetected] := '';
                      if SetupDiGetDeviceRegistryProperty(Info, DevInfo,
                        SPDRP_HARDWAREID, @RegType, @ABuf, sizeof(ABuf), @RLength) = True then
                      begin
                        DeviceString := UpperCase(StrPas(ABuf));
                        if Pos('SUBSYS', DeviceString) <> 0 then
                        begin
                          Delete(DeviceString, 1, Pos('SUBSYS', DeviceString) + 6);
                          Delete(DeviceString, 9, 255);
                          FlexCopSubsysOfCard[FlexCopNumberOfCardsDetected] := DeviceString;
                        end;
                      end;
                      Inc(FlexCopNumberOfCardsDetected);
                      if FlexCopNumberOfCardsDetected >
                        High(FlexCopNameForFileHandle) then
                        Exit;
                    end;
                  end;
              end;
            finally
              FreeMem(Detailed);
            end;
          end;
        end;
      end;
    finally
      SetupDiDestroyDeviceInfoList( Info );
    end;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Card>  Card to access
                    <0 will use the next available card which is not in use yet
  Returns : <Result> Opened handle.

  Descript: Open handle to driver.
 ------------------------------------------------------------------------------}

function FlexCopCreateFile(const Card: Integer): THandle; stdcall;
var
  Loop: Integer;
  UseCard: Integer;
  InUse: array[0..MAXCARDS - 1] of Boolean;
  Empty: Integer;
begin
  try
    Result := INVALID_HANDLE_VALUE;
    UseCard := Card;
    // If find one ourselves
    if UseCard < Low(FlexCopNameForFileHandle) then
    begin
      // Fill in array if cards in use
      for Loop := Low(InUse) to High(InUse) do
        InUse[Loop] := False;
      for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
        if FlexCopHandles[Loop].Handle <> INVALID_HANDLE_VALUE then
        begin
          if FlexCopHandles[Loop].Card in [Low(InUse)..High(InUse)] then
            InUse[FlexCopHandles[Loop].Card] := True;
        end;
      UseCard := Low(FlexCopNameForFileHandle);
      while (UseCard < FlexCopNumberOfCardsDetected) and
        InUse[Usecard] do
        Inc(Usecard);
    end;
    if (UseCard >= 0) and
      (UseCard < FlexCopNumberOfCardsDetected) then
    begin
      // Check if we have room to store handle
      Empty := Low(FlexCopHandles);
      while (FlexCophandles[Empty].Handle <> INVALID_HANDLE_VALUE) and (Empty <
        High(FlexCopHandles)) do
        Inc(Empty);
      if Empty = High(FlexCopHandles) then
        Exit;
      FlexCopHandles[Empty].Card := UseCard;
      FlexCopHandles[Empty].Handle :=
        CreateFile(Pchar(FlexCopNameForFileHandle[UseCard]),
          // Open device handle to driver
        GENERIC_WRITE or GENERIC_READ, // Both read and write
        0,
        nil,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);
      Result := FlexCopHandles[Empty].Handle;
    end;
  except
    Result := INVALID_HANDLE_VALUE;
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Handle>  Handle to close
  Returns : -

  Descript: Close handle to driver.
 ------------------------------------------------------------------------------}

procedure FlexCopCloseHandle(const Handle: THandle); stdcall;
var
  Loop: Integer;
begin
  try
    if Handle <> INVALID_HANDLE_VALUE then
    begin
      try
        CloseHandle(Handle);
      except
        // In case we close a handle which was already closed by some means ..
      end;
      // Make sure the local handle is reset too
      // The procedure might have been called externally and not from <FlexCopCloseAllHandles>
      for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
        if FlexCopHandles[Loop].Handle = Handle then
          FlexCopHandles[Loop].Handle := INVALID_HANDLE_VALUE;
    end;
  except
  end;
end;

{------------------------------------------------------------------------------
  Params  : <Handle>  Handle to close
  Returns : -

  Descript: Close handle to driver.
 ------------------------------------------------------------------------------}

procedure FlexCopCloseAllHandles;
var
  Loop: Integer;
begin
  for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    FlexCopCloseHandle(FlexCopHandles[Loop].Handle);
end;

initialization
  FlexCopNumberOfCardsDetected := 0;                       // No cards detected by default
  for Loop := Low(FlexCopNameForFileHandle) to High(FlexCopNameForFileHandle) do
    FlexCopNameForFileHandle[Loop] := CFlexCopDriverName;  // These are the names for NT 4.0 driver

  for Loop := Low(FlexCopHandles) to High(FlexCopHandles) do
    FlexCopHandles[Loop].Handle := INVALID_HANDLE_VALUE;   // Set no handles

  WDMEnumeration;                                          // Try getting names for WDM drivers (overrule NT 4.0 names)


finalization
begin
  FlexCopCloseAllHandles;
end;

end.
