/*static char *SCCSID = "@(#)mdswap1.c  6.7 91/09/30";*/
#define SCCSID  "@(#)mdswap1.c  6.7 91/09/30"
//==========================================================================
//
//                         IBM Confidential
//
//                 Copyright (c) IBM Corporation  1987, 1990
//                           All Rights Reserved
//
//==========================================================================
//===================== START OF SPECIFICATIONS ==========================
//
//  Source File Name: MDSWAP1.C
//
//  Descriptive Name: Monitor Dispatcher DevHlp routines - Part 5
//
//  Function:  Character Device Monitors provide a mechanism for
//     applications or subsystems to monitor all characters passing
//     through a device driver.  This mechanism allows any reg-
//     istered process to remove, insert or modify the information
//     passing through the device.
//
//     The Monitor Dispatcher DevHlp routines provide the services
//     for device drivers to support character stream monitoring.
//
//  Notes: These modules make up the Monitor Dispatcher DevHlps:
//
//     MDINIT.ASM  - Initialization routine
//     MDINTRF.ASM - Stub entries for devhlps in low mem
//     MDSUBA.ASM  - Common subroutines that must be assembler
//     MDLOCK.C    - dhw_Write and subroutines (interrupt-time routines)
//     MDSWAP1.C   - MD32IMONREAD, MD32IMONWRITE, and subroutines
//     MDSWAP2.C   - dhw_Create, dhw_Register, dhw_Deregister, and subroutines
//
//  Procedures Defined in this module:
//    ArrayToFlat        - copy array entry to flat buffer
//    MonitorFromBufferI - Find monitor with given input buffer
//    MonitorFromBufferO - Find monitor with given output buffer
//    MD32IMONREAD       - 32-bit kernel worker for DosMonRead
//    MD32IMONWRITE      - 32-bit kernel worker for DosMonWrite
//
//  External References (other kernel components):
//    KSEMWaitEvent
//    KSEMPostEvent
//    KSEMResetEvent
//    SELVirtToLin
//
//========================================================================
#define  FNO_NUM FNO_MDSWAP1_C

#include <fnodev.h>
#include <mddvhlp.h>
#include <kmode.h>
#include <string.h>

extern HEADSTRUC Head;
VOID Notify32MonNotify(pPERCHAININFO MonChainPtr);
extern USHORT FlatToArray(char *FlatBuff,pBUFFINFO ArrayPtr,
                          HKSEM WaitSemHandle,HKSEM TranSemHandle,
                          ULONG bytecount, ULONG wait_flag,
                          ULONG dd_timeout_req, ULONG oldflags);

//=========================================================================
//
//  WARNING: Everything below this line will be in swappable code!!!!
//
//=========================================================================
#pragma  code_seg("MON32SWAP_TEXT", "CODE")
//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: ArrayToFlat
//
// DESCRIPTIVE NAME: Copy from an array buffer to a flat buffer
//
// FUNCTION:  ArrayToFlat will determine if the buffer is empty, and wait if
//            appropriate.  If the buffer is not empty, then ArrayToFlat will
//            copy the first array entry to the flat buffer.
//
// ENTRY POINT: ArrayToFlat
//
// INPUT:
//         ArrayPtr        = 0:32 pointer to source buffer info
//         FlatBuff        = 0:32 pointer to destination buffer
//         WaitSemHandle   = Semaphore handle to wait on if neccessary
//                         =    and signal non-empty/empty transition
//         TransSemHandle  = Sema4 handle to signal full/non-full transition
//                         =   for Array
//         BytecntAddr     = 0:32 pointer to put number of bytes copied
//         waitflag        = 0 or 1 (never 2 or called_from_dhflush)
//         oldflags        = status of flag reg before ints were disabled
//
//
// EXIT-NORMAL: returns:
//                      NO_ERROR    (buffer was copied)
//                      TRY_AGAIN   (Wait condition successfully completed)
//
// EXIT-ERROR:  returns error:
//                      ERROR_NOT_ENOUGH_MEMORY
//                      ERROR_INTERRUPT
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//       KSEMWaitEvent
//       KSEMPostEvent
//       KSEMResetEvent
//
//======================== END OF SPECIFICATIONS ===========================
USHORT ArrayToFlat(ArrayPtr,FlatBuff,WaitSemHandle,TransSemHandle,
                   BytecntAddr,waitflag, oldflags)
 // should be called with interrupts disabled
 // re-enables interrupts
   pBUFFINFO        ArrayPtr;        // 0:32 pointer to source buffer info
   char             *FlatBuff;       // 0:32 pointer to destination buffer
   HKSEM            WaitSemHandle; // Semaphore handle to wait on if neccessary
                                    //   and signal non-empty/empty transition
   HKSEM            TransSemHandle; // Sema4 handle to signal full/non-full
                                    //  transition for Array
   USHORT           *BytecntAddr;   // 0:32 pointer to put number of bytes
                                    // copied
   ULONG            waitflag;   // 0 or 1 (never 2 or called_from_dhflush)
   ULONG            oldflags;  // status of flag reg before ints were disabled
{
   ULONG            returncode;
   ULONG            ByteIndex;
   KSEMSTATE        KSEMState;

   returncode = NO_ERROR;
//==========================================================================
//      empty  buffer
//==========================================================================
   if ((ArrayPtr->InIndex == ArrayPtr->OutIndex) && (!(ArrayPtr->Buff_Flags
      &FULL_ARRAY)))                   // empty buffer
      {
      if (waitflag == WF_NOWAIT)
         {
         returncode = ERROR_MON_BUFFER_EMPTY;
         }
      // waitflag == WF_WAIT_INFINTE
      else
         {
         //Need to make sure the semaphore is reset
         KSEMQuery(WaitSemHandle,SSToDS(&(KSEMState)));
         if (KSEMState.kss_fOwned ==0)       //It was posted
            {
            KSEMResetEvent(WaitSemHandle);   //so reset it
            }
         // enable interrupts
         TKEnable(oldflags);
         if (NO_ERROR == (returncode = KSEMWaitEvent(WaitSemHandle,
                                                     INFINITE_TIMEOUT)))
            {
            returncode = TRY_AGAIN;
            }
         else
            {
            // If error, and it's not ERROR_SEM_TIMEOUT, then return this
            // errorcode.  We cannot get ERROR_SEM_TIMEOUT, so we don't check
            // before setting.
            returncode = ERROR_INTERRUPT;
            }
         }
      }
//==========================================================================
// Not empty
//==========================================================================
   else
      {
      ByteIndex = (ArrayPtr->EntrySize)*(ArrayPtr->OutIndex);

      *BytecntAddr = *(USHORT *)(&ArrayPtr->Buff[ByteIndex]);

      memcpy(FlatBuff, &ArrayPtr->Buff[ByteIndex+2],*BytecntAddr);
      // InIndex is 0 based, and ArrayEntries is 1 based, so we check after
      // we add, and we use greater than or equal.
      if ((++ArrayPtr->OutIndex) >= ArrayPtr->ArrayEntries)
         {
         ArrayPtr->OutIndex = 0;
         }
      if (ArrayPtr->Buff_Flags&FULL_ARRAY)
         //We were full, now we are not full, so let prev thread write
         {
         KSEMPostEvent(TransSemHandle);
         }
      if ((ArrayPtr->InIndex == ArrayPtr->OutIndex)
          || (ArrayPtr->ArrayEntries == 1))
         //We were not empty, but we are empty now, so block on next read
         {
         KSEMResetEvent(WaitSemHandle);
         }
      ArrayPtr->Buff_Flags &= !(FULL_ARRAY);
      }
   // enable interrupts
   TKEnable(oldflags);
   return ((USHORT)returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: MonitorFromBufferI
//
// DESCRIPTIVE NAME: Find monitor based on input buffer
//
// ENTRY POINT: MonitorFromBufferI
//
// INPUT:
//         InputBuff   = 16:16 pointer to monitor's input buffer
//         ChainAddr   = 0:32 pointer to place 0:32 pointer to perchaininfo
//
//
// EXIT-NORMAL: returns pointer to permonitorinfo
//                      *ChainAddr points to perchaininfo
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//
//======================== END OF SPECIFICATIONS ===========================
pPERMONINFO MonitorFromBufferI(InputBuff,ChainAddr)
   ULONG            InputBuff;
   ppPERCHAININFO   ChainAddr;
{
   pPERCHAININFO    tempchain;
   pPERMONINFO      tempmon;
   USHORT           curpid;

   curpid = pPTDACur->ptda_Pid;
   tempchain = Head.FirstChain;
   while (tempchain != NULL)
      {
      tempmon = tempchain->First_Monitor;
      while (tempmon != NULL)
         {
         if ((tempmon->MonPID == curpid) && (tempmon->BufferI == InputBuff))
            {
            *ChainAddr = tempchain;
            return (tempmon);
            }
         tempmon = tempmon->NextMon;
         }
      tempchain = tempchain->NextChain;
      }
   return (NULL);
}
//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: MonitorFromBufferO
//
// DESCRIPTIVE NAME: Find monitor based on Output buffer
//
// ENTRY POINT: MonitorFromBufferO
//
// INPUT:
//         OutputBuff  = 16:16 pointer to monitor's Output buffer
//         ChainAddr   = 0:32 pointer to place 0:32 pointer to perchaininfo
//
//
// EXIT-NORMAL: returns pointer to permonitorinfo
//                      *ChainAddr points to perchaininfo
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//
//======================== END OF SPECIFICATIONS ===========================
pPERMONINFO MonitorFromBufferO(OutputBuff,ChainAddr)
   ULONG            OutputBuff;
   ppPERCHAININFO   ChainAddr;
{
   pPERCHAININFO    tempchain;
   pPERMONINFO      tempmon;
   USHORT           curpid;

   curpid = pPTDACur->ptda_Pid;
   tempchain = Head.FirstChain;
   while (tempchain != NULL)
      {
      tempmon = tempchain->First_Monitor;
      while (tempmon != NULL)
         {
         if ((tempmon->MonPID == curpid) && (tempmon->BufferO == OutputBuff)
            )
            {
            *ChainAddr = tempchain;
            return (tempmon);
            }
         tempmon = tempmon->NextMon;
         }
      tempchain = tempchain->NextChain;
      }
   return (NULL);
}

//================== START OF SPECIFICATIONS ===================
//
// SUBROUTINE NAME:  MD32IMONREAD
//
// DESCRIPTIVE NAME:  Read data to a monitor structure.
//
// FUNCTION: MD32IMONREAD reads a data packet from the previous
//           monitor in the chain.
//
//  NOTES: This is the kernel worker routine called by DosMonRead, from
//         MONCALLS.DLL
//
//
//
//
// ENTRY POINT:  MD32IMONREAD
//    LINKAGE:  CALL GATE, from DosMonRead via Dos32IMonRead
//
// INPUT:  (from user's stack)
//
//         @BufferI = 16:16 selector/offset address of input
//                    buffer in calling process' address space.
//                    This is only used as a handle.
//
//         WaitFlag = Block/Run indicator (=0 if to block until
//                    input is ready; =1, if to return if no
//                    input is ready).
//
//         @databuff = 16:16 selector/offset address of
//                       private data area in calling process'
//                       address space.
//
//         @BytecntAddr = 16:16 address of the word containing size
//                          of the private data area.
//
//         retaddr      = 16:16 return address (two calls, only put
//                        parms on stack once.
//
//
// EXIT-NORMAL:  returns 0
//
// EXIT-ERROR:   returns error code:
//                  ERROR_MON_BUFFER_EMPTY
//                  ERROR_MON_BUFFER_TOO_SMALL
//                  ERROR_MON_INVALID_PARMS
//
// EFFECTS:  Number of bytes actually written into private
//           data area returned at @Bytecnt.
//
//
// INTERNAL REFERENCES:
//             MonitorFromBufferI
//             ArrayToFlat
//
//
// EXTERNAL REFERENCES:
//             SELVirtToLin
//             TKProbeBuff
//
//================== END OF SPECIFICATIONS =====================
USHORT cdecl MD32IMONREAD(sef,BufferI,WaitFlag,databuff,BytecntAddr,retaddr)
   THUNKSEF         sef;
   ULONG            BufferI;
   ULONG            WaitFlag;
   struct FarPtr    databuff;
   struct FarPtr    BytecntAddr;
   struct FarPtr    retaddr;
{
   char            *dbuff;
   char            *bcbuff;
   pPERCHAININFO    MonChainPtr;
   pPERMONINFO      MonitorPtr;
   ULONG            oldflags;
   USHORT           returncode;

   sef;                // eliminate compiler error
   retaddr;            // eliminate compiler error

   returncode = NO_ERROR;
   if ((WaitFlag > 1) ||
       (NULL == MonitorFromBufferI(BufferI, SSToDS(&MonChainPtr))))
      {
      returncode = ERROR_MON_INVALID_PARMS;
      }
   else
      {
      do
         {
         returncode = NO_ERROR;
         // Disable interrupts
         oldflags = TKDisable();
         dbuff  = (char *) SELVirtToLin((ULONG)databuff.Offst,
                                        databuff.Segmt);
         bcbuff = (char *) SELVirtToLin((ULONG)BytecntAddr.Offst,
                                        BytecntAddr.Segmt);
         if ((NO_ERROR != TKProbeBuff( bcbuff, 2, TK_UM_WRITE))
            || (NO_ERROR != TKProbeBuff( dbuff,
                                       *(USHORT *)bcbuff,
                                       TK_UM_WRITE)))
            {
            returncode = MYERROR_FORCE_A_FAULT;
            }
         else if (NULL == (MonitorPtr = MonitorFromBufferI(BufferI,
                                                      SSToDS(&MonChainPtr))))
            {
            // Checked before do-while for initial error condition, We got
            // here because the monitor went away, return NOT_ENOUGH_MEMORY
            returncode = ERROR_NOT_ENOUGH_MEMORY; // Monitor went away
            *bcbuff = (USHORT)0;                  //##AW return 0 bytes read
            }
         else if (*(USHORT *)bcbuff < (USHORT)(MonChainPtr->dd_buff_length - 2))
            {
            returncode = ERROR_MON_BUFFER_TOO_SMALL;
            }
         else
            {
            if (MonitorPtr->PrevMon == NULL)
               {
               returncode = ArrayToFlat(MonChainPtr->FFBuff,
                                        dbuff,
                                        MonitorPtr->ReadSemHandle,
                                        MonChainPtr->FFWriteSemHandle,
                                        bcbuff,
                                        WaitFlag,
                                        oldflags);
               // interrupts re-enabled by ArrayToFlat //
               }
            else
               {
               returncode = ArrayToFlat(MonitorPtr->R0Buff,
                                        dbuff,
                                        MonitorPtr->ReadSemHandle,
                                        MonitorPtr->PrevMon->WriteSemHandle,
                                        bcbuff,
                                        WaitFlag,
                                        oldflags);
            // interrupts re-enabled by ArrayToFlat //
               }
            }
         //enable interrupts
         TKEnable(oldflags);
         }  while (returncode == TRY_AGAIN);
      } /* endif */
   return (returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: MD32IMONWRITE
//
// DESCRIPTIVE NAME:  Write data to a monitor structure.
//
// FUNCTION: MD32IMONWRITE writes data from the calling monitor's private
//           output buffer to the following monitor's registered input buffer.
//           It then wakes up the following monitor so that it may read the
//           data.  If this is the last monitor, then the data is written to
//           the device driver's final buffer and the MonNotify routine is
//           called.
//
//
//  NOTES: This is the kernel worker routine called by DosMonWrite, from
//         MONCALLS.DLL
//
//
// ENTRY POINT: MD32IMONWRITE
//    LINKAGE: CALL GATE, from DosMonWrite via Dos32IMonWrite
//
// INPUT:
//
//          BufferO  = 16:16 selector/offset address of output buffer in
//                     calling process' address space. We simply use this
//                     value as a "handle", we never write into this buffer.
//
//          data_buff = 16:16 selector/offset address of private data area
//                     in calling process's address space.
//
//          bytecount = number of bytes to write from data_buff
//
//          retaddr   = 16:16 return address (two calls, only put
//                      parms on stack once.
//
//
// EXIT-NORMAL: returns 0
//
//
// EXIT-ERROR:  returns error code:
//                  ERROR_MON_DATA_TOO_LARGE
//                  ERROR_MON_INVALID_PARMS
//                  ERROR_NOT_ENOUGH_MEMORY
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//             MonitorFromBufferO
//             Notify32MonNotify
//             FlatToArray
//
// EXTERNAL REFERENCES:
//          SELVirtToLin
//          TKProbeBuff
//
//======================== END OF SPECIFICATIONS ===========================
USHORT cdecl MD32IMONWRITE(sef,BufferO,data_buff,bytecount,retaddr)
   THUNKSEF         sef;
   ULONG            BufferO;
   struct FarPtr    data_buff;
   ULONG            bytecount;
   struct FarPtr    retaddr;
   {
   char            *in_buff;
   ULONG            oldflags;
   ULONG            returncode;
   pPERCHAININFO    MonChainPtr;
   pPERMONINFO      MonitorPtr;

   sef;                // eliminate compiler error
   retaddr;            // eliminate compiler error

   returncode = NO_ERROR;
   if ((bytecount == 0)
      || (NULL == (MonitorPtr=MonitorFromBufferO(BufferO,
                                                 SSToDS(&MonChainPtr)))))
      {
      returncode = ERROR_MON_INVALID_PARMS;
      }
   else if ((ULONG)MonChainPtr->dd_buff_length < bytecount)
      {
      returncode = ERROR_MON_DATA_TOO_LARGE;
      }
   else
      {
      do
         {
         returncode = NO_ERROR;
         // Disable interrupts
         oldflags = TKDisable();
         if (NULL == (MonitorPtr = MonitorFromBufferO(BufferO,
                                                      SSToDS(&MonChainPtr))))
            {
            // Checked before do-while for initial error condition, We got
            // here because the monitor went away, return NOT_ENOUGH_MEMORY
            returncode = ERROR_NOT_ENOUGH_MEMORY; // Monitor went away
            }
         else
            {
            in_buff = (char *)SELVirtToLin((ULONG)data_buff.Offst,
                                           data_buff.Segmt);
            if (NO_ERROR != TKProbeBuff( in_buff,
                                         MonChainPtr->dd_buff_length,
                                         TK_UM_WRITE))
               {
               returncode = (MYERROR_FORCE_A_FAULT);
               }
            else
               {
               if (MonitorPtr->NextMon == NULL)
                  {
                  memcpy(&MonChainPtr->FinalBuff[2],in_buff,bytecount)
                     ;
                 ((USHORT *)MonChainPtr->FinalBuff)[0] = (USHORT)(bytecount+2);
                  Notify32MonNotify(MonChainPtr);
                  //========================================================
                  // reset flush_in_progress if needed
                  //========================================================
                  if ((in_buff[0] == 04) && (in_buff[1] == 00))
                     {
                     MonChainPtr->Chain_Flags &= !FLUSH_IN_PROGRESS;
                     }
                  }
               else
                  {
                  returncode = FlatToArray(in_buff,
                     MonitorPtr->NextMon->R0Buff,
                     MonitorPtr->WriteSemHandle,
                     MonitorPtr->NextMon->ReadSemHandle, bytecount,
                     WF_WAIT_INFINITE, INFINITE_TIMEOUT, oldflags );
            // interrupts re-enabled by FlatToArray //
                  }
               }
            }
         // enable interrupts
         TKEnable(oldflags);
         }  while (returncode == TRY_AGAIN);
      }
   return ((USHORT)returncode);
   }

