//{ **************************************************************************** }
//{ FileName............: HANDLE.CPP                                             }
//{ Project.............: Generic                                                }
//{ Author(s)...........: M.Majoor                                               }
//{ Original contents...: Walter Oney ('Programming the Windows Driver Model')   }
//{ Version.............: 2.00                                                   }
//{ ---------------------------------------------------------------------------- }
//{ Implementation of handle object.                                             }
//{ Includes a 'single' event handle used by the whole driver. This means only 1 }
//{ user of the driver can use it.                                               }
//{                                                                              }
//{ Version  Date      Comment                                                   }
//{ 1.00     20010810  - Initial release                                         }
//{ 2.00     20060722  - No changes                                              }
//{ **************************************************************************** }


#include "stddcls.h"
#include "driver.h"

//{ **************************************************************************** }
//{ Params   : <pdx>  Device extension                                           }
//{            <fop>  File object                                                }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Close handle.                                                     }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID CloseHandle(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop)
{
  PHANDLE_OBJECT hop = FindHandle(pdx, fop);               // Get the file handle
  if (!hop)                                                // If none exist
    return;
  KIRQL oldirql;
  KeAcquireSpinLock(&pdx->lockHandles, &oldirql);          // Don't let anything interfere
  RemoveEntryList(&hop->link);                             // Remove handle from list
  KeReleaseSpinLock(&pdx->lockHandles, oldirql);

  ReleaseHandle(hop);                                      // Release handle
  ReleaseHandle(hop);                                      // Doing it again does no harm..
}


//{ **************************************************************************** }
//{ Params   : <pdx>  Device extension                                           }
//{            <fop>  File object                                                }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : De-register event.                                                }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID DeregisterEvent(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop)
{
  PHANDLE_OBJECT hop = FindHandle(pdx, fop);               // Get the file handle
  if (!hop)                                                // If none exist
    return;
  if (hop->pevent)                                         // If it has an event handler
  {
    ObDereferenceObject(hop->pevent);                      // De-reference it
    hop->pevent = NULL;                                    // Clear event 'handler'
  }
  ReleaseHandle(hop);
}


//{ **************************************************************************** }
//{ Params   : <pdx>  Device extension                                           }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : De-register the global event.                                     }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID DeregisterEventGlobal(PDEVICE_EXTENSION pdx)
{
  if (pdx->pevent)                                         // If it has an event handler
  {
    ObDereferenceObject(pdx->pevent);                      // De-reference it
    pdx->pevent = NULL;                                    // Clear event 'handler'
  }
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <fop>     File object                                             }
//{ Return   : <Result>  Handle                                                  }
//{                                                                              }
//{ Descript : Find handle.                                                      }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
PHANDLE_OBJECT FindHandle(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop)
{
  // The FsContext and FsContext2	fields of the file object are for use by
  // whoever actually implements the IRP_MJ_CREATE (us, this case). We
  // save our handle object pointer there in OpenHandle
  PHANDLE_OBJECT hop = (PHANDLE_OBJECT) fop->FsContext;
  if (hop)
    ++hop->refcnt;
  return hop;
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <fop>     File object                                             }
//{ Return   : <Result>  Handle                                                  }
//{                                                                              }
//{ Descript : Open handle.                                                      }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
PHANDLE_OBJECT OpenHandle(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop)
{
  // Create space for handle object
  PHANDLE_OBJECT hop = (PHANDLE_OBJECT) ExAllocatePool(NonPagedPool, sizeof(HANDLE_OBJECT));
  if (!hop)                                                // If we could not allocate
    return NULL;
  // Now set all to their initial settings
  hop->refcnt = 1;
  hop->pevent = NULL;
  hop->FileObject = fop;
  fop->FsContext = (PVOID) hop;
  // Add object to list
  ExInterlockedInsertTailList(&pdx->hlist, &hop->link, &pdx->lockHandles);
  return hop;
}


//{ **************************************************************************** }
//{ Params   : <pdx>         Device extension                                    }
//{            <fop>         File object                                         }
//{            <hEvent>      Event                                               }
//{            <AccessMode>  Access mode                                         }
//{ Return   : <Result>      TRUE if success                                     }
//{                                                                              }
//{ Descript : Register event.                                                   }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
BOOLEAN RegisterEvent(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop, HANDLE hEvent, KPROCESSOR_MODE AccessMode)
{
  PHANDLE_OBJECT hop = FindHandle(pdx, fop);               // Get the file handle
  if (!hop)                                                // If none exist
    return FALSE;
  // If the event is already registered, we need to remove it first
  if (hop->pevent)                                         // If event already registered
  {
    ObDereferenceObject(hop->pevent);
    hop->pevent = NULL;
  }

  // Now we can register the event
  NTSTATUS status = ObReferenceObjectByHandle(hEvent, EVENT_MODIFY_STATE, *ExEventObjectType,
                                              AccessMode, (PVOID*) &hop->pevent, NULL);
  ReleaseHandle(hop);
  if (!NT_SUCCESS(status))
  {
    KdPrint((DRIVERNAME "- ObReferenceObjectByHandle failed - %X\n", status));
    return FALSE;
  }
  return TRUE;
}


//{ **************************************************************************** }
//{ Params   : <pdx>         Device extension                                    }
//{            <hEvent>      Event                                               }
//{            <AccessMode>  Access mode                                         }
//{ Return   : <Result>      TRUE if success                                     }
//{                                                                              }
//{ Descript : Register a single, global, event.                                 }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
BOOLEAN RegisterEventGlobal(PDEVICE_EXTENSION pdx, HANDLE hEvent, KPROCESSOR_MODE AccessMode)
{
  // If a global event is already registered, we need to remove it first
  if (pdx->pevent)                                         // If event already registered
  {
    ObDereferenceObject(pdx->pevent);
    pdx->pevent = NULL;
  }

  // Now we can register the global event
  NTSTATUS status = ObReferenceObjectByHandle(hEvent, EVENT_MODIFY_STATE, *ExEventObjectType,
                                              AccessMode, (PVOID*) &pdx->pevent, NULL);
  if (!NT_SUCCESS(status))
  {
    KdPrint((DRIVERNAME "- ObReferenceObjectByHandle failed (global event)- %X\n", status));
    return FALSE;
  }
  return TRUE;
}


//{ **************************************************************************** }
//{ Params   : <hop>  Handle object                                              }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Release handle.                                                   }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID ReleaseHandle(PHANDLE_OBJECT hop)
{
  if (InterlockedDecrement(&hop->refcnt) > 0)              // If there is none
    return;
  if (hop->pevent)                                         // If an event has been regsitered
    ObDereferenceObject(hop->pevent);                      // De-reference it
  ExFreePool(hop);                                         // Release allocated resources
}


//{ **************************************************************************** }
//{ Params   : <pdx>         Device extension                                    }
//{            <fop>         File object                                         }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Signal event and release it.                                      }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID SignalEvent(PDEVICE_EXTENSION pdx, PFILE_OBJECT fop)
{
  PHANDLE_OBJECT hop = FindHandle(pdx, fop);               // Get the file handle
  if (hop && hop->pevent)                                  // If it exists and has an event handler
    KeSetEvent(hop->pevent, 0, FALSE);
  if (hop)                                                 // Release the handle
    ReleaseHandle(hop);
}


//{ **************************************************************************** }
//{ Params   : <pdx>         Device extension                                    }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Signal global event (NO release).                                 }
//{ Notes    : -                                                                 }
//{ **************************************************************************** }
VOID SignalEventGlobal(PDEVICE_EXTENSION pdx)
{
  if (pdx->pevent)                                         // If there is an event handler
    KeSetEvent(pdx->pevent, 0, FALSE);
}
