/*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 = VCDROM.C
 *
 * DESCRIPTIVE NAME = VCDROM Initializations
 *
 * DESCRIPTION This module contains all the VCDROM's initialization
 *             procedures, event handler procedures, and global
 *             as well as VDM instance data declarations.
 *
 *
 * ENTRY POINTS:
 *             VDDInit
 *             VCDROMCreateVDM
 *               InstallStub
 *             VCDROMTerminateVDM
 *             VCDROMPDBDestroy
 *               CloseHandles
 *
 * EXTERNAL REFERENCES:
 *
*/


#include <mvdm.h>
#include <propname.h>
#include "vcdromp.h"
#include "reqhdr.h"
#include "ctrlblk.h"
#include "cdstub.h"



#ifdef  VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**  VCDROM Global Data
*/

#pragma BEGIN_GLOBAL_DATA

#ifdef  VDDSTRICT
CHAR    szAssertMsg[] = "VCDROM assertion failure in %s, line %d\n";
#endif

#ifdef  VDDDEBUG
CHAR    szModule[] = "VCDROM: ";
#endif

BOOL    flSLAVE;
SZ      szRealTime = PROP_NAME_INT_DURING_IO;
SZ      szVPICName = "VPIC";
HVDD    hvddVPIC=NULL;
PFN     pfnSlaveRequest=NULL;

#pragma END_GLOBAL_DATA

#pragma BEGIN_SWAP_DATA

#pragma END_SWAP_DATA

/*
**  VCDROM Instance Data
*/

#pragma BEGIN_SWAP_INSTANCE

HVDM hvdmCurrent=0;                /* instance copy of HVDM                  */

ULONG  slaveparms [10];            /* VDHFSCtl parms to pass to slave thread */

struct                             /* OS/2 generic ioctl code and category   */
CategoryCode
IoctlOutputCodes [] = {
                        0x44, 0x80,         /* eject disk                    */
                        0x46, 0x80,         /* lock/unlock                   */
                        0x40, 0x80,         /* reset                         */
                        0x40, 0x81,         /* audio channel control         */
                        0x4F, 0x80,         /* write drive bytes             */
                        0x45, 0x80          /* close tray                    */
};

struct                             /* OS/2 generic ioctl code and category   */
CategoryCode
IoctlInputCodes [] = {
                        0xFF, 0x80,         /* undefined                     */
                        0x70, 0x80,         /* location of head              */
                        0xFF, 0x80,         /* undefined                     */
                        0xFF, 0x80,         /* undefined                     */
                        0x60, 0x81,         /* audio channel info            */
                        0x6F, 0x80,         /* read drive bytes              */
                        0x60, 0x80,         /* device status                 */
                        0x63, 0x80,         /* return sector size            */
                        0x78, 0x80,         /* return volume size            */
                        0x00, 0x80,         /* ** MEDIA CHANGED exception ** */
                        0x61, 0x81,         /* audio disk info               */
                        0x62, 0x81,         /* audio track info              */
                        0x63, 0x81,         /* q channel info                */
                        0x64, 0x81,         /* sub channel info              */
                        0x79, 0x80,         /* UPC code                      */
                        0x65, 0x81          /* audio status info             */
};

USHORT  NumberOfDrives = 0;
USHORT  FirstDriveNumber = 0;

VPDOSDDTYPE DosDDHeader;                    /* V86FAR address of dos driver  */

HFILE
DriveHandle [MAX_NUMBER_OF_DRIVES];         /* DASD open drive handles       */

UCHAR
CdromCharDriverName[] = CDROM_CHAR_DRIVER_NAME;

UCHAR
DriveString [] = "A:\0B:\0C:\0D:\0E:\0F:\0G:\0H:\0I:\0J:\0K:\0L:\0M:\0"
                 "N:\0O:\0P:\0Q:\0R:\0S:\0T:\0U:\0V:\0W:\0X:\0Y:\0Z:\0";

ULONG       DataArea[4];                    /* for the VDHDevIOCtl call      */
ULONG       ParmList[20];                   /* for VDHDevIOCtl/VDHFSCtl calls*/

ULONG       DataLengthInOut;                /* these are just dummies for    */
ULONG       ParmLengthInOut;                /*  the VDHDevIOCtl call         */

ULONG       ActionTaken;                    /* for VDHOpen                   */
HFILE       FileHandle;                     /* for VDHOpen                   */

#pragma END_SWAP_INSTANCE

/*
**  VCDROM Initialization Data
*/

#pragma BEGIN_INIT_DATA

#pragma END_INIT_DATA


#pragma BEGIN_GLOBAL_CODE

#pragma END_GLOBAL_CODE

/*
**  VCDROM Initialization Code
*/

#pragma BEGIN_INIT_CODE


/****************************************************************************
 *
 * FUNCTION NAME = VDDInit
 *
 * DESCRIPTION   = Virtual CDROM Initialization Entry Point
 *
 *                 VCDROM initialization routine
 *                 See VDD Manager for complete semantics.
 *
 *                 This dynamically exported subroutine is called during
 *                 system initialization.
 *
 *                 CONTEXT:
 *                 Init-time
 *
 *                 PSEUDOCODE:
 *                 define user event hook (VCDROMCreateVDM)
 *                 define user event hook (VCDROMTerminateVDM)
 *                 define user event hook (VCDROMPDBDestroy)
 *
 * INPUT         = psz - pointer to configuration strings (not used)                                                                 NONE
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = return TRUE
 *
 * RETURN-ERROR  = return FALSE
 *
 ****************************************************************************/

BOOL EXPENTRY VDDInit ( PSZ  psz )
{
    /*---------------------------------------------------------------*/
    /*- Register a VDM Creation handler entry point.                -*/
    /*---------------------------------------------------------------*/
    if ((VDHInstallUserHook((ULONG)VDM_CREATE,
                            (PUSERHOOK)VCDROMCreateVDM)) == 0)
        return FALSE;                     /* return FALSE if VDH call failed */

    /*---------------------------------------------------------------*/
    /*- Register a VDM Termination handler entry point.             -*/
    /*---------------------------------------------------------------*/
    if ((VDHInstallUserHook((ULONG)VDM_TERMINATE,
                            (PUSERHOOK)VCDROMTerminateVDM)) == 0)
        return FALSE;                     /* return FALSE if VDH call failed */

    /*---------------------------------------------------------------*/
    /*- Register a VDM PDB destruction handler entry point.         -*/
    /*---------------------------------------------------------------*/
    if ((VDHInstallUserHook((ULONG)VDM_PDB_DESTROY,
                            (PUSERHOOK)VCDROMPDBDestroy)) == 0)
        return FALSE;                     /* return FALSE if VDH call failed */

    return TRUE;                            /* successful installation       */

}


#pragma END_INIT_CODE

#pragma BEGIN_SWAP_CODE


/****************************************************************************
 *
 * FUNCTION NAME = VCDROMCreateVDM
 *
 * DESCRIPTION   = VDM creation notification
 *                 See VDHInstallUserHook for complete semantics.
 *
 *                 This registered subroutine is called each time a new VDM
 *                 is created.  Note that not a lot of initialization should be
 *                 needed (ie, static initialization for most of the instance
 *                 data suffices).
 *
 *                 CONTEXT:
 *                 VDM Task-time
 *
 *                 PSEUDOCODE:
 *                 Get the drive letter information from the physical driver.
 *                 Install the DOS stub driver.
 *                 request VPIC slave thread routine for MSCDEX absolute disc
 *                 reads.  Set flSLAVE flag if INT_DURING_IO property set.
 *
 * INPUT         = hvdm - handle of VDM
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = return TRUE
 *
 * RETURN-ERROR  = return FALSE
 *
 ****************************************************************************/

BOOL HOOKENTRY VCDROMCreateVDM (HVDM hvdm)
{
    register
    ULONG   rc;

    hvdmCurrent = hvdm;

/*  INT_3;  */

    rc = VDHOpen ( CdromCharDriverName,
                   &FileHandle,
                   &ActionTaken,
                   FILESIZE,
                   FILEATTRIBUTE,
                   OPENFLAG,
                   CHAROPENMODE,
                   EABUF );


    if ( rc == VDH_FAILURE )
    {
        rc = VDHGetError ();               /* For debugging and to clear it. */
        return TRUE;                       /* Do not fail install because    */
    }                                      /*  the VDM will crash, but do    */
                                           /*  not register anything.        */

    ParmLengthInOut = PARM_LENGTH_MAX;     /* these are just dummies for     */
    DataLengthInOut = DATA_LENGTH_MAX;     /*  the VDHDevIOCtl call          */

    rc = VDHDevIOCtl ( FileHandle,
                       0x0082,
                       0x0060,             /* return drive letter information*/
                       ParmList,
                       PARM_LENGTH_MAX,
                       &ParmLengthInOut,
                       DataArea,
                       DATA_LENGTH_MAX,
                       &DataLengthInOut );

    if ( rc == VDH_FAILURE )
    {
        rc = VDHGetError ();
    }
    else
    {
        rc = FALSE;
    }
    if ( rc )
    {
        VDHClose ( FileHandle );
        return FALSE;                      /* do not install                 */
    }

    VDHClose ( FileHandle );

    NumberOfDrives = (USHORT) *DataArea;
    FirstDriveNumber = * ( (PUSHORT) DataArea + 1 );

    if ( ! InstallStub () )
        return FALSE;

    /*
    ** if INT_DURING_IO DOS property set, and have VPIC slave routine
    ** then set the flSLAVE flag for dual thread operations.
    */
    if(VDHQueryProperty(szRealTime))
    {
       if(hvddVPIC==NULL)
          hvddVPIC = VDHOpenVDD(szVPICName);
       if((hvddVPIC!=NULL)&&(pfnSlaveRequest==NULL))
           VDHRequestVDD(hvddVPIC,
                         0,
                         VPIC_SLAVE_ADDRESS,
                         NULL,
                         (PVOID)&pfnSlaveRequest);
       if(pfnSlaveRequest!=NULL)
          flSLAVE = TRUE;
    }

    return TRUE;

}


/****************************************************************************
 *
 * FUNCTION NAME = InstallStub
 *
 * DESCRIPTION   = install our DOS Device Driver
 *
 *                 The DOS Device Driver stub serves three purposes:
 *
 *                   To let DOS applications know that CDROM and MSCDEX are
 *                   installed. Most apps call int 2f to determine this, some
 *                   will search for the device driver name.
 *
 *                   To trap int 2fh (ah=15h) calls and relay them to this
 *                   driver.
 *
 *                   To route cdrom device driver requests to this code.
 *
 *                 GLOBAL EFFECTS:
 *                 Memory is allocated from DOS to hold our device driver
 *
 *                 PSEUDOCODE:
 *                 Get some VDM memory for our device driver.
 *                 Copy the driver into VDM space.
 *                 Copy the number of units into the device driver header.
 *                 Install the driver as a DOS device driver.
 *                 Save the DOS drivers address.
 *                 If the installation failed, erase the driver name
 *                     (so that applications that try to identify the
 *                     existence of the CDROM driver by hunting through
 *                     the device driver list will not find a
 *                     half-installed CDROM driver).
 *
 * INPUT         = NONE
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = returns TRUE
 *
 * RETURN-ERROR  = returns FALSE
 *
 ****************************************************************************/

BOOL PRIVENTRY InstallStub (VOID)
{
    register
    PBYTE  dosDDstart;                /* where the Dos Device Driver will go  */
    VPVOID vpBP;                      /* breakpoint address for ARPL          */
    HHOOK  hBPHook;                   /* hook handle                          */

    if ( ! ( dosDDstart = VDHAllocDosMem (CDROMStubLength) ) )
        return FALSE;

    memcpy ( dosDDstart, CDROMStubStart, CDROMStubLength );

    *(dosDDstart + CDROMNumUnits) = (UCHAR) NumberOfDrives; /* patch dd header*/

                                                 /* set arpl to VCDROMDosLink */
    hBPHook = VDHAllocHook (VDH_BP_HOOK,(PFNARM) VCDROMDosLink, 0);
    if (hBPHook == NULL)
      return FALSE;

    vpBP = VDHArmBPHook (hBPHook);                       /* get arpl address  */

    *(PDWORD)(dosDDstart + CDROMPatch0) = (DWORD) vpBP; /* patch device driver*/

    DosDDHeader = (VPDOSDDTYPE) VPFROMP(dosDDstart);

    if ( ! VDHSetDosDevice ( (VPDOSDDTYPE) VPFROMP(dosDDstart) ))
    {
        *(PDWORD) (dosDDstart + CDROMDevName) = 0;    /* kill the device name */
        return FALSE;
    }

    return TRUE;
}


/****************************************************************************
 *
 * FUNCTION NAME = VCDROMTerminateVDM
 *
 * DESCRIPTION   = VDM Destruction
 *
 *                 VDM destruction notification
 *                 See VDHInstallUserHook for complete semantics.
 *
 *                 This registered subroutine is called each time a VDM
 *                 is destroyed.  This handler deallocates the per VDM
 *                 resources used by the VDM being destroyed.
 *
 *                 CONTEXT:
 *                 VDM Task-time
 *
 *                 PSEUDOCODE:
 *                 Close any open drive handles
 *
 * INPUT         = hvdm - handle of VDM
 *
 * OUTPUT        = return TRUE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL HOOKENTRY VCDROMTerminateVDM (HVDM hvdm)
{
    CloseHandles();
    return TRUE;
}


/****************************************************************************
 *
 * FUNCTION NAME = VCDROMPDBDestroy
 *
 * DESCRIPTION   = VDM PDB destruction notification
 *                 See VDHInstallUserHook for complete semantics.
 *
 *                 This registered subroutine is called each time a VDM PDB
 *                 is destroyed.  This handler causes the per VDM drive handles
 *                 to be closed.
 *
 *                 When an application terminates, the PDB is destroyed.  This
 *                 also means (probably) that the CD-ROM disc is being changed.
 *                 The open drive handles need to be closed.  When a new drive
 *                 request occurs, vcdrom will open the drive and mount the new
 *                 disc (or remount the old disc if no disc change occurred).
 *
 *                 CONTEXT:
 *                 VDM Task-time
 *
 *                 PSEUDOCODE:
 *                 Close any open drive handles
 *
 * INPUT         = hvdm - handle of VDM
 *                 vpdb - V86 segment of terminating DOS Program Data Block
 *
 * OUTPUT        = TRUE
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL HOOKENTRY VCDROMPDBDestroy(HVDM hvdm, USHORT vPDB)
{
    CloseHandles();
    return TRUE;
}


/****************************************************************************
 *
 * FUNCTION NAME = CloseHandles
 *
 * DESCRIPTION   = Close any open per-VDM drive handles
 *
 *                 This routine is called to close any open per-VDM drive
 *                 handles. This routine searches through the DriveHandle array
 *                 for any non NULL drive handles.  If one is found, VDHClose
 *                 is called to close the handle and NULL is stored in the
 *                 DriveHandle array.
 *
 *                 CONTEXT:
 *                 VDM Task-time
 *
 *                 PSEUDOCODE:
 *                 Close any open drive handles
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID PRIVENTRY CloseHandles (VOID)
{
    register
    ULONG   n;
    register
    ULONG   last_drive;
    register
    HFILE   handle;

    last_drive = FirstDriveNumber + NumberOfDrives;

    for ( n = FirstDriveNumber; n < last_drive; ++n ) {
        handle = DriveHandle [n];
        if ( handle != NULL ) {
            VDHClose ( handle );
            DriveHandle [n] = NULL;
        }
    }

    return;
}


#pragma END_SWAP_CODE
