/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* 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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = MEMORY.C
 *
 * DESCRIPTIVE NAME = Postscript printer driver - memory handling routines
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION:
 *
 * This file contains memory handling routines for the postscript
 * printer driver for Presentation Manager.
 * Global heap is created permanently when the logical device block is filled
 * (in prde_FillLdb).  Once the heap is created here, it is around
 * forever.  This is true since, under OS/2, once a device driver has
 * opened a DC the driver is loaded in memory until the maching is
 * turned off or rebooted.  The global heap can also be created by
 * OS2_PM_DRV_DEVNAMES and OS2_PM_DRV_DEVMODE.  If the logical
 * device block exists at the time either of these routines is
 * called, no new heap is created.  These two routines will just
 * use the global heap created by prde_FillLdb.  If, however, the
 * logical device block does not exist at the time either of
 * these routines is called, they will create and initialize the
 * global heap, do what they have to do, then destroy the global
 * heap.
 *
 *
 * FUNCTIONS :
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include <stdio.h>
#include <string.h>
#include <builtin.h>
#include "inc\prdinclt.h"
#undef   INCL_WIN
#define  INCL_SHLERRORS
#define  INCL_WINERRORS
#define  INCL_WINHEAP
#define  INCL_DOSFILEMGR
#include <pmwin.h>
#include "inc\prdgcone.h"
#include "inc\utl.h"
#include "inc\config.h"

#define CB_GLOBAL_HEAP  16384
#define CB_64K_HEAP     65536
#define CB_4K           4096
#define CB_1K           1024
#define CB_PROCESS_HEAPSIZE  CB_1K *  64
#define CB_DC_HEAPSIZE       CB_1K * 128
#define PROCESSMEM 1
#define DCMEM      2
#define DOSMEM     3

#include "inc\pspagtun.h"             /* V2.174057  Page Tuning */
#define INCL_GENPLIB_MEMORY
#define INCL_GENPLIB_HANDLER
#include <genplib.h>



extern HMTX tsemDriver;

/*
** here's a convenient place to store some GLOBAL variables.
*/
/*
**  GLOBAL DATA - initialize once
*/
#pragma data_seg( global )

ULONG ULGreVersion;   /*            */
GLOBALDATA globals = { 0, NULL, NULL, { 0, NULL, NULL } , 0 }; /*            */
/*
** table of pointers to resource strings.
*/
BOOL fStringsLoaded = 0;
PBYTE StringTable[MAX_STRINGS];


/*
** This is PER INSTANCE data - new copy per process
*/
#pragma data_seg( perprocess )

/*
** a flag which is set to TRUE only after the logical device block has
** been established.
*/
BOOL    fLogicalDeviceBlock;


PVOID pProcessHeap = NULL;
HMODULE pscript_module;   /* This is the module handle for this driver */

/*
**           
** Structure used for page tuning.
*/
#ifdef PAGE_TUNE
  TUNERPROCDATA TunerProcData;
#endif

#pragma data_seg( DATA32 )

/*
===============================================================================
*/


/***************************************************************************
 *
 * FUNCTION NAME =  CreateProcessHeap( )
 *
 * DESCRIPTION   =  Allocates a object,  then creates the driver's
 *                  process heap in that segment.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL   CreateProcessHeap()
{
  /*
  ** create the heap.  if it already exists, don't do it again.
  */
  if ( pProcessHeap )
     return (TRUE);

  if ( pProcessHeap = (PVOID) GplMemoryCreateInstance( CB_PROCESS_HEAPSIZE,
                                     CB_PROCESS_HEAPSIZE, 0, PROCESS_MEMORY ) )
    return TRUE;
  else
    return FALSE;

}

/***************************************************************************
 *
 * FUNCTION NAME =  CreateDCHeap( )
 *
 * DESCRIPTION   =  Allocates a object,  then creates the driver's
 *                  process heap in that segment.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

PDV CreateDCHeap( )
{
  HMCB  hMCB;
  PDV   pdv;

  if ( !( hMCB = GplMemoryCreateInstance( CB_DC_HEAPSIZE, CB_DC_HEAPSIZE, 0,
                                                           PROCESS_MEMORY ) ) )
    return NULL;

  /*
  ** Allocate the DV from the heap
  */
  if ( !( pdv = (PDV)GplMemoryAlloc( hMCB, (ULONG)sizeof(DV) ) ) )
    return (NULL);

  pdv->pDCHeap = (PVOID)hMCB;

  return (pdv);

}


/***************************************************************************
 *
 * FUNCTION NAME =  KillHeap
 *
 * DESCRIPTION   =  Deletes the driver's heap
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 ****************************************************************************/

VOID   KillHeap( PVOID pHeap )
{
  GplMemoryDeleteInstance( (HMCB)pHeap );
  return ;
}


/***************************************************************************
 *
 * FUNCTION NAME =  LoadStringTable
 *
 * DESCRIPTION   =  Loads a string table from the resource file into a
 *                  global data structure.
 *
 * INPUT         = hHeap
 *                 Sel
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 ****************************************************************************/

BOOL LoadStringTable(PVOID pHeap)
{
  PBYTE pb;
  CHAR szBuf[MAX_COMMENT];
  register s,i;

  /*
  ** load a pointer for each string in the resource into
  ** StringTable.
  */
  for (i = 0; i < ( IDS_EndString - STRING_BASE ); ++i)
  {
    if (!(s = WinLoadString((HAB)NULL, pscript_module, (SHORT)(STRING_BASE+i),
                                                     MAX_COMMENT, (PSZ)szBuf)))
    {
      RIP("LoadStringTable: Can't load strings resource");
      return (FALSE);
    }
    s++;                     /* make room for the NULL terminator.          */

    /*
    ** get a block of memory for the string, put a pointer to it
    ** in the table, and copy the string into the newly allocated memory.
    */
    pb = GplMemoryAlloc( (HMCB)pHeap, (ULONG)s );

    StringTable[i] = pb;
    szNewCopy(pb, szBuf, s);
  }

  fStringsLoaded = TRUE;

  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME =  InitGlobalHeap
 *
 * DESCRIPTION   =  Creates the driver's heap, gets and stores the
 *                  driver's module handle, then loads the string
 *                  table from the resources.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 ****************************************************************************/

BOOL   InitGlobalHeap()
{
  ULONG ulBootDrive;

  if ( !( globals.hMCB = (PVOID)GplMemoryCreateInstance( CB_GLOBAL_HEAP,
                                        CB_GLOBAL_HEAP, 0, SHARED_MEMORY ) ) )
    return FALSE;

  /*
  ** now that we have the driver's module handle, let's install
  ** our resource strings table.
  */
  if ( ! fStringsLoaded )
  {
    if ( ! LoadStringTable( globals.hMCB ) )
    {
      return (FALSE);
    }
  }

  /*
  ** Set up the boot drive in global space
  */
  if ( ! globals.BootDrive )
  {
    DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                     sizeof(ULONG) );
    globals.BootDrive =  ulBootDrive - 1 + 'A';
  }


  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME =  KillGlobalHeap
 *
 * DESCRIPTION   =  Deletes the driver's heap, resets some global
 *                  heap variables.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 ****************************************************************************/

VOID   KillGlobalHeap()
{
  INT i;

  /*
  ** this routine is called by devquerydevicenames and devpostdevicemodes.
  ** we don't want to delete the global heap if the logical device block
  ** exists, because it too will be using the heap.
  */

  for (i = 0; i < ( IDS_EndString - STRING_BASE ); ++i)
  {
    GplMemoryFree( StringTable[i] );
  }

  /*
  ** delete the driver's heap.
  */

  GplMemoryDeleteInstance( globals.hMCB );

  /*
  ** reset heap information.
  */
  globals.hMCB = NULL;
  globals.FontData.lCount = 0;
  globals.FontData.pszFontFiles = NULL;
  fStringsLoaded = FALSE;
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME =  GetR3String
 *
 * DESCRIPTION   =  This routine takes a string id and returns the
 *                  pointer to that given string to the caller. It
 *                  is called by ring 2 routines which need to get
 *                  at these ring 3 strings in StringTable.
 *
 * INPUT         = usStringId
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

PBYTE   GetR3String(SHORT usStringID)
{
  PBYTE pb;

    /* get the pointer to the specified string and return it.*/
  pb = StringTable[usStringID-STRING_BASE];
  return (pb);
}


/***************************************************************************
 *
 * FUNCTION NAME =  SetLDBFlag
 *
 * DESCRIPTION   =  Sets the fLogicalDeviceBlock to TRUE; meaning that
 *                  the logical device block now exists.
 *
 * INPUT         = NONE
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 ****************************************************************************/

VOID   SetLDBFlag()
{
  fLogicalDeviceBlock = TRUE;
  return ;
}

/*
**           
*/
/*****************************************************************************\
**
** ExceptHandler
** -------------
**
** This is the handler for exception management
**
\*****************************************************************************/

ULONG APIENTRY ExceptHandler( PEXCEPTIONREPORTRECORD       p1,
                              PEXCEPTIONREGISTRATIONRECORD p2,
                              PCONTEXTRECORD               p3,
                              PVOID                        pDummy      )
{
  switch( p1->ExceptionNum )
  {
    case XCPT_ACCESS_VIOLATION:
      longjmp( ((PREGREC)p2)->jmp,  p1->ExceptionNum );
      break;

    case XCPT_BREAKPOINT:
    // intel chip won't restart an int 3 -- just goes on to the next instruction
      return XCPT_CONTINUE_EXECUTION;
  }
  return XCPT_CONTINUE_SEARCH;
}


/*
**           
** Added code for global heap
*/
/***************************************************************************
 *
 * FUNCTION NAME = _DLL_InitTerm
 *
 * DESCRIPTION   = Called when proces loads or uloads driver dll
 *                 See LIBRARY statement in .def file
 *
 * INPUT         = ULONG hModule = Module handle
 *                 ULONG ulFlag  = Action init or term
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = (SUCCESS) 1L
 *
 * RETURN-ERROR  = (FAILURE) 0
 *
 **************************************************************************/

ULONG _System _DLL_InitTerm( ULONG hModule,
                             ULONG ulFlag )
{
  INT i;
  APIRET rc;
  REGREC Regrec;
  ULONG  ulException;

  Regrec.pfn = (PFNEH)ExceptHandler;
  DosSetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD) &Regrec );
  ulException = setjmp( Regrec.jmp );
  if( ulException )
  {
    DosReleaseMutexSem( globals.hSem );
    goto done;
  }

  switch( ulFlag )
  {
    case 0: /* init */

      /*
      ** Open or create the global semaphore if needed
      */
      i = 2;
      do
      {
        rc = DosOpenMutexSem( NULL, &globals.hSem );

        if ( rc )
        {
          rc = DosCreateMutexSem( NULL, &globals.hSem, DC_SEM_SHARED, FALSE );
        }
        else
        {
          break;
        }
      } while ( rc && --i );

      /* Get the sem */

      DosRequestMutexSem( globals.hSem, SEM_INDEFINITE_WAIT );

      globals.cInstances++;

      pscript_module = hModule;

      if ( globals.cInstances == 1 )  /* For the first time */
      {
        if ( InitGlobalHeap() == FALSE )
        {
          DosReleaseMutexSem( globals.hSem );
          return 0;
        }
      }

      DosReleaseMutexSem( globals.hSem );

      if ( CreateProcessHeap() == FALSE )   /* Create the per process heap */
      {
        return 0;
      }

      break;

    case 1: /* term */

      KillHeap( pProcessHeap );
      pProcessHeap = NULL;

      DosRequestMutexSem( globals.hSem, SEM_INDEFINITE_WAIT );

      globals.cInstances--;

      if ( ! globals.cInstances ) /* If unloading */
      {
        KillGlobalHeap();
      }

      DosReleaseMutexSem( globals.hSem );
      DosCloseMutexSem( globals.hSem );

      if ( ! globals.cInstances ) /* If unloading */
      {
        globals.hSem = NULL;
      }

      break;

    default:
      break;
  }
done:
  DosUnsetExceptionHandler( (PEXCEPTIONREGISTRATIONRECORD)&Regrec );
  return 1L;
}
