/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
// initterm.c

// per-process DLL initialization and termination






#define INCL_DOS
#define INCL_ERRORS
#define INCL_PM
#include <os2.h>


// c includes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <builtin.h>


#include "def.h"
#include "driver.h"
#include "funcs.h"
#include "assert.h"



// ------------------------------------------------------------------------------------------------------------------
// _DLL_InitTerm is called once at process init, and once at process terminate
// ulFlag == 0, then do DLL init per process
// ulFlag == 1, then DLL freed per process


ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  int              i;
  APIRET           rc;
  PPIB             pPib;
  PTIB             pTib;
  BOOL             fMustAttach;
  REGREC           regrec;
  ULONG            ulException;
  ULONG            ulrc;


  REGISTERHANDLER( regrec );
  ulException = setjmp( regrec.jmp );
  if( ulException ) {
    // clean up here as required
    // check for the killed-thread case
    switch( ulException ) {
    case XCPT_PROCESS_TERMINATE:
    case XCPT_ASYNC_PROCESS_TERMINATE:
      DosUnsetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD)&regrec );
      DosExit( EXIT_THREAD, 0 );
    }
    // error result
    ulrc = 0;
    goto depart;
  }

#ifdef KDB
  _interrupt(3);
#endif


  switch( ulFlag ) {
  case 0:
    // process is instantiating the driver
    DBPRINTF(( "DLL_InitTerm - instantiating\n" ));

    // get process id and store in process data area
    rc = DosGetInfoBlocks( &pTib, &pPib );
    assert( rc == 0 );
    procdata.pid = pPib->pib_ulpid;


    // SYSTEM-GLOBAL MUTEX SEMAPHORE

    // two chances to open the shared mutex sem;
    // necessary due to possible race condition where two processes
    // could be loading the driver at nearly the same time
    i = 2;
    do {
      // open the shared mutex sem for this process
      rc = DosOpenMutexSem( globals.pszSemName, &procdata.hmtxGlobalSem );
      switch( rc ) {
      case 0:
        // good result; sem  opened
        break;
      case ERROR_SEM_NOT_FOUND:
        // perhaps never been created? quite possible.
        // create a named (therefore shared) system, mutex sem in the unowned state;
        // in a race condition, this could fail, too, because other process could
        // have just created it. In this case, loop and try to open again
        rc = DosCreateMutexSem( globals.pszSemName, &procdata.hmtxGlobalSem, 0, 0 );
        break;
      default:
        // blow up the debug version
        assert( FALSE );
        break;
      }
    } while( rc && --i );
    assert( rc == 0 );


    // GLOBALS INIT STUFF

    // assume that this process must get (attach to) the shared memory pool
    fMustAttach = TRUE;

    // request the global mutex sem
    rc = DosRequestMutexSem( procdata.hmtxGlobalSem, SEM_INDEFINITE_WAIT );
    assert( rc == 0 );
    // if this is the first time, then do globals initialization
    if( !globals.hModule ) {
      // record module handle
      globals.hModule = hThisModule;
      assert( globals.hModule );
      // get module name
      rc = DosQueryModuleName( globals.hModule, sizeof( globals.szModule), globals.szModule );
      assert( rc == 0 );
      // count the number of times init'ed and term'ed; this is the first
      globals.cInstances = 1;
      // alloc a shared heap
      rc = DosAllocSharedMem( &globals.pvSharedHeap, NULL, LEN_SHAREDHEAP, OBJ_GIVEABLE|OBJ_GETTABLE|OBJ_TILE|PAG_WRITE );
      assert( rc == 0 );
      // init shared memory for suballocation
      rc = DosSubSetMem( globals.pvSharedHeap, DOSSUB_INIT | DOSSUB_SPARSE_OBJ, LEN_SHAREDHEAP );
      assert( rc == 0 );
      // this process is now attached; don't do it again
      fMustAttach = FALSE;
    }

    // module handles are the same across all processes in the system
    assert( globals.hModule == hThisModule );

    // create a private process-wide sem for this process in the unowned state
    assert( (HMTX)0 == procdata.hmtxProcessSem  );
    rc = DosCreateMutexSem( NULL, &procdata.hmtxProcessSem, 0, 0 );
    assert( rc == 0 );

    // get addressability to the shared heap for subsequent processes
    if( fMustAttach ) {
      rc = DosGetSharedMem( globals.pvSharedHeap, PAG_WRITE );
      assert( rc == 0 );
      // attach to the suballocation package for this shared memory
      rc = DosSubSetMem( globals.pvSharedHeap, DOSSUB_SPARSE_OBJ , LEN_SHAREDHEAP );
      assert( rc == 0 );
      // add a process
      globals.cInstances ++;
    }


    rc = DosReleaseMutexSem( procdata.hmtxGlobalSem );
    assert( rc == 0 );
    break;

  case 1:
    // process is unlinking the driver; do cleanup
    DBPRINTF(( "DLL_InitTerm - de-instantiating\n" ));

    rc = DosRequestMutexSem( procdata.hmtxGlobalSem, SEM_INDEFINITE_WAIT );
    assert( rc == 0 );
    // decrement the driver instance count
    globals.cInstances--;

    if( globals.cInstances == 0 ) {
      // last process is unlinking the driver
      // this never happens on 2.0SP or 2.1 once a dc is created
      // it can happen at install time
      rc = DosSubUnsetMem( globals.pvSharedHeap );
      assert( rc == 0 );
      rc = DosFreeMem( globals.pvSharedHeap );
      assert( rc == 0 );
      globals.pvSharedHeap = (PVOID)NULL;
      globals.hModule = (HMODULE)0;
    }
    rc = DosReleaseMutexSem( procdata.hmtxGlobalSem );
    assert( rc == 0 );

    rc = DosCloseMutexSem( procdata.hmtxProcessSem );
    assert( rc == 0 );
    procdata.hmtxProcessSem = HMTX_CLOSED;

    rc = DosCloseMutexSem( procdata.hmtxGlobalSem );
    assert( rc == 0 );
    procdata.hmtxGlobalSem = HMTX_CLOSED;

    break;

  default:
    // blow up the debug version
    assert( FALSE );
  }


  // success
  ulrc = 1;


depart:
  UNREGISTERHANDLER( regrec );
  return ulrc;
}
