/*static char *SCCSID = "@(#)mdlock.c   6.4 91/08/21";*/
#define SCCSID  "@(#)mdlock.c   6.4 91/08/21"
//==========================================================================
//
//                         IBM Confidential
//
//                 Copyright (c) IBM Corporation  1987, 1990
//                           All Rights Reserved
//
//==========================================================================
//===================== START OF SPECIFICATIONS ==========================
//
//  Source File Name: MDLOCK.C
//
//  Descriptive Name: Monitor Dispatcher DevHlp routines - Part 4
//
//  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:
//     dhw_MonWrite - worker routine to start a mon packet down a mon chain
//     FlatToArray  - copy a flat buffer to an array entry (or wait)
//     FindChain    - find perchaininfo for a given chain handle
//
//  External References (other kernel components):
//     SELVirtToLin
//     KSEMWaitEvent
//     KSEMPostEvent
//     KSEMResetEvent
//
//========================================================================
#define  FNO_NUM FNO_MDLOCK_C

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

extern LONG     InterruptLevel;      // Interrupt level
extern HEADSTRUC Head;
VOID Notify32MonNotify(pPERCHAININFO MonChainPtr);
pPERCHAININFO FindChain(USHORT Chain_Handle);
USHORT FlatToArray(char *FlatBuff,pBUFFINFO ArrayPtr,HKSEM WaitSemHandle,
                   HKSEM TransSemHandle,ULONG bytecount,ULONG waitflag,
                   ULONG dd_timeout_req, ULONG oldflags);

//=======================================================================
//
//  WARNING: Everything below this line will be in lockable code!!!!
//
//=======================================================================
#pragma  code_seg("MON32LOCK_TEXT", "CODE")
//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: dhw_MonWrite
//
// DESCRIPTIVE NAME: Start a data packet down a monitor chain
//
// FUNCTION: dhw_MonWrite passes a data record(s) to the monitors for
//           filtering by writing a data record(s) to the chains's First
//           Fixed Buffer,  allocated from the resident kernel heap.  If
//           there are no monitors installed, then dhw_MonWrite writes the
//           data record to the device driver's final buffer and calls the
//           MonNotify routine.
//
// NOTES:  This is a Device Helper Service; this function may called both at
//         task time and at interrupt time. This routine can be swappable but
//         must be locked when monitor chains are present (created).
//
//
// ENTRY POINT: dhw_MonWrite
//    LINKAGE: FAR CALL, from dh_MonWrite and dh_MonFlush
//
// INPUT:
//
//          databuff       = 16:16 selector/offset address data record
//          count          = Byte count of data record
//          Chain_Handle   = handle of chain, returned from
//                            previous MonitorCreate call
//          wait_flag      = wait/no wait indicator
//                            0 = wait, infinite timeout
//                            1 = nowait
//                            2 = wait, timeout in dd_timeout_req
//                            WF_CALLED_FROM_DHFLUSH = called from dh_monflush
//          dd_timeout_req = DWORD requested timeout value if wait_flag == 2
//
// EXIT-NORMAL: returns 0
//
//
// EXIT-ERROR:  returns error code:
//                  ERROR_MON_INVALID_HANDLE
//                  ERROR_NOT_ENOUGH_MEMORY
//                  ERROR_INTERRUPT
//                  ERROR_SEM_TIMEOUT
//
// EFFECTS: 'C' (carry flag) and AX register altered.
//          All other registers, including FS and GS are preserved.
//
// INTERNAL REFERENCES:
//          FindChain
//          Notify32MonNotify
//          FlatToArray
//
// EXTERNAL REFERENCES:
//          SELVirtToLin
//
//======================== END OF SPECIFICATIONS ===========================
USHORT dhw_MonWrite(data_buff,bytecount,Chain_Handle,wait_flag,
                    dd_timeout_req)
   struct FarPtr    data_buff;
   ULONG            bytecount;
   USHORT           Chain_Handle;
   ULONG            wait_flag;
   ULONG            dd_timeout_req;
{
   pPERCHAININFO    MonChainPtr;
   char            *in_buff;
   ULONG            oldflags;
   USHORT           returncode;
   USHORT           RWFlags;

   returncode = NO_ERROR;
   RWFlags = 0;
   RWFlags |= FIRST_TIME;
   do
      {
      // Disable interrupts
      oldflags = TKDisable();
      if (NULL == (MonChainPtr = FindChain(Chain_Handle)))
         {
         if (RWFlags&FIRST_TIME)
            {
            returncode = ERROR_MON_INVALID_HANDLE;
            }
         else
            {
            returncode = ERROR_NOT_ENOUGH_MEMORY; // Monitor went away
            }
         }
      else
         {
         RWFlags &= !(FIRST_TIME);
         if ((MonChainPtr->Chain_Flags&DEREG_IN_PROGRESS)
             || (bytecount > (ULONG) MonChainPtr->dd_buff_length))
            {
            returncode = ERROR_NOT_ENOUGH_MEMORY;
            }
         else if (MonChainPtr->Chain_Flags&FLUSH_IN_PROGRESS)
            {
            if (wait_flag&WF_CALLED_FROM_DHFLUSH)
               {
               returncode = NO_ERROR;
               }
            else                    // called from dh_monwrite
               {
               returncode = ERROR_NOT_ENOUGH_MEMORY;
               }
            }
         else
            {
            in_buff = (char *)SELVirtToLin((ULONG)(data_buff.Offst),
                                           data_buff.Segmt);
            if (wait_flag&WF_CALLED_FROM_DHFLUSH)
               {
               MonChainPtr->Chain_Flags |= FLUSH_IN_PROGRESS;
               }
            if (NULL == MonChainPtr->First_Monitor)
               {
               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, MonChainPtr->FFBuff,
                  MonChainPtr->FFWriteSemHandle,
                  MonChainPtr->First_Monitor->ReadSemHandle, bytecount,
                  wait_flag, dd_timeout_req, oldflags);
               // interrupts re-enabled by FlatToArray //
               }
            }
         }
               //re-enable interrupts before returning (or looping)
      // enable interrupts
      TKEnable(oldflags);
      }  while (returncode == TRY_AGAIN);
   return (returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: FlatToArray
//
// DESCRIPTIVE NAME: Copy from a flat buffer to an array buffer
//
// FUNCTION:  FlatToArray will determine if the buffer is full, and wait if
//            appropriate.  If the buffer is not full, then FlatToArray will
//            copy the Flat buffer to the next available array entry.
//
// ENTRY POINT: FlatToArray
//
// *NOTES 1.should be called with interrupts disabled
//        2.  re-enables interrupts
//
// INPUT:
//         FlatBuff        = 0:32 pointer to source buffer
//         ArrayPtr        = 0:32 pointer to destination buffer info
//         WaitSemHandle   = Sema4 handle to wait on if neccessary
//                         =   and signal non-full/full transition
//         TransSemHandle  = Sema4 handle to signal empty/non-empty transition
//                         =   for array
//         bytecount       = number of bytes to write
//         waitflag        = 0, 1, or 2, or CALLED_FROM_DH_MONFLUSH
//         dd_timeout_req  = requested timeout if wait condition
//         oldflags        = status of flags register 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
//                      ERROR_SEM_TIMEOUT
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//        KSEMWaitEvent
//        KSEMPostEvent
//        KSEMResetEvent
//
//======================== END OF SPECIFICATIONS ===========================
USHORT FlatToArray(FlatBuff,ArrayPtr,WaitSemHandle,TransSemHandle,bytecount,
                   waitflag,dd_timeout_req, oldflags)
// *NOTES 1.should be called with interrupts disabled
//        2.  re-enables interrupts
   char             *FlatBuff;         // 0:32 pointer to source buffer
   pBUFFINFO        ArrayPtr;   // 0:32 pointer to destination buffer info
   HKSEM            WaitSemHandle; // Sema4 handle to wait on if neccessary
                                    //   and signal non-full/full transition
   HKSEM            TransSemHandle; // Sema4 handle to signal empty/non-empty
                                    //   transition for array
   ULONG            bytecount;         // number of bytes to write
   ULONG            waitflag;    // 0, 1, or 2, or CALLED_FROM_DH_MONFLUSH
   ULONG            dd_timeout_req; // requested timeout if wait condition
   ULONG            oldflags;  //status of flags reg before ints were disabled
{
   ULONG            returncode;
   ULONG            ByteIndex;
   BOOL             NeedToRunOtherThread;
   KSEMSTATE        KSEMState;

   returncode = NO_ERROR;
//==========================================================================
//       full  buffer
//==========================================================================
   if (ArrayPtr->Buff_Flags&FULL_ARRAY) // full buffer
      {
      if ((InterruptLevel != -1) || (waitflag == WF_NOWAIT)
         || (waitflag == WF_CALLED_FROM_DHFLUSH))
         {
         returncode = ERROR_NOT_ENOUGH_MEMORY;
         }
      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 (waitflag == WF_WAIT_TIMED)
            {
            returncode = KSEMWaitEvent(WaitSemHandle, dd_timeout_req);
            }
         else                          // waitflag == WF_WAIT_INFINITE
            {
            returncode = KSEMWaitEvent(WaitSemHandle, INFINITE_TIMEOUT);
            }

         if (returncode == NO_ERROR)
            {
            returncode = TRY_AGAIN;
            }
         else if (returncode == ERROR_TIMEOUT)  // 16 bit compatibility
            {
            returncode = ERROR_SEM_TIMEOUT;
            }
         }
      }
//==========================================================================
// room to write
//==========================================================================
   else
      {
      ByteIndex = (ULONG)(ArrayPtr->EntrySize)*(ArrayPtr->InIndex);
      memcpy(&ArrayPtr->Buff[ByteIndex+2],FlatBuff,bytecount);
      *(USHORT *)(&ArrayPtr->Buff[ByteIndex]) = (USHORT)bytecount;
      if (ArrayPtr->InIndex == ArrayPtr->OutIndex)
      //We were empty, now we are not empty, so let next thread read
         {
         NeedToRunOtherThread = TRUE;
         }
      else
         {
         NeedToRunOtherThread = FALSE;
         }
      // InIndex is 0 based, and ArrayEntries is 1 based, so we check after
      // we add, and we use greater than or equal.
      if ((++ArrayPtr->InIndex) >= ArrayPtr->ArrayEntries)
         {
         ArrayPtr->InIndex = 0;
         }
      if (ArrayPtr->InIndex == ArrayPtr->OutIndex)
         {
         ArrayPtr->Buff_Flags |= FULL_ARRAY;
         //We were not full, but we are full now, so wait on next write
         KSEMResetEvent(WaitSemHandle);
         }
      if (NeedToRunOtherThread)
         {
         KSEMPostEvent(TransSemHandle);
         }
      }
   // enable interrupts
   TKEnable(oldflags);
   return ((USHORT)returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: FindChain
//
// DESCRIPTIVE NAME: Find the PerChainInfo for the chain with the given handle
//
// ENTRY POINT: FindChain
//
// INPUT:
//     Chain_Handle  = Chain handle to find
//
//
// EXIT-NORMAL: returns pointer to PERCHAININFO
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//
//======================== END OF SPECIFICATIONS ===========================
pPERCHAININFO FindChain(Chain_Handle)
   USHORT           Chain_Handle;
{
   pPERCHAININFO    temp;

   for (temp = Head.FirstChain; temp != NULL; temp = temp->NextChain)
      {
      if (temp->handle == Chain_Handle)
         {
         return (temp);
         }
      }
   return (temp);
}

