unit OS2COM;
(*
**
** Serial and TCP/IP communication routines for DOS, OS/2 and Win9x/NT.
** Tested with: TurboPascal   v7.0,    (DOS)
**              VirtualPascal v2.1,    (OS/2, Win32)
**              FreePascal    v0.99.12 (DOS, Win32)
**              Delphi        v4.0.    (Win32)
**
** Version : 1.01
** Created : 21-May-1998
** Last update : 12-May-1999
**
** Note: (c) 1998-1999 by Maarten Bekers
**
*)

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)
 INTERFACE
(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

uses Combase, BufUnit, Threads,
     {$IFDEF OS2}
       Os2Base
     {$ENDIF}

     {$IFDEF VirtualPascal}
       ,Use32
     {$ENDIF};

Const WriteTimeout   = 20000;                             { Wait max. 20 secs }
      ReadTimeOut    = 5000;                      { General event, 5 secs max }

      InBufSize      = 1024 * 32;
      OutBufSize     = 1024 * 32;

type TOs2Obj = Object(TCommObj)
        ReadProcPtr: Pointer;             { Pointer to TX/RX handler (thread) }
        WriteProcPtr: Pointer;            { Pointer to TX/RX handler (thread) }
        ThreadsInitted: Boolean;          { Are the thread(s) up and running? }

        ClientHandle  : Longint;

        InBuffer      : ^BufArrayObj;             { Buffer system internally used }
        OutBuffer     : ^BufArrayObj;

        DoTxEvent     : PSysEventObj; { Event manually set when we have to transmit }
        DoRxEvent     : PSysEventObj;      { Event manually set when we need data }

        TxClosedEvent : PSysEventObj;    { Event set when the Tx thread is closed }
        RxClosedEvent : PSysEventObj;    { Event set when the Rx thread is closed }

        CriticalTx    : PExclusiveObj;                        { Critical sections }
        CriticalRx    : PExclusiveObj;

        TxThread      : PThreadsObj;           { The Transmit and Receive threads }
        RxThread      : PThreadsObj;

        EndThreads    : Boolean;    { Set to true when we have to end the threads }

        constructor Init;
        destructor Done;

        function  Com_Open(Comport: Byte; BaudRate: Longint; DataBits: Byte;
                           Parity: Char; StopBits: Byte): Boolean; virtual;
        function  Com_OpenKeep(Comport: Byte): Boolean; virtual;
        function  Com_GetChar: Char; virtual;
        function  Com_CharAvail: Boolean; virtual;
        function  Com_Carrier: Boolean; virtual;
        function  Com_SendChar(C: Char): Boolean; virtual;
        function  Com_ReadyToSend(BlockLen: Longint): Boolean; virtual;
        function  Com_GetBPSrate: Longint; virtual;
        function  Com_GetHandle: Longint; virtual;

        procedure Com_OpenQuick(Handle: Longint); virtual;
        procedure Com_Close; virtual;
        procedure Com_SendBlock(var Block; BlockLen: Longint; var Written: Longint); virtual;
        procedure Com_ReadBlock(var Block; BlockLen: Longint; var Reads: Longint); virtual;
        procedure Com_GetBufferStatus(var InFree, OutFree, InUsed, OutUsed: Longint); virtual;
        procedure Com_SetDtr(State: Boolean); virtual;
        procedure Com_GetModemStatus(var LineStatus, ModemStatus: Byte); virtual;
        procedure Com_SetLine(BpsRate: longint; Parity: Char; DataBits, Stopbits: Byte); virtual;
        procedure Com_PurgeInBuffer; virtual;
        procedure Com_PurgeOutBuffer; virtual;
        procedure Com_FlushOutBuffer(Slice: SliceProc); virtual;

        procedure Com_PauseCom(CloseCom: Boolean); virtual;
        procedure Com_ResumeCom(OpenCom: Boolean); virtual;
        procedure Com_SetFlow(SoftTX, SoftRX, Hard: Boolean); virtual;

        procedure Com_SetDataProc(ReadPtr, WritePtr: Pointer); virtual;

        procedure Com_ReadProc(var TempPtr: Pointer);
        procedure Com_WriteProc(var TempPtr: Pointer);

        function  Com_StartThread: Boolean;
        procedure Com_InitVars;
        procedure Com_StopThread;
     end; { object TOs2Obj }

Type POs2Obj = ^TOs2Obj;

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)
 IMPLEMENTATION
(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

uses SysUtils;

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

constructor TOs2Obj.Init;
begin
  inherited Init;

  Com_InitVars;
  ThreadsInitted := FALSE;
end; { constructor Init }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

destructor TOs2Obj.Done;
begin
  inherited done;
end; { destructor Done }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_ReadProc(var TempPtr: Pointer);
Type TBuffRec = Record
         BytesIn   : SmallWord;               { Number of bytes in the buffer }
         MaxSize   : SmallWord;                     { Full size of the buffer }
     end; { TBuffRec }

var Available : Boolean;
    BytesRead : Longint;
    BlockLen  : Longint;
    ReturnCode: Longint;
    BufferRec : TBuffRec;
begin
  repeat
     if DoRxEvent.WaitForEvent(ReadTimeOut) then
      if NOT EndThreads then
       begin
         CriticalRx.EnterExclusive;
         ReturnCode := 0;
         DosDevIoCtl(ClientHandle,                             { File-handle }
                     ioctl_Async,                                 { Category }
                     async_GetInQueCount,                         { Function }
                     nil,                                           { Params }
                     ReturnCode,                          { Max param length }
                     @ReturnCode,                             { Param Length }
                     @BufferRec,                             { Returned data }
                     SizeOf(TBuffRec),                     { Max data length }
                     @ReturnCode);                             { Data length }

         Available := (BufferRec.BytesIn > 00);

         DoRxEvent.ResetEvent;

         if Available then
          begin
            {----------- Start reading the gathered date -------------------}

            if InBuffer^.BufRoom > 0 then
              begin
                BlockLen := BufferRec.BytesIn;
                if BlockLen > InBuffer^.BufRoom then
                  BlockLen := InBuffer^.BufRoom;
                if BlockLen > 1024 then
                  BlockLen := 1024;

                if BlockLen > 00 then
                 begin
                   DosRead(ClientHandle,
                           InBuffer^.TmpBuf,
                           BlockLen,
                           BytesRead);

                   InBuffer^.Put(InBuffer^.TmpBuf, BytesRead);
                 end; { if }

              end; { if }
          end; { if available }

         CriticalRx.LeaveExclusive;
       end; { if RxEvent }
  until EndThreads;

  RxClosedEvent.SignalEvent;
  ExitThisThread;
end; { proc. ComReadProc }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_WriteProc(var TempPtr: Pointer);
var BlockLen    : Longint;
    Written     : Longint;
    ReturnCode  : Longint;
    TempBuf     : ^CharBufType;
begin
  New(TempBuf);

  repeat
     if DoTxEvent.WaitForEvent(WriteTimeOut) then
      if NOT EndThreads then
       begin
         CriticalTx.EnterExclusive;
         DoTxEvent.ResetEvent;

         if OutBuffer^.BufUsed > 00 then
           begin
             Written := 00;
             BlockLen := OutBuffer^.Get(OutBuffer^.TmpBuf, OutBuffer^.BufUsed, false);

             DosWrite(ClientHandle,
                      OutBuffer^.TmpBuf,
                      BlockLen,
                      Written);

             ReturnCode := OutBuffer^.Get(OutBuffer^.TmpBuf, Written, true);
             if Written <> BlockLen then
                DoTxEvent.SignalEvent;
           end; { if }

         CriticalTx.LeaveExclusive;
       end; { if }

  until EndThreads;

  Dispose(TempBuf);
  TxClosedEvent.SignalEvent;
  ExitThisThread;
end; { proc. ComWriteProc }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_StartThread: Boolean;
begin
  Result := false;
  EndThreads := false;
  if ThreadsInitted then EXIT;
  ThreadsInitted := true;

  {----------------------- Create all the events ----------------------------}
  New(DoTxEvent, Init);
  if NOT DoTxEvent.CreateEvent(false) then EXIT;

  New(DoRxEvent, Init);
  if NOT DoRxEvent.CreateEvent(false) then EXIT;

  New(RxClosedEvent, Init);
  if NOT RxClosedEvent.CreateEvent(false) then EXIT;

  New(TxClosedEvent, Init);
  if NOT TxClosedEvent.CreateEvent(false) then EXIT;


  {-------------- Startup the buffers and overlapped events -----------------}
  New(InBuffer, Init(InBufSize));
  New(OutBuffer, Init(OutBufSize));

  {-------------------- Startup a seperate write thread ---------------------}
  New(CriticalTx, Init);
  CriticalTx.CreateExclusive;

  New(TxThread, Init);
  if NOT TxThread.CreateThread(16384,                            { Stack size }
                               WriteProcPtr,               { Actual procedure }
                               nil,                              { Parameters }
                               0)                            { Creation flags }
                                 then EXIT;

  {-------------------- Startup a seperate read thread ----------------------}
  New(CriticalRx, Init);
  CriticalRx.CreateExclusive;

  New(RxThread, Init);
  if NOT RxThread.CreateThread(16384,                            { Stack size }
                               ReadProcPtr,                { Actual procedure }
                               nil,                              { Parameters }
                               0)                            { Creation flags }
                                 then EXIT;

  Result := true;
end; { proc. Com_StartThread }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_InitVars;
begin
  DoTxEvent := nil;
  DoRxEvent := nil;
  RxClosedEvent := nil;
  TxClosedEvent := nil;
  TxThread := nil;
  RxThread := nil;

  InBuffer := nil;
  OutBuffer := nil;
  CriticalRx := nil;
  CriticalTx := nil;
end; { proc. Com_InitVars }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_StopThread;
begin
  EndThreads := true;
  ThreadsInitted := false;

  if DoTxEvent <> nil then DoTxEvent.SignalEvent;
  if DoTxEvent <> nil then DoRxEvent.SignalEvent;


  if TxThread <> nil then TxThread.CloseThread;
  if RxThread <> nil then RxThread.CloseThread;

  if TxClosedEvent <> nil then
   if NOT TxClosedEvent^.WaitForEvent(1000) then
     TxThread.TerminateThread(0);

  if RxClosedEvent <> nil then
   if NOT RxClosedEvent^.WaitForEvent(1000) then
     RxThread.TerminateThread(0);

  if TxThread <> nil then TxThread.Done;
  if RxThread <> nil then RxThread.Done;

  if DoTxEvent <> nil then Dispose(DoTxEvent, Done);
  if DoRxEvent <> nil then Dispose(DoRxEvent, Done);
  if RxClosedEvent <> nil then Dispose(RxClosedEvent, Done);
  if TxClosedEvent <> nil then Dispose(TxClosedEvent, Done);

  if CriticalTx <> nil then Dispose(CriticalTx, Done);
  if CriticalRx <> nil then Dispose(CriticalRx, Done);

  if InBuffer <> nil then Dispose(InBuffer, Done);
  if OutBuffer <> nil then Dispose(OutBuffer, Done);

  Com_InitVars;
end; { proc. Com_StopThread }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_GetHandle: Longint;
begin
  Result := ClientHandle;
end; { func. Com_GetHandle }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_OpenQuick(Handle: Longint);
begin
  ClientHandle := Handle;

  InitFailed := NOT Com_StartThread;
end; { proc. TOs2Obj.Com_OpenQuick }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_OpenKeep(Comport: Byte): Boolean;
var ReturnCode: Longint;
    OpenAction: Longint;
    Temp       : Array[0..15] of Char;
begin
  InitFailed := NOT Com_StartThread;

  if NOT InitFailed then
    begin
      OpenAction := file_Open;
      StrpCopy(Temp, 'COM' + IntToStr(ComPort));

      ReturnCode :=
        DosOpen(Temp,                                    { Filename, eg: COM2 }
                ClientHandle,
                OpenAction,
                0,                                                 { Filesize }
                0,                                               { Attributes }
                FILE_OPEN or OPEN_ACTION_OPEN_IF_EXISTS,         { Open flags }
                OPEN_ACCESS_READWRITE or OPEN_SHARE_DENYNONE or    { OpenMode }
                OPEN_FLAGS_FAIL_ON_ERROR,
                nil);                                   { Extended attributes }

      InitFailed := (ReturnCode <> 0);
    end; { if }

  Com_OpenKeep := NOT InitFailed;
end; { func. Com_OpenKeep }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_Open(Comport: Byte; BaudRate: Longint; DataBits: Byte;
                            Parity: Char; StopBits: Byte): Boolean;
begin
  InitFailed := true;

  if Com_OpenKeep(Comport) then
    begin
      Com_SetLine(BaudRate, Parity, DataBits, StopBits);

      InitFailed := false;
    end; { if }

  Com_Open := NOT InitFailed;
end; { func. TOs2Obj.Com_OpenCom }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_SetLine(BpsRate: longint; Parity: Char; DataBits, Stopbits: Byte);
type TBpsRec = Record
         Rate  : Longint;
         Frac  : Byte;
      end; { record }

var TempRec      : Array[1..3] of Byte;
    BpsRec       : TBpsRec;
    RetLength    : Longint;
    Temp_Parity  : Byte;
    Temp_StopBits: Byte;
begin
  if NOT (DataBits in [5,7,8]) then DataBits := 8;
  if NOT (Parity in ['O', 'E', 'N', 'M', 'S']) then Parity := 'N';
  if NOT (StopBits in [0..2]) then StopBits := 1;

  Temp_Parity := 00;
  Case Parity of
    'N' : Temp_Parity := 00;
    'O' : Temp_Parity := 01;
    'E' : Temp_Parity := 02;
    'M' : Temp_Parity := 03;
    'S' : Temp_Parity := 04;
  end; { case }

  Temp_Stopbits := 00;
  Case StopBits of
     1  : StopBits := 0;
     2  : StopBits := 2;
  end; { case }

  Fillchar(TempRec, SizeOf(TempRec), 00);
  TempRec[01] := DataBits;
  TempRec[02] := Temp_Parity;
  TempRec[03] := Temp_StopBits;

  {------------------------- Set line parameters ----------------------------}
  DosDevIoCtl(ClientHandle,                                    { File-handle }
              ioctl_Async,                                        { Category }
              async_SetLineCtrl,                                  { Function }
              @TempRec,                                             { Params }
              SizeOf(TempRec),                            { Max param length }
              @RetLength,                                     { Param Length }
              @TempRec,                                      { Returned data }
              SizeOf(TempRec),                             { Max data length }
              @RetLength);                                     { Data length }

  {------------------------- Set speed parameters ---------------------------}
  BpsRec.Rate := BpsRate;
  BpsRec.Frac := 00;
  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_ExtSetBaudRate,                                { Function }
              @BpsRec,                                               { Params }
              SizeOf(BpsRec),                              { Max param length }
              @RetLength,                                      { Param Length }
              @BpsRec,                                        { Returned data }
              SizeOf(BpsRec),                               { Max data length }
              @RetLength);                                      { Data length }
end; { proc. TOs2Obj.Com_SetLine }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_Close;
begin
  if DontClose then EXIT;

  if ClientHandle <> -1 then
    begin
      Com_StopThread;
      DosClose(ClientHandle);

      ClientHandle := -1;
    end; { if }

end; { func. TOs2Obj.Com_CloseCom }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_SendChar(C: Char): Boolean;
var Written: Longint;
begin
  Com_SendBlock(C, SizeOf(C), Written);
  Com_SendChar := (Written = SizeOf(c));
end; { proc. TOs2Obj.Com_SendChar }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_GetChar: Char;
var Reads: Longint;
begin
  Com_ReadBlock(Result, SizeOf(Result), Reads);
end; { func. TOs2Obj.Com_GetChar }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_SendBlock(var Block; BlockLen: Longint; var Written: Longint);
begin
  if OutBuffer^.BufRoom < BlockLen then
   repeat
     {$IFDEF OS2}
       DosSleep(1);
     {$ENDIF}
   until (OutBuffer^.BufRoom >= BlockLen) OR (NOT Com_Carrier);

  CriticalTx.EnterExclusive;
    Written := OutBuffer^.Put(Block, BlockLen);
  CriticalTx.LeaveExclusive;

  DoTxEvent.SignalEvent;
end; { proc. TOs2Obj.Com_SendBlock }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_ReadBlock(var Block; BlockLen: Longint; var Reads: Longint);
begin
  if InBuffer^.BufUsed < BlockLen then
    begin
      repeat
        if Com_CharAvail then
          DoRxEvent.SignalEvent;

        DosSleep(1);
      until (InBuffer^.BufUsed >= BlockLen) OR (NOT Com_Carrier);
    end; { if }

  CriticalRx.EnterExclusive;
    Reads := InBuffer^.Get(Block, BlockLen, true);
  CriticalRx.LeaveExclusive;
end; { proc. TOs2Obj.Com_ReadBlock }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_CharAvail: Boolean;

Type TBuffRec = Record
         BytesIn   : SmallWord;               { Number of bytes in the buffer }
         MaxSize   : SmallWord;                     { Full size of the buffer }
     end; { TBuffRec }

var ReturnCode: Longint;
    BufferRec : TBuffRec;
begin
  if InBuffer^.BufUsed < 1 then
    begin
      ReturnCode := 0;
      DosDevIoCtl(ClientHandle,                             { File-handle }
                  ioctl_Async,                                 { Category }
                  async_GetInQueCount,                         { Function }
                  nil,                                           { Params }
                  ReturnCode,                          { Max param length }
                  @ReturnCode,                             { Param Length }
                  @BufferRec,                             { Returned data }
                  SizeOf(TBuffRec),                     { Max data length }
                  @ReturnCode);                             { Data length }

      if (BufferRec.BytesIn > 0) then
        DoRxEvent.SignalEvent;
    end; { if }

  Result := (InBuffer^.BufUsed > 0);
end; { func. TOs2Obj.Com_CharAvail }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_Carrier: Boolean;
var Status    : Byte;
    RetLength : Longint;
begin
  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_GetModemInput,                                 { Function }
              nil,                                                   { Params }
              00,                                          { Max param length }
              @RetLength,                                      { Param Length }
              @Status,                                        { Returned data }
              SizeOf(Status),                               { Max data length }
              @RetLength);                                      { Data length }

  Com_Carrier := Status AND 128 <> 00;
end; { func. TOs2Obj.Com_Carrier }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_GetModemStatus(var LineStatus, ModemStatus: Byte);
begin
  LineStatus := 00;
  ModemStatus := 08;

  if Com_Carrier then ModemStatus := ModemStatus OR (1 SHL 7);
end; { proc. TOs2Obj.Com_GetModemStatus }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_SetDtr(State: Boolean);
type
   TRtsDtrRec = record
      Onmask,
      Offmask : Byte;
   end; { record }

var MaskRec   : TRtsDtrRec;
    RetLength : Longint;
begin
  if State then
    begin
      MaskRec.OnMask   := $01;
      MaskRec.OffMask  := $FF;
    end
      else begin
             MaskRec.OnMask   := $00;
             MaskRec.OffMask  := $FE;
           end; { if }

  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_SetModemCtrl,                                  { Function }
              @MaskRec,                                              { Params }
              SizeOf(MaskRec),                             { Max param length }
              @RetLength,                                      { Param Length }
              @MaskRec,                                       { Returned data }
              SizeOf(MaskRec),                              { Max data length }
              @RetLength);                                      { Data length }
end; { proc. TOs2Obj.Com_SetDtr }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

function TOs2Obj.Com_GetBpsRate: Longint;
type
   TBpsRec = record
      CurBaud  : Longint;                                  { Current BaudRate }
      CurFrac  : Byte;                                     { Current Fraction }
      MinBaud  : Longint;                                  { Minimum BaudRate }
      MinFrac  : Byte;                                     { Minimum Fraction }
      MaxBaud  : Longint;                                  { Maximum BaudRate }
      MaxFrac  : Byte;                                     { Maximum Fraction }
   end; { TBpsRec }

var BpsRec   : TBpsRec;
    Status   : Byte;
    RetLength: Longint;
begin
  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_ExtGetBaudRate,                                { Function }
              nil,                                                   { Params }
              00,                                          { Max param length }
              @RetLength,                                      { Param Length }
              @BpsRec,                                        { Returned data }
              SizeOf(BpsRec),                               { Max data length }
              @RetLength);                                      { Data length }

  Com_GetBpsRate := BpsRec.CurBaud;
end; { func. TOs2Obj.Com_GetBpsRate }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_GetBufferStatus(var InFree, OutFree, InUsed, OutUsed: Longint);
begin
  DoRxEvent.SignalEvent;
  DoTxEvent.SignalEvent;

  InFree := InBuffer^.BufRoom;
  OutFree := OutBuffer^.BufRoom;
  InUsed := InBuffer^.BufUsed;
  OutUsed := OutBuffer^.BufUsed;
end; { proc. TOs2Obj.Com_GetBufferStatus }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_PurgeInBuffer;
begin
  CriticalRx.EnterExclusive;

  InBuffer^.Clear;

  CriticalRx.LeaveExclusive;
end; { proc. TOs2Obj.Com_PurgeInBuffer }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_PurgeOutBuffer;
begin
  CriticalTx.EnterExclusive;

  OutBuffer^.Clear;

  CriticalTx.LeaveExclusive;
end; { proc. TOs2Obj.Com_PurgeInBuffer }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_FlushOutBuffer(Slice: SliceProc);
begin
  DosResetBuffer(ClientHandle);

  inherited Com_FlushOutBuffer(Slice);
end; { proc. Com_FlushOutBuffer }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)


function TOs2Obj.Com_ReadyToSend(BlockLen: Longint): Boolean;
begin
  Result := OutBuffer^.BufRoom >= BlockLen;
end; { func. ReadyToSend }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_PauseCom(CloseCom: Boolean);
begin
  if CloseCom then Com_Close
    else Com_StopThread;
end; { proc. Com_PauseCom }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_ResumeCom(OpenCom: Boolean);
begin
  if OpenCom then Com_OpenKeep(0)
    else Com_StartThread;
end; { proc. Com_ResumeCom }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_SetFlow(SoftTX, SoftRX, Hard: Boolean);
var Dcb      : DCBINFO;
    RetLength: Longint;
begin
  FillChar(Dcb, SizeOF(Dcb), 0);

  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_GetDcbInfo,                                    { Function }
              nil,                                                   { Params }
              00,                                          { Max param length }
              @RetLength,                                      { Param Length }
              @Dcb,                                           { Returned data }
              SizeOf(DcbInfo),                              { Max data length }
              @RetLength);                                      { Data length }

  if (SoftTX) or (SoftRX) then
    begin
      dcb.fbFlowReplace := dcb.fbFlowReplace + MODE_AUTO_RECEIVE + MODE_AUTO_TRANSMIT;
    end
      else begin
             dcb.fbFlowReplace := MODE_RTS_HANDSHAKE;
             dcb.fbCtlHndShake := dcb.fbCtlHndShake + MODE_CTS_HANDSHAKE;
           end; { if }

  dcb.fbTimeout := MODE_NO_WRITE_TIMEOUT + MODE_WAIT_READ_TIMEOUT;
  dcb.bXONChar := $11;
  dcb.bXOFFChar := $13;

  RetLength := SizeOf(DcbInfo);
  DosDevIoCtl(ClientHandle,                                     { File-handle }
              ioctl_Async,                                         { Category }
              async_SetDcbInfo,                                    { Function }
              @Dcb,                                                  { Params }
              SizeOf(DcbInfo),                             { Max param length }
              @RetLength,                                      { Param Length }
              nil,                                            { Returned data }
              RetLength,                                    { Max data length }
              @RetLength);                                      { Data length }

end; { proc. Com_SetFlow }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

procedure TOs2Obj.Com_SetDataProc(ReadPtr, WritePtr: Pointer);
begin
  ReadProcPtr := ReadPtr;
  WriteProcPtr := WritePtr;
end; { proc. Com_SetDataProc }

(*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-+-*-*)

end.
