/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VTHOOK.C
 *
 * DESCRIPTIVE NAME = Virtual Touch Device Driver Hook Functions.
 *
 *
 * VERSION = V2.0
 *
 * DATE        09/20/91
 *
 * DESCRIPTION
 *             This module contains the VTD's hook functions.
 *
 * FUNCTIONS   VTCreate,         VTDestroy,           VTSetFgnd,
 *             VTSetBgnd,        VTDOSLink,           VTExclusiveTouch,
 *             VTInt7fHook,      VTUserSubIntHook,    VTUserSubReturnHook,
 *             VTInt10Hook.
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#include "vtdef.h"

/*
** Externals
*/

extern  FPFNPDD                 fpfnPTDRequestProc;
extern  TOUDEVICESTATE          touDeviceState;
extern  BOOL                    fTouchEnabled;
extern  HHOOK                   hhookSendEventHook;
extern  HHOOK                   hhookUserSubReturnHook;
extern  HHOOK                   hhookJmpTableHook;
extern  HIRQ                    hirq;
extern  ULONG                   ulSG;
extern  ULONG                   ulSstate;
extern  BOOL                    fSubActive;
extern  PVTINT7FFN              apfnInt7fServices[];
extern  BOOL                    fEventsPending;
extern  PERVDMDATA              VDMDataDefaults;
extern  ULONG                   ulExclusiveSG;
extern  PSZ                     pszExclusiveTouch;
extern  SZ                      szVMBoot;
extern  VPVOID                  vpBP74;
extern  VPVOID                  vpBP7f;

extern  BYTE FARV86             VTouchBPAddress;
extern  BYTE FARV86             VTouchNextInt7f;
extern  BYTE FARV86             VTouchRouterTable;
extern  BYTE FARV86             VTouchRouter;
extern  BYTE FARV86             VTouchRouterBegin;
extern  BYTE FARV86             VTouchRouterEnd;
extern  BYTE FARV86             VTouchInt7fHandler;

/*
** VDM Jump table router defines
*/

#define ROUTERSIZE              (&VTouchRouterEnd    - &VTouchRouterBegin)
#define ROUTERENTRYOFS          (&VTouchRouterEntry  - &VTouchRouterBegin)
#define ROUTERINT7FENTRY        (&VTouchInt7fHandler - &VTouchRouterBegin)
#define ROUTERBPOFS             (&VTouchBPAddress    - &VTouchRouterBegin)
#define ROUTERNEXTINT7FOFS      (&VTouchNextInt7f    - &VTouchRouterBegin)
#define ROUTERTABLEOFS          (&VTouchRouterTable  - &VTouchRouterBegin)

#pragma BEGIN_SWAP_DATA

PVTINT7FFN apfnInt7fServices[] =
{
  vtInt7fReset,                        /* 0                    */
  vtInt7fEmulReset,                    /* 1                    */
  vtInt7fEmulOnOff,                    /* 2                    */
  vtInt7fSetCoOrdinates,               /* 3                    */
  vtInt7fSetOrigin,                    /* 4                    */
  vtInt7fSetThresholds,                /* 5                    */
  vtInt7fSetSelection,                 /* 6                    */
  vtInt7fSetReportRate,                /* 7                    */
  vtInt7fSetBlockMask,                 /* 8                    */
  vtInt7fGetTouchDown,                 /* 9                    */
  vtInt7fGetLiftOff,                   /* 10                   */
  vtInt7fGetSelectOn,                  /* 11                   */
  vtInt7fGetSelectOff,                 /* 12                   */
  vtInt7fGetCurrent,                   /* 13                   */
  vtInt7fSetUserSub,                   /* 14                   */
  vtInt7fSwapUserSub,                  /* 15                   */
  vtInt7fSetFilterType,                /* 16                   */
  vtInt7fQueryStorage,                 /* 17                   */
  vtInt7fSaveState,                    /* 18                   */
  vtInt7fRestoreState,                 /* 19                   */
  vtInt7fGetEmulState,                 /* 20                   */
  vtInt7fGetEmulOnOff,                 /* 21                   */
  vtInt7fGetCoOrdinates,               /* 22                   */
  vtInt7fGetOrigin,                    /* 23                   */
  vtInt7fGetThresholds,                /* 24                   */
  vtInt7fGetSelection,                 /* 25                   */
  vtInt7fGetReportRate,                /* 26                   */
  vtInt7fGetBlockMask,                 /* 27                   */
  vtInt7fGetFilterType,                /* 28                   */
  vtInt7fGetDriverInfo,                /* 29                   */
  vtInt7fGetClickLock,                 /* 30                   */
  vtInt7fSetClickLock,                 /* 31                   */
  vtInt7fDldCalibConsts                /* must always be last  */
};
#pragma END_SWAP_DATA

#pragma BEGIN_SWAP_CODE


/****************************************************************************
 *
 * FUNCTION NAME = VTCreate
 *
 * DESCRIPTION   = Set up at VDM creation time.
 *                 Called each time a new VDM is created
 *   PSEUDO-CODE -
 *      define Int IRQ hook
 *      allocate event hooks, SendEvent, UserSubReturn
 *      allocate BP hook for VDM jump table
 *      allocate VDM memory for Int 7fh router and jump table
 *      patch Int 7fh vector with address of router
 *      copy router into VDM memory
 *      check for exclusive touch advanced property set
 *      if not, tell PDD current emul state
 *
 *
 * INPUT         = hvdm -> new VDM
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE (new VDM accepted)
 * RETURN-ERROR  = FALSE (could not allocate resources)
 *
 ****************************************************************************/

BOOL HOOKENTRY VTCreate( HVDM hvdm )
{
  register PBVDM  DosMem = NULL;
  BOOL            rc = FALSE;
  BOOL            fVMBoot;
  USHORT          Int7fSeg;
  int             i;
  HLOCK           RouterLock;
  PVDM            pVdm;

  if( (*fpfnPTDRequestProc)( PTDREQ_QUERYDEVICESTATE, NULL,
                             F16PFROMP( &touDeviceState ) ) )
  {
    fTouchEnabled = !!( touDeviceState.fTouState & TOUSTATE_CALIBRATED );
    VDHInstallIntHook( hvdm, INTFROMIRQ( touDeviceState.irq ),
                       VTUserSubIntHook, !VDH_ASM_HOOK );
    if( hhookSendEventHook = VDHAllocHook( VDH_CONTEXT_HOOK,
                                           (PFNARM)vtSendEvent,
                                           sizeof(HVDM *) ) )
    {
      *(HVDM *)VDHQueryHookData( hhookSendEventHook ) = hvdm;
      if( hhookUserSubReturnHook = VDHAllocHook( VDH_RETURN_HOOK,
                                                 (PFNARM)VTUserSubReturnHook,
                                                 0 ) )
        if( hhookJmpTableHook = VDHAllocHook( VDH_BP_HOOK,
                                              (PFNARM)VTInt7fhook, 0 ) )
        {
          REFHVDM(hvdm, ULONG, ulSG) = VDHQuerySysValue(hvdm, VDHLSV_SESSIONID);

          /* For VM boot we don't need to allocate memory for INT 7fh      */
          fVMBoot = (BOOL)*(PSZ)VDHQueryProperty( szVMBoot );

          /* Save int 74 ARPL address                                      */
          vpBP74 = VDMBase.rb_avpIVT[INTFROMIRQ( touDeviceState.irq )];
          /* Arm BP Hook, save ARPL addresses                              */
          vpBP7f = VDHArmBPHook( hhookJmpTableHook );

          if( fVMBoot )
          {
            /* Install Int Hook for VDD - DOS communication                */
            /* 60491 VDHInstallIntHook(hvdm, 0x66, VTDOSLink, !VDH_ASM_HOOK); */
            VDHRegisterAPI( VTD_NAME, (PFNHOOK)VTDOSLink, 0 );
            rc = TRUE;
          }
          else if( DosMem = VDHAllocDosMem( ROUTERSIZE ) )
          {
            /* get VDM Segment value                                       */
            Int7fSeg = HISEG( DosMem );

            /* copy the 'router' into allocated VDM space                  */
            VDHCopyMem( &VTouchRouterBegin, DosMem, ROUTERSIZE );

            /* insert jump table BP address                                */
            pVdm = PFROMVADDR( Int7fSeg, ROUTERBPOFS );
            *(VPVOID *)pVdm = vpBP7f;

            /* patch in current IVT entry for INT 7fh for chaining         */
            pVdm = PFROMVADDR( Int7fSeg, ROUTERNEXTINT7FOFS );
            *(VPVOID *)pVdm = VDMBase.rb_avpIVT[TOUCHINT];

            /* replace current IVT entry with our new INT 7fh handler      */
            VDMBase.rb_avpIVT[TOUCHINT] = VPFROMVADDR( Int7fSeg,
                                                       ROUTERINT7FENTRY );

            /* patch the jump table with segment addresses                 */
            pVdm = DosMem + ROUTERTABLEOFS + 2;
            for( i=0; i<(sizeof(apfnInt7fServices)/sizeof(PVTINT7FFN)); i++ )
              *((PUSHORT)&(((DWORD *)pVdm)[i])) = Int7fSeg;

            rc = TRUE;
          }

          if( rc == TRUE )
          {
            /* copy default data to instance data                          */
            VDHCopyMem( &VDMDataDefaults, &VDMData, sizeof(PERVDMDATA) );

            /* look for Properties set                                     */
            if( VDHQueryProperty( pszExclusiveTouch ) )
              VTExclusiveTouch( VDHPROP_SET, hvdm, 0, pszExclusiveTouch );
            else                       /* Inform PDD of emulation state    */
              vtNotifyEmulState( hvdm );

            /* Install Int 10h hook to track video modes (messy)           */
            VDHInstallIntHook(hvdm, 0x10, (PFNHOOK)VTInt10Hook, !VDH_ASM_HOOK);
          }
        }
    }
  }
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTDestroy
 *
 * DESCRIPTION   = VDM termination notification
 *                 This registered subroutine is called when a VDM is
 *                 terminated.
 *   PSEUDO-CODE -
 *      Notify device-dependent driver of exclusive access
 *      state change (ie. disappearing), if necessary.
 *
 *
 * INPUT         = hvdm -> terminating VDM
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL HOOKENTRY VTDestroy( HVDM hvdm )
{
  if( REFHVDM( hvdm, ULONG, ulSstate ) & SESSION_EXCLUSIVE )
    vtNotifyExclusiveState( hvdm, FALSE );
  return( TRUE );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTSetFgnd
 *
 * DESCRIPTION   = VDM foreground (Full-Screen) notification
 *                 This registered subroutine is called each time a VDM is
 *                 switched to Full-Screen.
 *   PSEUDO-CODE -
 *      update session state to foreground
 *      notify device-independent driver of current emulation state
 *
 *
 * INPUT         = hvdm -> new foreground VDM
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL HOOKENTRY VTSetFgnd( HVDM hvdm )
{
  REFHVDM( hvdm, ULONG, ulSstate ) |= SESSION_FOREGROUND;
  vtNotifyEmulState( hvdm );
  return( TRUE );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTSetBgnd
 *
 * DESCRIPTION   = VDM background (Windowed) notification
 *                 This registered subroutine is called each time a VDM is
 *                 switched away from Full-Screen.
 *   PSEUDO-CODE -
 *      update session state
 *
 *
 * INPUT         = hvdm -> new background VDM
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL HOOKENTRY VTSetBgnd( HVDM hvdm )
{
  REFHVDM( hvdm, ULONG, ulSstate ) &= ~SESSION_FOREGROUND;
  return( TRUE );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTDOSLink
 *
 * DESCRIPTION   = Provide an interface for DOS Device driver (INT 66H)
 *                 This function is called by our stub
 *                 (which is sitting in VDM memory).  The register frame
 *                 contains the registers as when invoked by the application.
 *    GLOBAL EFFECTS -
 *          depends on service called
 *    METHOD     -
 *          use VDHPopStack to get return address from client's stack (pcrf)
 *          service = client's AH
 *
 *
 * INPUT         = p     - reserved
 *                 pcrf  - client's register frame.
 *                         Registers contain input parameters for services.
 * OUTPUT        = NONE
 *                 Client's registers contain information, depending
 *                 on which service was called.
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID HOOKENTRY VTDOSLink( PVOID p, PCRF pcrf )
{
  USHORT          uFunction;
  /* 60491 PVOID  pVDDName;      */
  PVOID           pBreakPnt;
  VPVOID          vpretaddr;

/* 60491 pVDDName = PFROMVADDR(DS(pcrf),SI(pcrf));                            */
/* 60491 if((*(PULONG)pVDDName)==0x554f5456) { // Look for the letters VTOU   */
/* 60491   VDHPopInt();                        // prevent return to ROM       */

  if( VDHPopStack( 4, SSToDS( &vpretaddr ) ) )
  {
    IP( pcrf ) = OFFSETOF16( vpretaddr );
    CS( pcrf ) = SEGMENTOF16( vpretaddr );

    switch( uFunction = AH( pcrf ) )
    {
      case 0:                          /* Used for VTOUCH present check    */
        break;
      case 1:                          /* Get Int74 breakpoint address     */
        pBreakPnt = PFROMVADDR(ES(pcrf),DI(pcrf));
                                       /* store breakpoint at ES:DI        */
        *(PULONG)pBreakPnt = (ULONG)vpBP74;
        AX(pcrf) = INTFROMIRQ(touDeviceState.irq);
        break;
      case 2:                          /* Get Int7f breakpoint address     */
        pBreakPnt = PFROMVADDR(ES(pcrf),DI(pcrf));
                                       /* store breakpoint at ES:DI        */
        *(PULONG)pBreakPnt = (ULONG)vpBP7f;
        break;
      default:
        break;
    }
  }
  return;
}


/****************************************************************************
 *
 * FUNCTION NAME = VTExclusiveTouch
 *
 * DESCRIPTION   = Give this VDM Exclusive Touch access
 *                 Called when the user changes the DOS Property
 *                 registered for Exclusive Touch access.
 *   PSEUDO-CODE -
 *      if no screen group has exclusive access,
 *        OR set/clear is by SG that already has excl. access
 *          allow change of state
 *      otherwise
 *          sound warning beep
 *
 *
 * INPUT         = op   - operation to perform
 *                 hvdm - the 'exclusive' VDM
 *                 cb   - size of value passed in
 *                 psz  - interpreted as BOOL
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = no error, always
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER
 *
 ****************************************************************************/

RETCODE EXPENTRY VTExclusiveTouch( ULONG op, HVDM hvdm, ULONG cb, PSZ psz )
{
  Assert( op != VDHPROP_SET );
  if( ( REFHVDM(hvdm, ULONG, ulSstate) & SESSION_EXCLUSIVE ) || !ulExclusiveSG )
  {
    vtNotifyExclusiveState( hvdm, !!psz );
    return( ERROR_NONE );
  }
  else
  {
    VDHDevBeep( PROPBEEP_FREQUENCY, PROPBEEP_DURATION );
    return( ERROR_INVALID_PARAMETER );
  }
}


/****************************************************************************
 *
 * FUNCTION NAME = VTInt7fHook
 *
 * DESCRIPTION   = Int 7fh processing and dispatch
 *      Dispatches Int 7fh requests routed from a VDM by the 'router'
 *      MASM function. This hook was installed as a breakpoint (BP) hook.
 *      We have to set the user's CS:IP to appropriate values, explicitly, to
 *      ensure that when we return to the VDM, execution continues correctly.
 *      The return address is on the user's stack by virtue of the 'router'
 *      making a far call to the BP address. This is 'popped' into the CRF
 *      using VDHPopStack.
 *   PSEUDO-CODE -
 *      Set up VDM return address.
 *      Reroute Interrupt 7FH to appropriate function.
 *      Client-Register Frame has AX = function number
 *                                DS:SI -> Input parms
 *                                ES:DI -> Output parms
 *
 *
 * INPUT         = pRefData -> not used, none allocated
 *                 pcrf     -> VDM register frame (CRF)
 *                   where: AX - function number
 *                          DS:SI -> input parameters
 *                          ES:DI -> output parameters
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID HOOKENTRY VTInt7fHook( PVOID pRefData, register PCRF pcrf )
{
  VDHPopStack( sizeof(WORD), &pcrf->crf_eip );  /* Set up VDM return       */
  VDHPopStack( sizeof(WORD), &pcrf->crf_cs );   /* address.                */
  AX(pcrf) = apfnInt7fServices[AX(pcrf)]( PFROMVADDR( DS(pcrf), SI(pcrf) ),
                                          PFROMVADDR( ES(pcrf), DI(pcrf) ) );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTUserSubIntHook
 *
 * DESCRIPTION   = Simulate a hardware interrupt to a VDM
 *      Intercepts simulated hardware interrupts before they start executing
 *      in VDM space, decides whether it was ours, if so, eats the interrupt,
 *      (there may or may not be a valid interrupt handler installed in the
 *      VDM), EOI's the interrupt, saves the VDM's registers on its own stack,
 *      pushes the VDM's data onto the VDM stack and sets up VDM execution
 *      for a far call to its "user-subroutine", with a return hook.
 *
 *      The "user-subroutine" is defined with PASCAL calling convention
 *      and therefore cleans up it's stack.
 *
 *      The return hook will restore the VDM's registers.
 *
 *   CONTEXT     -
 *      NOTE:
 *         VDHQueryVIRQ() - returns IRQ status as ulong of flag bits:
 *                 VPICQ_REQUEST_PENDING
 *                 VPICQ_IN_SERVICE
 *                 VPICQ_VIRT_MASK
 *                 VPICQ_STAT_IRET_PENDING
 *   PSEUDO-CODE -
 *      check to see if we generated this interrupt;
 *      if not return FALSE;
 *      eat the interrupt to disallow chaining;
 *      clear interrupt;
 *      send virtual EOI;
 *      save all VDM registers;
 *      push event data onto VDM stack;
 *      push user-sub far-call address on the stack;
 *      arm hook to catch the return from user-sub;
 *      return TRUE;
 *
 *
 * INPUT         = pcrf -> VDM register frame
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE  - EMULATED
 * RETURN-ERROR  = FALSE - NOT EMULATED (pass control to next VDD and/or ROM)
 *
 ****************************************************************************/

BOOL HOOKENTRY VTUserSubIntHook( PCRF pcrf )
{
  HVDM hvdm = CURRENT_VDM;
  BOOL rc = FALSE;

  if( VDHQueryVIRQ( hvdm, hirq ) & VPICQ_REQUEST_PENDING )
  {
    VDHPopInt( );
    VDHClearVIRR( hvdm, hirq );
    VDHSendVEOI( hirq );
    VDHPushRegs( VDHREG_ALL );
    VDHPushStack( sizeof(USERSUBSTACK), (PVOID)&VDMData.UserSubStack );
    /* nicky     VDHPushFarCall(VDMData.vpUserSub);                        */
    VDHPushFarCall( VDMData.fpfnUserSub );
    VDHArmReturnHook( hhookUserSubReturnHook, VDHARH_NORMAL_RET );
    rc = TRUE;
  }
  return( rc );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTUserSubReturnHook
 *
 * DESCRIPTION   = User-defined subroutine return processing
 *      Restores the VDM's registers, and returns to the VDM, so that it may
 *      continue normal processing.  We also request another interrupt to be
 *      simulated at this time, if more events are pending.
 *   PSEUDO-CODE -
 *      restore VDM registers saved prior callout
 *      inform system VDM has data to process (VDHWakeIdle())
 *      flag user-subroutine no longer active
 *      re-arm context hook (local) to send another event
 *
 *
 * INPUT         = p      -> undefined (not used)
 *                 pcrf   -> VDM register frame
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

VOID HOOKENTRY VTUserSubReturnHook( PVOID p, PCRF pcrf )
{
  HVDM hvdm = CURRENT_VDM;

  VDHPopRegs( VDHREG_ALL );
  VDHWakeIdle( hvdm );
  fSubActive = FALSE;
  if( REFHVDM( hvdm, BOOL, fEventsPending ) )
    VDHArmContextHook( REFHVDM( hvdm, HHOOK, hhookSendEventHook ), hvdm );
}


/****************************************************************************
 *
 * FUNCTION NAME = VTInt10Hook
 *
 * DESCRIPTION   = Look for video mode changes to track screen size
 *      Not the way we want to do this but there seems to be no other.
 *      So far unable to get the Virtual Video Driver to notify
 *      us on mode changes, in a device-independent way.
 *   PSEUDO-CODE -
 *      if this INT 10H is mode set AH == 0
 *        and we're tracking video modes.
 *          Set XMax, YMax as required.
 *      always return false, to chain to next handler.
 *
 *
 * INPUT         = pRefData -> not used, none allocated
 *                 pcrf     -> VDM register frame (CRF), regs set as for Int 10H
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

BOOL HOOKENTRY VTInt10Hook( register PCRF pcrf )
{
  HVDM hvdm = CURRENT_VDM;

  if( ( AH(pcrf) == 0 ) && VDMData.fTrackVideoMode )

  switch( AL(pcrf) & 0x7f )
  {
    case 0: case 1:                    /* 40 x 25              */
      VDMData.coData.usXMax = 40;
      VDMData.coData.usYMax = 25;
      break;
    case 2: case 3: case 7:            /* 80 x 25              */
      VDMData.coData.usXMax = 80;
      VDMData.coData.usYMax = 25;
      break;
    case 4: case 5: case 9:
    case 0x0D: case 0x13:              /* 320 x 200            */
      VDMData.coData.usXMax = 320;
      VDMData.coData.usYMax = 200;
      break;
    case 6:
    case 0x0A: case 0x0B:
    case 0x0C: case 0x0E:              /* 640 x 200            */
      VDMData.coData.usXMax = 640;
      VDMData.coData.usYMax = 200;
      break;
    case 0x0F: case 0x10:              /* 640 x 350            */
      VDMData.coData.usXMax = 640;
      VDMData.coData.usYMax = 350;
      break;
    case 0x11: case 0x12:              /* 640 x 480            */
      VDMData.coData.usXMax = 640;
      VDMData.coData.usYMax = 480;
      break;
    default:                           /* don't understand     */
      break;                           /* others, so ignore    */
  }

  return( FALSE );
}

#pragma END_SWAP_CODE
