unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TCOSData = array of packed record
    A, B, C: Byte;
  end;

  TMainForm = class(TForm)
    LogMemo: TMemo;
    Panel2: TPanel;
    CardCombo: TComboBox;
    ControlPanel: TPanel;
    WDGGroup: TGroupBox;
    WDGIRQButton: TButton;
    SoftRestartRadio: TRadioButton;
    MostlySoftRestartRadio: TRadioButton;
    DisableWDGCheck: TCheckBox;
    NoRestartRadio: TRadioButton;
    GoWDGButton: TButton;
    CancelWDGButton: TButton;
    TempButton: TButton;
    StatusButton: TButton;
    RebootButton: TButton;
    COSGroup: TGroupBox;
    COSDataLabel: TLabel;
    COSIRQButton: TButton;
    GenGroup: TGroupBox;
    GenIRQLabel: TLabel;
    GenIRQButton: TButton;
    AbortButton: TButton;
    StatusLabel: TLabel;
    TempLabel: TLabel;
    WDGTime: TTimer;
    RegisterGroup: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    OffsetEdit: TEdit;
    DataEdit: TEdit;
    Read32Button: TButton;
    Write32Button: TButton;
    Read16Button: TButton;
    Write16Button: TButton;
    Read8Button: TButton;
    Write8Button: TButton;
    Label3: TLabel;
    BARCombo: TComboBox;
    procedure GenIRQButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure AbortButtonClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure COSIRQButtonClick(Sender: TObject);
    procedure WDGIRQButtonClick(Sender: TObject);
    procedure GoWDGButtonClick(Sender: TObject);
    procedure CancelWDGButtonClick(Sender: TObject);
    procedure TempButtonClick(Sender: TObject);
    procedure StatusButtonClick(Sender: TObject);
    procedure RebootButtonClick(Sender: TObject);
    procedure CardComboChange(Sender: TObject);
    procedure WDGPetTimer(Sender: TObject);
    procedure WDGWaitTimer(Sender: TObject);
    procedure OffsetEditChange(Sender: TObject);
    procedure Read8ButtonClick(Sender: TObject);
    procedure Read16ButtonClick(Sender: TObject);
    procedure Read32ButtonClick(Sender: TObject);
    procedure Write8ButtonClick(Sender: TObject);
    procedure Write16ButtonClick(Sender: TObject);
    procedure Write32ButtonClick(Sender: TObject);
  protected
    function GetSelBAR: Integer;
  public
    procedure AddLine(CardNum: Integer; Line: String);
    procedure ReceiveGenIRQ(CardNum: Integer);
    procedure ReceiveCOSData(CardNum: Integer; COSData: TCOSData);
  end;

var
  MainForm: TMainForm;

implementation

{$R *.DFM}

uses
  IRQThreadUnit, AIOWDM;

type
  TCardData = record
    Base: LongWord;
    PPIs: LongWord;
    IsWDG: Boolean;
    IRQCount: LongWord;
    LastCOSData: TCOSData;
    Log: String;
    WDGPets: LongWord;
  end;

var
  CardData: array of TCardData;

procedure TMainForm.FormCreate(Sender: TObject);
var
  NumCards, CardNum, I, J: Integer;
  DeviceID, Base: LongWord;
  NameSize: LongWord;
  Name: AnsiString;
  Success: LongWord;

begin
  CardCombo.Items.Clear;
  NumCards := GetNumCards;
  if NumCards = 0 then begin
    GenGroup.Visible := False;
    COSGroup.Visible := False;
    WDGGroup.Visible := False;

    for I := 0 to ControlPanel.ControlCount - 1 do
      for J := 0 to TWinControl(ControlPanel.Controls[I]).ControlCount - 1 do
      TWinControl(ControlPanel.Controls[I]).Controls[J].Enabled := False
    ;
    LogMemo.Text := 'No cards found. The cards may not be installed, or they may be installed using a driver other than AIOWDM.sys.';
  end
  else begin
    SetLength(CardData, NumCards);
    for CardNum := 0 to NumCards - 1 do begin
      NameSize := 256;
      SetString(Name, nil, NameSize);
      Success := QueryCardInfo(CardNum, @DeviceID, @Base, @NameSize, PAnsiChar(Name));
      SetLength(Name, NameSize - 1);
      if Success = 0 then
        CardCombo.Items.Add('Error getting data: ' + Name + '[' + IntToHex(Base, 3) + ']')
      else begin
        CardCombo.Items.Add(Name + '[' + IntToHex(Base, 3) + ']');

        CardData[CardNum].Base := Base;
        case DeviceID of
          $0E50, $0E53, $0E54, $0E56, $0E57, $2E50, $F051: begin //PCI-DIO-24S, PCIe-DIO-24S, PCIe-DIO-24DS, PCIe-DIO-24DCS, mPCIe-DIO-24S, mPCIe-DIO-24A, 104-DIO-24S
            CardData[CardNum].PPIs := 1;
            CardData[CardNum].Log := 'NOTE: This sample does not enable IRQs.  Please unmask CoS IRQs (by writing to +B).  Refer to the manual for details.';
          end;
          $0E60, $0E61, $F044, $7007: begin //PCI-DIO-48S, PCIe-DIO-48S, 104-DIO-48S, IOD-48S
            CardData[CardNum].PPIs := 2;
            CardData[CardNum].Log := 'NOTE: This sample does not enable IRQs.  Please unmask CoS IRQs (by writing to +B).  Refer to the manual for details.';
          end;
          $0E6A: begin //PCIe-DIO-72S
            CardData[CardNum].PPIs := 3;
            CardData[CardNum].Log := 'NOTE: This sample does not enable IRQs.  Please enable as described in the manual.';
          end;
          $0E71: begin //PCIe-DIO-96S
            CardData[CardNum].PPIs := 4;
            CardData[CardNum].Log := 'NOTE: This sample does not enable IRQs.  Please enable as described in the manual.';
          end;
          $0E79: begin //PCIe-DIO-120S
            CardData[CardNum].PPIs := 5;
            CardData[CardNum].Log := '';
            CardData[CardNum].Log := 'NOTE: This sample does not enable IRQs.  Please enable as described in the manual.';
          end;
          $22C0, $25C0, $2FC0, $2FC1: begin //PCI-WDG-CSM, P104-WDG-E, P104-WDG-CSM, and P104-WDG-CSMA
            CardData[CardNum].IsWDG := True;
            WDGInit(CardNum);
            CardData[CardNum].Log := '';
          end;
        end;
        SetLength(CardData[CardNum].LastCOSData, CardData[CardNum].PPIs);

        if CardData[CardNum].PPIs <> 0 then
          CardData[CardNum].Log :=
              'AIOWDM provides generic IRQ-handling functions for all cards '
            + 'and special IRQ-handling functions for COS cards like this one.'#13#10#13#10
            + 'Click "Detect Generic IRQs" to handle IRQs generically for the card selected above, '
            + 'or "Detect COS IRQs" to handle IRQs for the card selected above by updating COS data.'#13#10#13#10
        else if CardData[CardNum].IsWDG then
          CardData[CardNum].Log :=
              'AIOWDM provides generic IRQ-handling functions for all cards '
            + 'and special functions for watchdog cards like this one.'#13#10#13#10
            + 'Click "Detect Generic IRQs" to handle IRQs generically for the card selected above, '
            + 'or "Handle Watchdog IRQ" to handle the next IRQ for the card selected above with the selected action.'#13#10#13#10
            + 'You can also click "Test Watchdog Timing" to set the watchdog timer, pet it a few times, then let it time out, '
            + '"Read Temp" to read the temperature(if installed), '
            + '"Read Status" to read the card''s status byte, '
            + 'or "Reboot" to quickly reboot your computer.'#13#10#13#10
        else
          CardData[CardNum].Log :=
              'AIOWDM provides generic IRQ-handling functions for all cards.'#13#10#13#10
            + 'Click "Detect Generic IRQs" to handle IRQs for the card selected above.'#13#10#13#10
            + CardData[CardNum].Log
        ;
      end;
    end;
    CardCombo.ItemIndex := 0;
    CardComboChange(CardCombo);
    BARCombo.ItemIndex := 0;
  end;
end;

procedure TMainForm.CardComboChange(Sender: TObject);
var
  CardNum: Integer;
  Buf: String;
  I: Integer;

begin
  CardNum := CardCombo.ItemIndex;

  LogMemo.Text := CardData[CardNum].Log;
  LogMemo.SelStart := Length(CardData[CardNum].Log);
  LogMemo.SelLength := 1;

  //Generic IRQs
  if CardData[CardNum].IRQCount = 0 then
    GenIRQLabel.Caption := ''
  else
    GenIRQLabel.Caption := IntToStr(CardData[CardNum].IRQCount) + ' IRQ(s) detected'
  ;

  //COS IRQs
  if CardData[CardNum].PPIs <> 0 then begin
    Buf := '';
    for I := 0 to High(CardData[CardNum].LastCOSData) do
      Buf := Buf + '  ' + IntToHex(CardData[CardNum].LastCOSData[I].A, 2) + ' ' + IntToHex(CardData[CardNum].LastCOSData[I].B, 2) + ' ' + IntToHex(CardData[CardNum].LastCOSData[I].C, 2)
    ;
    COSDataLabel.Caption := Buf;

    COSGroup.Visible := True;
  end
  else
    COSGroup.Visible := False //Hide COS IRQ stuff if it's not a COS card
  ;

  //Hide watchdog stuff if it's not a watchdog card
  WDGGroup.Visible := CardData[CardNum].IsWDG;
  TempLabel.Caption := '';
  StatusLabel.Caption := '';
end;

procedure TMainForm.GenIRQButtonClick(Sender: TObject);
var
  CardNum: Integer;
  Thrd: TIRQThread;

begin
  CardNum := CardCombo.ItemIndex;
  Thrd := TIRQThread.Create(True);
  Thrd.COSPPIs := 0;
  Thrd.CardNum := CardNum;
  Thrd.Start;
  AddLine(CardNum, 'Detecting generic IRQs; generate IRQs to see this sample count them. Press "Abort IRQ Detect" to cancel when done.');
end;

procedure TMainForm.COSIRQButtonClick(Sender: TObject);
var
  Thrd: TIRQThread;
  CardNum: Integer;

begin
  CardNum := CardCombo.ItemIndex;
  Thrd := TIRQThread.Create(True);
  Thrd.CardNum := CardNum;
  Thrd.COSPPIs := CardData[CardNum].PPIs;
  Thrd.Start;
  AddLine(CardNum, 'Detecting COS IRQs; change the input data to see the COS data update. Press "Abort IRQ Detect" to cancel when done.');
end;

procedure TMainForm.AbortButtonClick(Sender: TObject);
var
  CardNum: Integer;

begin
  CardNum := CardCombo.ItemIndex;
  if AbortRequest(CardNum) <> 0 then
    AddLine(CardNum, 'Aborting IRQ detection...')
  else
    AddLine(CardNum, 'No IRQ request to abort.')
  ;
end;

procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  CardNum: Integer;

begin
  {
    Abort all pending IRQ requests. If we don't do this before exiting, later
    when an IRQ comes in or somebody else calls AbortRequest the pending IRQ
    request will attempt to unlock a thread that no longer exists, which is bad.

    (We can AbortRequest for a card with no pending requests, it'll just tell us
    that, and it's easier than remembering which cards have pending requests.)
  }
  for CardNum := 0 to GetNumCards - 1 do
    AbortRequest(CardNum)
  ;
end;

function TMainForm.GetSelBAR: Integer;
begin
  case BARCombo.ItemIndex of
    0: Result := 2;
    1: Result := 1;
    2: Result := 3;
    3: Result := $80;
    4: Result := $81;
    5: Result := $82;
    6: Result := $83;
    7: Result := $84;
    8: Result := $85;
    -1: raise Exception.Create('No BAR selected.');
    else raise Exception.Create('Invalid BAR "' + BARCombo.Items[BARCombo.ItemIndex] + '" selected.');
  end;
end;

procedure TMainForm.OffsetEditChange(Sender: TObject);
var
  Offset: Word;
  Code: Integer;
  bOK: Boolean;

begin
  Val('$' + OffsetEdit.Text, Offset, Code);

  bOK := Code = 0;
  Read8Button.Enabled := bOK;
  Write8Button.Enabled := bOK;
  if (Offset and 1) <> 0 then bOK := False;
  Read16Button.Enabled := bOK;
  Write16Button.Enabled := bOK;
  if (Offset and 3) <> 0 then bOK := False;
  Read32Button.Enabled := bOK;
  Write32Button.Enabled := bOK;
end;

procedure TMainForm.Read8ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := AIOWDM.InBAR8(CardCombo.ItemIndex, iBAR, Offset);
  DataEdit.Text := IntToHex(Data, 2);
end;

procedure TMainForm.Read16ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := AIOWDM.InBAR16(CardCombo.ItemIndex, iBAR, Offset);
  DataEdit.Text := IntToHex(Data, 4);
end;

procedure TMainForm.Read32ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := AIOWDM.InBAR32(CardCombo.ItemIndex, iBAR, Offset);
  DataEdit.Text := IntToHex(Data, 8);
end;

procedure TMainForm.Write8ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := StrToInt('$' + DataEdit.Text);
  if Data > $FF then Data := Data and $FF;
  DataEdit.Text := IntToHex(Data, 2);

  AIOWDM.OutBAR8(CardCombo.ItemIndex, iBAR, Offset, Data);
end;

procedure TMainForm.Write16ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := StrToInt('$' + DataEdit.Text);
  if Data > $FFFF then Data := Data and $FFFF;
  DataEdit.Text := IntToHex(Data, 4);

  AIOWDM.OutBAR16(CardCombo.ItemIndex, iBAR, Offset, Data);
end;

procedure TMainForm.Write32ButtonClick(Sender: TObject);
var
  Offset, Data: Longword;
  iBAR: Integer;

begin
  iBAR := GetSelBAR;
  Offset := StrToInt('$' + OffsetEdit.Text);
  Data := StrToInt('$' + DataEdit.Text);
  DataEdit.Text := IntToHex(Data, 8);

  AIOWDM.OutBAR32(CardCombo.ItemIndex, iBAR, Offset, Data);
end;

procedure TMainForm.WDGIRQButtonClick(Sender: TObject);
var
  Action: LongWord;
  CardNum: Integer;

begin
  CardNum := CardCombo.ItemIndex;

  if SoftRestartRadio.Checked then
    Action := WDG_ACTION_SOFT_RESTART
  else if MostlySoftRestartRadio.Checked then
    Action := WDG_ACTION_MOSTLY_SOFT_RESTART
  else
    Action := WDG_ACTION_IGNORE
  ;

  if DisableWDGCheck.Checked then
    Action := Action + WDG_ACTION_DISABLE
  ;

  WDGHandleIRQ(CardNum, Action);

  AddLine(CardNum, 'Watchdog IRQ handler started, press "Abort IRQ Detect" to cancel.');
end;

procedure TMainForm.GoWDGButtonClick(Sender: TObject);
var
  CardNum: Integer;

begin
  CardNum := CardCombo.ItemIndex;

  WDGSetTimeout(CardNum, 2 * 1000, PCI_WDG_CSM_RATE); //2 sec timeout
  WDGSetResetDuration(CardNum, 1, PCI_WDG_CSM_RATE); //infinite reset
  //WDGSetResetDuration(CardNum, 250, PCI_WDG_CSM_RATE); //250 ms reset

  WDGStart(CardNum);

  AddLine(CardNum, 'Watchdog timing started...');
  CardData[CardNum].WDGPets := 0;
  WDGTime.Tag := CardNum;
  WDGTime.OnTimer := WDGPetTimer;
  WDGTime.Interval := 1000;
  WDGTime.Enabled := True;
end;

procedure TMainForm.WDGPetTimer(Sender: TObject);
var
  CardNum: Integer;

begin
  CardNum := WDGTime.Tag;
  
  if (RelInPortB(CardNum, 4) and $01) = 0 then begin
    AddLine(CardNum, 'Watchdog timed out early!');
    WDGTime.Enabled := False;
    Exit;
  end;

  Inc(CardData[CardNum].WDGPets);
  if CardData[CardNum].WDGPets >= 4 then begin
    AddLine(CardNum, 'Waiting for timeout...');
    WDGTime.OnTimer := WDGWaitTimer;
    WDGTime.Interval := 100;
  end
  else begin
    WDGPet(CardNum);
    AddLine(CardNum, 'Petting watchdog, pet ' + IntToStr(CardData[CardNum].WDGPets) + ' of 3...');
  end;
end;

procedure TMainForm.WDGWaitTimer(Sender: TObject);
begin
  if (RelInPortB(WDGTime.Tag, 4) and $01) = 0 then begin
    AddLine(WDGTime.Tag, 'Watchdog timed out.');
    WDGTime.Enabled := False;
  end;
end;

procedure TMainForm.CancelWDGButtonClick(Sender: TObject);
var
  CardNum: Integer;

begin
  CardNum := CardCombo.ItemIndex;
  WDGStop(CardNum);
  WDGTime.Enabled := False;
  AddLine(CardNum, 'Watchdog timing cancelled.');
end;

procedure TMainForm.TempButtonClick(Sender: TObject);
begin
  TempLabel.Caption := FloatToStr(WDGReadTemp(CardCombo.ItemIndex));
end;

procedure TMainForm.StatusButtonClick(Sender: TObject);
begin
  StatusLabel.Caption := IntToHex(WDGReadStatus(CardCombo.ItemIndex), 2);
end;

procedure TMainForm.RebootButtonClick(Sender: TObject);
begin
  if EmergencyReboot <> 0 then
        AddLine(CardCombo.ItemIndex, 'Rebooting...')
  else
    AddLine(CardCombo.ItemIndex, 'Reboot failed.')
  ;
end;

procedure TMainForm.AddLine(CardNum: Integer; Line: String);
begin
  CardData[CardNum].Log := CardData[CardNum].Log + Line + #13#10#13#10;
  if CardNum = CardCombo.ItemIndex then CardComboChange(CardCombo);
end;

procedure TMainForm.ReceiveCOSData(CardNum: Integer; COSData: TCOSData);
begin
  CardData[CardNum].LastCOSData := COSData;
  if CardNum = CardCombo.ItemIndex then CardComboChange(CardCombo);
end;

procedure TMainForm.ReceiveGenIRQ(CardNum: Integer);
begin
  Inc(CardData[CardNum].IRQCount);
  if CardNum = CardCombo.ItemIndex then CardComboChange(CardCombo);
end;


end.
