/*static char *SCCSID = "@(#)mdswap2.c  6.5 91/10/17";*/
#define SCCSID  "@(#)mdswap2.c  6.5 91/10/17"
//==========================================================================
//
//                         IBM Confidential
//
//                 Copyright (c) IBM Corporation  1987, 1990
//                           All Rights Reserved
//
//==========================================================================
//===================== START OF SPECIFICATIONS ==========================
//
//  Source File Name: MDSWAP2.C
//
//  Descriptive Name: Monitor Dispatcher DevHlp routines - Part 6
//
//  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_MonitorCreate
//     dhw_Register
//     dhw_Deregister
//     FindMonFromPid
//     UseChain
//     UnUseChain
//
//  External References (other kernel components):
//          KSEMCreate
//          KSEMPostEvent
//          KSEMRequestMutex
//          KSEMReleaseMutex
//          KSEMDestroy
//          VMAllocKHB
//          VMFreeKHB
//          VMUnlock
//          VMLockMem
//          SELVirtToLin
//
//========================================================================
// CHANGE HISTORY:
// WHEN       WHO            WHY     WHAT
//-------------------------------------------------------------------------
// 16Feb1993  D.Azzarito     62268   Monitor chain wasn't pointing to first

#define  FNO_NUM FNO_MDSWAP2_C

#define  SLEEPYTIME 250

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

VMLH MonLockHandle;
extern HEADSTRUC Head;
extern pPERMONINFO MonitorFromBufferI(PVOID InputBuff,ppPERCHAININFO
                                      ChainAddr);

extern pPERMONINFO MonitorFromBufferO(PVOID OutputBuff,ppPERCHAININFO
                                      ChainAddr);

extern ULONG access_ubuffers(PVOID BufferIn,PVOID BufferOut);
extern pPERCHAININFO FindChain(USHORT Chain_Handle);
extern pPERCHAININFO UseChain(USHORT Chain_Handle);
extern VOID UnUseChain(pPERCHAININFO MonChainPtr);
extern pPERMONINFO FindMonFromPid(pPERCHAININFO MonChainPtr,USHORT
                                  Monitor_PID);

//      Lock Region Boundaries
extern char      EndMON32LOCK_TEXT;    // Segment Labels
extern char      StartMON32LOCK_TEXT;  // Segment Labels
//=========================================================================
//
//  WARNING: Everything below this line will be in swappable code!!!!
//
//=========================================================================
#pragma  code_seg("MON32SWAP_TEXT", "CODE")
//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: dhw_MonitorCreate
//
// DESCRIPTIVE NAME: Create a monitor chain or remove a monitor chain
//
// FUNCTION: Create a monitor chain or remove a monitor chain
//
//
// NOTES:  This is a Device Helper Service; this function may only be called
//         task time. This routine may be swappable
//
// ENTRY POINT: dhw_MonitorCreate
//    LINKAGE: FAR CALL, from dh_MonitorCreate
//
// INPUT:  FinalBuff      = 16:16 segment:offset of device driver
//                          buffer where filtered data (i.e.,
//                          that has already passed thru the
//                          monitor chain) is to be placed.
//
//         Dev_Notify_Rtn = 16:16 segment:offset of device driver notification
//                         routine.
//
//         Chain_Handle   == 0; to create a new monitor chain;
//                        != 0; = handle returned from previous Monitor
//                                Create call, indicating that this chain
//                                is to be removed.
//
//
//
// EXIT-NORMAL: if input Chain_Handle != 0, returns NO_ERROR
//              if input Chain_Handle == 0, returns NO_ERROR and
//                                            new Chain_Handle returned
//
//
// EXIT-ERROR:  if input Chain_Handle != 0, returns error:
//                            ERROR_MON_INVALID_PARMS  //Chain not empty
//                            ERROR_MON_INVALID_HANDLE //No match for handle
// EXIT-ERROR:  if input Chain_Handle == 0, returns error:
//                            ERROR_NOT_ENOUGH_MEMORY
//
// EFFECTS:
//
//
// INTERNAL REFERENCES:
//
//
// EXTERNAL REFERENCES:
//          KSEMCreate
//          KSEMRequestMutex
//          KSEMDestroy
//          VMAllocKHB
//          VMFreeKHB
//          VMUnlock
//          VMLockMem
//          SELVirtToLin
//
//======================== END OF SPECIFICATIONS ===========================
USHORT dhw_MonitorCreate(DevFinalBuff,Dev_Notify_Rtn,Chain_Handle)
   struct FarPtr    DevFinalBuff;
   ULONG            Dev_Notify_Rtn;
   struct FarPtr    Chain_Handle;
{
   pPERCHAININFO    TempChainPtr;
   pPERCHAININFO    MonChainPtr;
   KSEMSTATE        KSEMState;
   USHORT          *ChainHandle32;
   ULONG            returncode;
   ULONG            oldflags;
   USHORT           entrycnt;
   USHORT           buffsize;

   returncode = NO_ERROR;
   if (NO_ERROR == (returncode = KSEMRequestMutex(Head.HeadSemHandle,
      INFINITE_TIMEOUT)))
   {
      ChainHandle32 = (USHORT *)SELVirtToLin((ULONG)(Chain_Handle.Offst),
                                             Chain_Handle.Segmt);

 //Remove the chain with handle ChainHandle32
      if (*ChainHandle32 != 0)
         {
         MonChainPtr = Head.FirstChain;
         TempChainPtr = MonChainPtr;
         while ((MonChainPtr != NULL) && (MonChainPtr->handle !=
            *ChainHandle32))
            {
            TempChainPtr = MonChainPtr;
            MonChainPtr = MonChainPtr->NextChain;
            }
         if (MonChainPtr == NULL)
            {
            returncode = ERROR_MON_INVALID_HANDLE; // No match
            }
         else if (MonChainPtr->First_Monitor != NULL)
            {
            returncode = ERROR_MON_INVALID_PARMS; // Chain not empty
            }
         else
            {
            // disable interrupts
            oldflags = TKDisable();
            if (MonChainPtr == Head.FirstChain)
               {
               Head.FirstChain = MonChainPtr->NextChain;
               }
            else
               {
               TempChainPtr->NextChain = MonChainPtr->NextChain;
               }
            // enable interrupts
            TKEnable(oldflags);

            KSEMQuery(MonChainPtr->ChainSemHandle,SSToDS(&(KSEMState)));
            while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
               {
               TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
               KSEMQuery(MonChainPtr->ChainSemHandle,SSToDS(&(KSEMState)));
               }
            KSEMDestroy(MonChainPtr->ChainSemHandle);

            KSEMPostEvent(MonChainPtr->FFWriteSemHandle);
            KSEMQuery(MonChainPtr->FFWriteSemHandle,SSToDS(&(KSEMState)));
            while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
               {
               TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
               KSEMQuery(MonChainPtr->FFWriteSemHandle,SSToDS(&(KSEMState)));
               }
            KSEMDestroy(MonChainPtr->FFWriteSemHandle);
            VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr->FFBuff->Buff);
            VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr->FFBuff);
            VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr);
            if (Head.FirstChain == NULL)
               {
               //UnLock Code in interrupt path
               VMUnlock(&MonLockHandle);
               }
         }

         *ChainHandle32 = 0;           // Makes ASM stub easier
      }

 //Create a chain and give it a new handle in ChainHandle32
      else
         {
         //Allocate memory for PerChainInfo from resident kernheap
         if (NO_ERROR != (VMAllocKHB(VM_HKH_PUB_RESRW, (sizeof(struct
            PERCHAININFO)), (VMHOB)MONITOROWNER, (PG_RESIDENT | PG_U),
            SSToDS(&MonChainPtr))))
            {
            returncode = ERROR_FROM_1ST_ALLOC;
            }
         //=================================================================
         // Allocate mem for First fixed buff info from res kernheap
         //=================================================================
         else if (NO_ERROR != (VMAllocKHB(VM_HKH_PUB_RESRW, (sizeof(struct
               BUFFINFO)), (VMHOB)MONITOROWNER, (PG_RESIDENT | PG_U),
               &(MonChainPtr->FFBuff))))
            {
            returncode = ERROR_FROM_2ND_ALLOC;
            }
         //==============================================================
         // Allocate mem for First fixed buff from res kernheap
         //==============================================================
         else
            {
            MonChainPtr->FinalBuff =
                       (char *)SELVirtToLin((ULONG)DevFinalBuff.Offst,
                                            DevFinalBuff.Segmt);

            MonChainPtr->dd_buff_length =*(USHORT *)MonChainPtr->FinalBuff;
            for (buffsize=0,entrycnt=0;
                ((buffsize < MAXFFBUFF)&&(entrycnt<FFBUFFMULTIPLIER));
                buffsize+=(USHORT)(MonChainPtr->dd_buff_length),entrycnt++);
            if ((buffsize > MAXFFBUFF)&&(entrycnt!= 1))
               {
               buffsize -=(USHORT)MonChainPtr->dd_buff_length;
               entrycnt--;
               }
            if (NO_ERROR != (VMAllocKHB(VM_HKH_PUB_RESRW,buffsize, (ULONG)
               MONITOROWNER, (PG_RESIDENT | PG_U),
               &(MonChainPtr->FFBuff->Buff))))
               {
               returncode = ERROR_FROM_3RD_ALLOC;
               }
               // Create Event Sem, posted
            else if (NO_ERROR != KSEMCreate(
                                   (PHKSEM)&(MonChainPtr->FFWriteSemHandle),
                                    KRNL_EVENTSEM))
               {
               returncode = ERROR_FROM_1ST_CREATESEM;
               }
               // Create MutexSem, unowned
            else if (NO_ERROR != (KSEMCreate(
                              (PHKSEM)&(MonChainPtr->ChainSemHandle),
                              KRNL_MUTEXSEM)))
               {
               returncode = ERROR_FROM_2ND_CREATESEM;
               }
            else
               {
               //initialize PerChainInfo
               MonChainPtr->First_Monitor = NULL;
               MonChainPtr->Last_Monitor = NULL;
               MonChainPtr->Last_FIRST_Monitor = NULL;
               MonChainPtr->Last_LAST_Monitor = NULL;
               MonChainPtr->dev_notify = Dev_Notify_Rtn;
               MonChainPtr->dev_buff = DevFinalBuff;
               MonChainPtr->Chain_Flags = 0;
               // initialize FFBuff (BUFFINFO)
               MonChainPtr->FFBuff->Buff_Flags = 0;
               MonChainPtr->FFBuff->InIndex = 0;
               MonChainPtr->FFBuff->OutIndex = 0;
               MonChainPtr->FFBuff->EntrySize =
                  MonChainPtr->dd_buff_length;
               MonChainPtr->FFBuff->ArrayEntries = entrycnt;
               //  disable interrupts
               oldflags = TKDisable();
               if (Head.FirstChain != NULL)
                  {
                  TempChainPtr = Head.FirstChain;
                  //find where to insert chain
                  while ((TempChainPtr->NextChain != NULL) &&
                       (TempChainPtr->NextChain->handle == (USHORT)
                       ((TempChainPtr->handle)-1)))
                     {
                     TempChainPtr = TempChainPtr->NextChain;
                     }
                 // Insert between two chains
                  if (TempChainPtr->NextChain != NULL)
                     {
                     MonChainPtr->NextChain =
                        TempChainPtr->NextChain;
                     TempChainPtr->NextChain = MonChainPtr;
                     //There's a previous monitor chain, so we don't
                     //need to check for handle overflow
                     MonChainPtr->handle = (USHORT)
                        (MonChainPtr->NextChain->handle+1);
                     }
                 // Insert as First Chain
                  else
                     {
                     if (Head.FirstChain->handle == -1)              // 62268
                        {
                        // Handle will overflow
                        //  (become 0 when we add 1)
                        returncode = ERROR_FROM_VMLOCK;
                        }
                     else
                        {
                        // We won't overflow the handle
                        MonChainPtr->NextChain = Head.FirstChain;
                        Head.FirstChain = MonChainPtr;
                        MonChainPtr->handle = (USHORT)
                           (MonChainPtr->NextChain->handle+1);
                        }
                     }
                  }
               else     // this is the first chain and only chain
                  //Lock Code in interrupt path
                  {
                  // enable interrupts before doing VMLock
                  TKEnable(oldflags);
                  // Do VMLock before setting Head.FirstChain
                  if (NO_ERROR != VMLockMem((ulong_t)
                     &StartMON32LOCK_TEXT, &EndMON32LOCK_TEXT-
                     &StartMON32LOCK_TEXT, VML_LONG, &MonLockHandle,
                     (struct pagelist_s *) VM_NO_ADDR, (ulong_t *) VM_NO_ADDR
                     ))
                     {
                     returncode = ERROR_FROM_VMLOCK;
                     }
                  //set these even if error, reduces logic in switch statement
                  MonChainPtr->handle = 1;
                  MonChainPtr->NextChain = NULL;
                  Head.FirstChain = MonChainPtr; // Set this last!
                  }
               // enable interrupts
               TKEnable(oldflags);
               }
            }
         switch (returncode)
            {
            case (ERROR_FROM_VMLOCK) :
               Head.FirstChain = Head.FirstChain->NextChain;
               KSEMQuery(MonChainPtr->ChainSemHandle,SSToDS(&(KSEMState)));
               while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
                  {
                  TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
                  KSEMQuery(MonChainPtr->ChainSemHandle,SSToDS(&(KSEMState)));
                  }
               KSEMDestroy(MonChainPtr->ChainSemHandle);
            case (ERROR_FROM_2ND_CREATESEM) :
               KSEMPostEvent(MonChainPtr->FFWriteSemHandle);
               KSEMQuery(MonChainPtr->FFWriteSemHandle,SSToDS(&(KSEMState)));
               while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
                  {
                  TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
                  KSEMQuery(MonChainPtr->FFWriteSemHandle,SSToDS(&(KSEMState)));
                  }
               KSEMDestroy(MonChainPtr->FFWriteSemHandle);
            case (ERROR_FROM_1ST_CREATESEM) :
               VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr->FFBuff->Buff);
            case (ERROR_FROM_3RD_ALLOC) :
               VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr->FFBuff);
            case (ERROR_FROM_2ND_ALLOC) :
               VMFreeKHB(VM_HKH_PUB_RESRW, MonChainPtr);
            case (ERROR_FROM_1ST_ALLOC) :
               returncode = ERROR_NOT_ENOUGH_MEMORY;
               break;
            default  :
               *ChainHandle32 = MonChainPtr->handle;
               break;
            }
         }
      KSEMReleaseMutex(Head.HeadSemHandle);
      }
   return ((USHORT)returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: dhw_Register
//
// DESCRIPTIVE NAME: Add a monitor to a monitor chain
//
// FUNCTION: Add a monitor to a monitor chain
//
//
// NOTES:  This is a Device Helper Service; this function may only be called
//         task time. This routine may be swappable
//
//         BufferI and BufferO are not used for input of output, only for
//         determining which monitor is calling DosMonRead/Write.
//
// ENTRY POINT: dhw_Register
//    LINKAGE: FAR CALL, from dh_Register
//
// INPUT:
//       BufferI        = 16:16 address of monitor's registered input buffer
//       BufferO        = 16:16 address of monitor's registered output buffer
//       Monitor_PID    = process ID of monitor task
//       Chain_Handle   = handle for monitor chain returned on previous
//                           MonitorCreate call.
//       Placement_Flag =  0 or 3 = default (middle)
//                         1 or 4 = first
//                         2 or 5 = last
//
// EXIT-NORMAL: returns NO_ERROR
//               # Monitors remaining is chain is returned in MonitorsLeft
//
//
// EXIT-ERROR:  returns error:
//                  ERROR_MON_INVALID_HANDLE
//                  ERROR_NOT_ENOUGH_MEMORY
//
// EFFECTS:
//
//
// INTERNAL REFERENCES:
//             UseChain
//             UnUseChain
//             MonitorFromBufferI
//             MonitorFromBufferO
//             access_ubuffers
//
// EXTERNAL REFERENCES:
//             VMAllocKHB
//             VMFreeKHB
//             SELVirtToLin
//             KSEMCreate
//             KSEMDestroy
//
//
//
//======================== END OF SPECIFICATIONS ===========================
USHORT dhw_Register(BufferSel,BufferIoff,BufferOoff,Monitor_PID,
                    Chain_Handle,Placement_Flag)
   SEL              BufferSel;
   USHORT           BufferIoff;
   USHORT           BufferOoff;
   USHORT           Monitor_PID;
   USHORT           Chain_Handle;
   USHORT           Placement_Flag;
{
   USHORT          *In_Buff;
   USHORT          *Out_Buff;
   KSEMSTATE        KSEMState;
   pPERCHAININFO    MonChainPtr;
   pPERMONINFO      MonitorPtr;
   ULONG            oldflags;
   USHORT           returncode;
   USHORT           entrycnt;
   USHORT           buffsize;

   returncode = NO_ERROR;
   if (NULL == (MonChainPtr = (UseChain(Chain_Handle)))) // gets perchain sem
      {
      returncode = ERROR_MON_INVALID_HANDLE;
      }
   else if ((Placement_Flag > PF_LAST_SPECIAL)
         || (NULL != MonitorFromBufferO (MAKEPTR(BufferSel, BufferOoff),
                                                 SSToDS(&MonChainPtr)))
         || (NULL != MonitorFromBufferI(MAKEPTR(BufferSel, BufferIoff),
                                                SSToDS(&MonChainPtr)))
         || (NO_ERROR != access_ubuffers(MAKEPTR(BufferSel, BufferOoff),
                                         MAKEPTR(BufferSel, BufferIoff))))
         // The placement flag is invalid, or this process has
         // already registered these buffers, or buffers are invalid
      {
      returncode = ERROR_MON_INVALID_PARMS;
      }
   else
      {
      In_Buff  = (USHORT *)SELVirtToLin((ULONG)BufferIoff,
                                        BufferSel);
      Out_Buff = (USHORT *)SELVirtToLin((ULONG)BufferOoff,
                                        BufferSel);

      //Tell monitor how big DD's buffer is (in case it's too small)
      if (In_Buff[0] >= 4)
         {
         (USHORT)In_Buff[1] = MonChainPtr->dd_buff_length;
         }
      if (Out_Buff[0] >= 4)
         {
         (USHORT)Out_Buff[1] = MonChainPtr->dd_buff_length;
         }

      if (  ((USHORT)In_Buff[0]  < MonChainPtr->dd_buff_length)
          ||((USHORT)Out_Buff[0] < MonChainPtr->dd_buff_length))
         {
         returncode = ERROR_MON_BUFFER_TOO_SMALL;
         }
      else
         {
         for (buffsize=0,entrycnt=0;
             ((buffsize < MAXFFBUFF)&&(entrycnt< FFBUFFMULTIPLIER));
             buffsize+=(USHORT)(MonChainPtr->dd_buff_length),entrycnt++);
         if ((buffsize > MAXFFBUFF)&&(entrycnt!= 1))
            {
            buffsize -=(USHORT)MonChainPtr->dd_buff_length;
            entrycnt--;
            }
         // allocate PerMonitorInfo from resident kernel heap
         if (NO_ERROR != VMAllocKHB(VM_HKH_PUB_RESRW,
            (sizeof(struct PERMONINFO)), (VMHOB)MONITOROWNER,
            (PG_RESIDENT | PG_U), SSToDS(&MonitorPtr)))
            {
            returncode = ERROR_FROM_1ST_ALLOC;
            }
         // allocate R0Buff (BUFFERINFO) from swappable kernel heap
         else if (NO_ERROR != VMAllocKHB(VM_HKH_PUB_SWAPRW,
               (sizeof(struct BUFFINFO)), (VMHOB)MONITOROWNER,
               (PG_SWAPPABLE | PG_U), &(MonitorPtr->R0Buff)))
            {
            returncode = ERROR_FROM_2ND_ALLOC;
            }
         // allocate Ring0 Buffer for this monitor from SwapKernHeap
         else if (NO_ERROR != VMAllocKHB(VM_HKH_PUB_SWAPRW, buffsize,
               (VMHOB) MONITOROWNER, (PG_SWAPPABLE | PG_U),
               &(MonitorPtr->R0Buff->Buff)))
            {
            returncode = ERROR_FROM_3RD_ALLOC;
            }
            // Create Event Sem, and reset
         else if (NO_ERROR != KSEMCreate(
                                 (PHKSEM)&(MonitorPtr->ReadSemHandle),
                                 KRNL_EVENTSEM))
            {
            returncode = ERROR_FROM_1ST_CREATESEM;
            }
         else
            {
            KSEMResetEvent(MonitorPtr->ReadSemHandle);
            // Create Event Sem, posted
            if (NO_ERROR != KSEMCreate(
                             (PHKSEM)&(MonitorPtr->WriteSemHandle),
                             KRNL_EVENTSEM))
               {
               returncode = ERROR_FROM_2ND_CREATESEM;
               }
            else
               {
               //Initialize data in MonitorPtr (permoninfo)
               MonitorPtr->BufferI = (ULONG)MAKEPTR(BufferSel, BufferIoff);
               MonitorPtr->BufferO = (ULONG)MAKEPTR(BufferSel, BufferOoff);
               MonitorPtr->MonPID = Monitor_PID;
               MonitorPtr->PrevMon = NULL;
               MonitorPtr->NextMon = NULL;
               //Initialize data in MonitorPtr->R0Buff (BuffInfo)
               MonitorPtr->R0Buff->InIndex = 0;
               MonitorPtr->R0Buff->OutIndex = 0;
               MonitorPtr->R0Buff->Buff_Flags = 0;
               MonitorPtr->R0Buff->EntrySize = MonChainPtr->dd_buff_length;
               MonitorPtr->R0Buff->ArrayEntries = entrycnt;
               if (Placement_Flag > PF_LAST)
                  {
                  Placement_Flag -= PF_SPECIAL_ADDER;
                  }
               // disable interrupts
               oldflags = TKDisable();
               //===============================================
               // Add at Last_FIRST_Monitor point
               //===============================================
               if ((Placement_Flag == PF_DEFAULT) ||
                  (Placement_Flag == PF_FIRST))
                  {
                  if (NULL == MonChainPtr->Last_FIRST_Monitor)
                     {
                     if (NULL == MonChainPtr->First_Monitor)
                        {
                        // install as one and only monitor
                        // in chain
                        MonChainPtr->First_Monitor = MonitorPtr;
                        MonChainPtr->Last_Monitor = MonitorPtr;
                        }
                     else
                        {
                        // install at beginning
                        MonitorPtr->NextMon = MonChainPtr->First_Monitor;
                        MonChainPtr->First_Monitor->PrevMon = MonitorPtr;
                        MonChainPtr->First_Monitor = MonitorPtr;
                        }
                     }
                  else
                     {
                     if (NULL == MonChainPtr->Last_FIRST_Monitor->NextMon )
                        {
                        // install at end
                        MonitorPtr->PrevMon = MonChainPtr->Last_Monitor;
                        MonChainPtr->Last_Monitor->NextMon = MonitorPtr;
                        MonChainPtr->Last_Monitor = MonitorPtr;
                        }
                     else
                        {
                        // insert between two monitors
                        MonitorPtr->PrevMon =
                         MonChainPtr->Last_FIRST_Monitor;
                        MonitorPtr->NextMon =
                         MonChainPtr->Last_FIRST_Monitor->NextMon ;
                        MonitorPtr->PrevMon->NextMon = MonitorPtr;
                        MonitorPtr->NextMon->PrevMon = MonitorPtr;
                        }
                     }
                  if (Placement_Flag == PF_FIRST)
                     {
                     MonChainPtr->Last_FIRST_Monitor = MonitorPtr;
                     }
                  }
               //===============================================
               // Add at Last_LAST_Monitor point
               //===============================================
               else        // Placement_Flag == PF_LAST
                  {
                  if (NULL == MonChainPtr->Last_LAST_Monitor)
                     {
                     if (NULL == MonChainPtr->First_Monitor)
                        {
                        // install as one and only monitor
                        // in chain
                        MonChainPtr->First_Monitor = MonitorPtr;
                        MonChainPtr->Last_Monitor = MonitorPtr;
                        }
                     else
                        {
                        // install at end
                        MonitorPtr->PrevMon = MonChainPtr->Last_Monitor;
                        MonChainPtr->Last_Monitor->NextMon = MonitorPtr;
                        MonChainPtr->Last_Monitor = MonitorPtr;
                        }
                     }
                  else
                     {
                     if (NULL == MonChainPtr->Last_LAST_Monitor->PrevMon)
                        {
                        // install at beginning
                        MonitorPtr->NextMon = MonChainPtr->First_Monitor;
                        MonChainPtr->First_Monitor->PrevMon = MonitorPtr;
                        MonChainPtr->First_Monitor = MonitorPtr;
                        }
                     else
                        {
                        //Insert between two monitors
                        MonitorPtr->PrevMon =
                          MonChainPtr->Last_LAST_Monitor->PrevMon
                           ;
                        MonitorPtr->NextMon=MonChainPtr->Last_LAST_Monitor;
                        MonitorPtr->PrevMon->NextMon = MonitorPtr;
                        MonitorPtr->NextMon->PrevMon = MonitorPtr;
                        }
                     }
                  MonChainPtr->Last_LAST_Monitor = MonitorPtr;
                  }
               //===============================================
               // Wake up Previous Mon to write, if neccessary
               //===============================================
               if (MonitorPtr->PrevMon)
                  {
                  KSEMPostEvent(MonitorPtr->PrevMon->WriteSemHandle);
                  // if FFBuff is full (dh_Write thrd waiting), we will
                  // get the data on first DosMonRead. No need to clear
                  // a sema4 now. Don't need to worry about NextMon,
                  // because it's Ring0Buff has not changed.
                  }
               // enable interrupts
               TKEnable(oldflags);
               }
            }
         switch (returncode)
            {
            case (ERROR_FROM_2ND_CREATESEM) :
               KSEMPostEvent(MonitorPtr->ReadSemHandle);
               KSEMQuery(MonitorPtr->ReadSemHandle,SSToDS(&(KSEMState)));
               while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
                  {
                  TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
                  KSEMQuery(MonitorPtr->ReadSemHandle,SSToDS(&(KSEMState)));
                  }
               KSEMDestroy(MonitorPtr->ReadSemHandle);
            case (ERROR_FROM_1ST_CREATESEM) :
               VMFreeKHB(VM_HKH_PUB_SWAPRW, MonitorPtr->R0Buff->Buff);
            case (ERROR_FROM_3RD_ALLOC) :
               VMFreeKHB(VM_HKH_PUB_SWAPRW, MonitorPtr->R0Buff);
            case (ERROR_FROM_2ND_ALLOC) :
               VMFreeKHB(VM_HKH_PUB_RESRW, MonitorPtr);
            case (ERROR_FROM_1ST_ALLOC) :
               returncode = ERROR_NOT_ENOUGH_MEMORY;
            default  :
               break;
            }
         }
      }
   UnUseChain(MonChainPtr);            // release perchain sem, if needed
   return (returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: dhw_Deregister
//
// DESCRIPTIVE NAME: Remove all monitors with matching PID from a mon chain
//
// FUNCTION: Remove all monitors with matching PID from a monitor chain
//
// NOTES:  This is a Device Helper Service; this function may only be called
//         task time. This routine may be swappable
//
//
// ENTRY POINT: dhw_Deregister
//    LINKAGE: FAR CALL, from dh_Deregister
//
// INPUT:
//        Monitor_PID  = monitor process ID
//        Chain_Handle = handle for monitor chain returned on
//                       previous MonitorCreate call.
//
// EXIT-NORMAL: returns NO_ERROR
//               # Monitors remaining is chain is returned in MonitorsLeft
//
//
// EXIT-ERROR:  returns error:
//                  ERROR_MON_INVALID_HANDLE
//
// EFFECTS:
//
//
// INTERNAL REFERENCES:
//             UseChain
//             UnUseChain
//             FindMonFromPid
//
// EXTERNAL REFERENCES:
//             KSEMPostEvent
//             KSEMDestroy
//             VMFreeKHB
//             SelVirtToLin
//
//======================== END OF SPECIFICATIONS ===========================
USHORT dhw_Deregister(Monitor_PID,Chain_Handle,MonitorsLeft)
   USHORT           Monitor_PID;
   USHORT           Chain_Handle;
   struct FarPtr    MonitorsLeft;
{
   pPERCHAININFO    MonChainPtr;
   pPERMONINFO      MonitorPtr;
   KSEMSTATE        KSEMState;
   ULONG            oldflags;
   USHORT           returncode;
   USHORT           i;

   returncode = NO_ERROR;
   if (NULL == (MonChainPtr = UseChain(Chain_Handle))) // gets perchain sem
      {
      returncode = ERROR_MON_INVALID_HANDLE;
      }
   else
      {
      MonChainPtr->Chain_Flags |= DEREG_IN_PROGRESS;
      while (NULL != (MonitorPtr = FindMonFromPid(MonChainPtr, Monitor_PID)))
         {
         // disable interrupts
         oldflags = TKDisable();
         //=================================================================
         // Relink chain
         //=================================================================
         if (MonitorPtr->NextMon != NULL)
            {
            MonitorPtr->NextMon->PrevMon = MonitorPtr->PrevMon;
            }
         if (MonitorPtr->PrevMon != NULL)
            {
            MonitorPtr->PrevMon->NextMon = MonitorPtr->NextMon;
            }
         //=================================================================
         // Reset LAST_LAST and  LAST_FIRST (ordering pointers)
         //=================================================================
         if (MonChainPtr->Last_FIRST_Monitor == MonitorPtr)
            {
            MonChainPtr->Last_FIRST_Monitor = MonitorPtr->PrevMon;
            }
         else if (MonChainPtr->Last_LAST_Monitor == MonitorPtr)
            {
            MonChainPtr->Last_LAST_Monitor = MonitorPtr->NextMon;
            }
         //=================================================================
         // Reset first and last pointers
         //=================================================================
         if (MonChainPtr->First_Monitor == MonitorPtr)
            {
            MonChainPtr->First_Monitor = MonitorPtr->NextMon;
            }
         if (MonChainPtr->Last_Monitor == MonitorPtr)
            {
            MonChainPtr->Last_Monitor = MonitorPtr->PrevMon;
            }
         //=================================================================
         // Wake up Previous Mon to write, if neccessary
         //=================================================================
         if (MonitorPtr->PrevMon)
            {
            if (MonitorPtr->NextMon == NULL)
               {
               KSEMPostEvent(MonitorPtr->PrevMon->WriteSemHandle);
               }
            else
               if (!(MonitorPtr->NextMon->R0Buff->Buff_Flags&FULL_ARRAY))
                  {
                  KSEMPostEvent(MonitorPtr->PrevMon->WriteSemHandle);
                  }
            }
         else if (MonChainPtr->First_Monitor != NULL)
            {
            // We were first in chain, now someone else is first
            if ((MonChainPtr->FFBuff->InIndex !=
               MonChainPtr->FFBuff->OutIndex) ||
               (MonChainPtr->FFBuff->Buff_Flags&FULL_ARRAY))
               {
               KSEMPostEvent(MonChainPtr->First_Monitor->ReadSemHandle);
               }
            }
         else                       // Chain is empty now
            {
            // This causes data loss, but we loose info in Ring0Buff's too.
            // initialize MonChainPtr->FFBuff info
            MonChainPtr->FFBuff->InIndex = 0;
            MonChainPtr->FFBuff->OutIndex = 0;
            MonChainPtr->FFBuff->Buff_Flags &= !(FULL_ARRAY);
            }
         // enable interrupts
         TKEnable(oldflags);

         //==============================================================
         // Wake up This Mon's read/write threads.  We've already
         // unlinked, so they will return the correct error
         //==============================================================
         KSEMPostEvent(MonitorPtr->ReadSemHandle);
         KSEMPostEvent(MonitorPtr->WriteSemHandle);
         KSEMQuery(MonitorPtr->ReadSemHandle,SSToDS(&(KSEMState)));
         while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
            {
            TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
            KSEMQuery(MonitorPtr->ReadSemHandle,SSToDS(&(KSEMState)));
            }
         KSEMDestroy(MonitorPtr->ReadSemHandle);
         KSEMQuery(MonitorPtr->WriteSemHandle,SSToDS(&(KSEMState)));
         while ((KSEMState.kss_fWaiter !=0) || (KSEMState.kss_fOwned !=0))
            {
            TKSleep(SSToDS(&(KSEMState)),(ULONG)SLEEPYTIME,0L,0L);
            KSEMQuery(MonitorPtr->WriteSemHandle,SSToDS(&(KSEMState)));
            }
         KSEMDestroy(MonitorPtr->WriteSemHandle);
         VMFreeKHB(VM_HKH_PUB_SWAPRW, MonitorPtr->R0Buff->Buff);
         VMFreeKHB(VM_HKH_PUB_SWAPRW, MonitorPtr->R0Buff);
         VMFreeKHB(VM_HKH_PUB_RESRW, MonitorPtr);
         }                             // end while
      //====================================================================
      // Count remaining monitors in this chain
      //====================================================================
      MonitorPtr = MonChainPtr->First_Monitor;
      for (i = 0; (MonitorPtr != NULL); i++)
         {
         MonitorPtr = MonitorPtr->NextMon;
         }                             // endfor
      *(USHORT *)SELVirtToLin((ULONG)(MonitorsLeft.Offst),
                              MonitorsLeft.Segmt) = i;
      returncode = NO_ERROR;
      MonChainPtr->Chain_Flags &= !(DEREG_IN_PROGRESS);
      }
   UnUseChain(MonChainPtr);         // release perchain sem
   return (returncode);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: FindMonFromPid
//
// DESCRIPTIVE NAME: Find monitor based on process ID
//
// ENTRY POINT: FindMonFromPid
//
// INPUT:
//         MonChainPtr     = chain to search
//         Monitor_PID     = process ID to look for
//
//
// EXIT-NORMAL: returns pointer to permonitorinfo
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//
//======================== END OF SPECIFICATIONS =========================== /
pPERMONINFO FindMonFromPid(MonChainPtr,Monitor_PID)
   pPERCHAININFO    MonChainPtr;
   USHORT           Monitor_PID;
{
   pPERMONINFO      tempmon;

   tempmon = MonChainPtr->First_Monitor;
   while (tempmon != NULL)
      {
      if (tempmon->MonPID == Monitor_PID)
         {
         return (tempmon);
         }
      tempmon = tempmon->NextMon;
      }
   return (NULL);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: UseChain
//
// DESCRIPTIVE NAME: Find monitor based on handle, and get exclusive right to
//                   alter chain
//
// ENTRY POINT: UseChain
//
// INPUT:
//         Chain_Handle    = chain handle to look for
//
//
// EXIT-NORMAL: returns pointer to perchaininfo
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//             FindChain
//
// EXTERNAL REFERENCES:
//
//======================== END OF SPECIFICATIONS ===========================
pPERCHAININFO UseChain(Chain_Handle)
   USHORT           Chain_Handle;
{
   pPERCHAININFO    temp,temp1;

   if (NULL != (temp1 = temp = FindChain(Chain_Handle))) /* set temp=temp1 */
      {
      KSEMRequestMutex(temp->ChainSemHandle, INFINITE_TIMEOUT);

      //Must call FindChain again, in case it went away while we blocked.
      //  if it did release the semaphore.

      if((temp1 = FindChain(Chain_Handle)) != temp) /* didn't find it again */
         {
         if (temp1 != NULL)
            {
            panic("UseChain: ChainHandles different"); /* big trouble     */
            }
         KSEMReleaseMutex(temp->ChainSemHandle);
         }                                          /* so release the sem */
      }

   return (temp1);
}

//====================== START OF SPECIFICATIONS ===========================
//
// SUBROUTINE NAME: UnUseChain
//
// DESCRIPTIVE NAME: Give up exclusive right to alter a monitor chain
//
// ENTRY POINT: UnUseChain
//
// INPUT:
//         MonChainPtr     = Monitor chain to stop using
//
//
// EXIT-NORMAL: returns pointer to perchaininfo
//
// EXIT-ERROR:  returns NULL
//
// EFFECTS:
//
// INTERNAL REFERENCES:
//
// EXTERNAL REFERENCES:
//          KSEMReleaseMutex
//
//======================== END OF SPECIFICATIONS ===========================
VOID UnUseChain(MonChainPtr)
   pPERCHAININFO    MonChainPtr;
{

   if (MonChainPtr != NULL)
      {
      KSEMReleaseMutex(MonChainPtr->ChainSemHandle);
      }
   return ;
}

