/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Lexmark 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 = PRDPWRIT
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   prdn_PrtWrite
 *             prdn_OpenPrinter
 *             prdn_ClosePrinter
 *             prdn_our_PrtAbort
 *             prdn_CloseDevice
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_32                        /* CON3201 */
#define INCL_DOSPROCESS                /* CON3201 */
#define INCL_DOSSEMAPHORES
#define INCL_GPIPRIMITIVES
#define INCL_WINHEAP
#define INCL_GPIBITMAPS
#define INCL_DOSFILEMGR
#define INCL_SPL
#define INCL_GPIERRORS
#define INCL_DOSMODULEMGR
#define INCL_DOSERRORS
#include <os2.h>
#undef INCL_DOSPROCESS
#undef INCL_DOSSEMAPHORES
#undef INCL_GPIPRIMITIVES
#undef INCL_WINHEAP
#undef INCL_GPIBITMAPS
#undef INCL_DOSFILEMGR
#undef INCL_SPL
#undef INCL_GPIERRORS
#undef INCL_DOSMODULEMGR
#undef INCL_DOSERRORS

#define INCL_DDIBUNDLES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIDEFS
#define INCL_GRE_JOURNALING
#include <pmddi.h>
#undef INCL_DDIBUNDLES
#undef INCL_DDIFONTSTRUCS
#undef INCL_DDIDEFS
#undef INCL_GRE_JOURNALING

#define INCL_WINP_SELSERVER
#define INCL_WINP_SELECTIVE
#define INCL_WINP_SEI
#include <pmwinx.h>
#undef INCL_WINP_SELSERVER
#undef INCL_WINP_SELECTIVE
#undef INCL_WINP_SEI
#undef INCL_32                         /* CON3201 */

#include <prdccone.h>
#include <prdconse.h>
#include <prdpcone.h>

#define NO_SYS
#define PRDMTYPE_INCL
#define NO_CONSTANT_INCL
#include <prdinclt.h>
#undef NO_CONSTANT_INCL
#undef PRDMTYPE_INCL
#undef NO_SYS

#include <prdaextf.h>
#include <prdeextf.h>
#include <prdjextf.h>
#include <prdgextf.h>
#include <prdpextf.h>
#include <prdyextf.h>
#include <prdnextf.h>
#include <prdncone.h>

/******************************************************************************/
/*  FUNCTION: prdn_PrtWrite                                                   */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  lpDCI   DCIData  DC instance pointer                                      */
/*  PBYTE   Data     data buffer pointer                                      */
/*  USHORT  Count    number of bytes to be written                            */
/*  lpPDBI  PDBInst  PDB instance data pointer                                */
/*  USHORT  Written  number of bytes written                                  */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Function performing a PrtWrite to the printer with appropriate error      */
/*  checking and retry parameters:                                            */
/*                                                                            */
/*  Note: PrtAbort not issued for AbortDoc, unless a PrtWrite/Close fails.    */
/******************************************************************************/
/*CON3201
USHORT pascal prdn_PrtWrite(DCIData, Data, Count, PDBInst, Written)

lpDCI   DCIData;
PBYTE   Data;
USHORT  Count;
lpPDBI  PDBInst;
USHORT  Written;
               */
SHORT prdn_PrtWrite(lpDCI   DCIData,  /* CON3201 make return value short */
                     PBYTE   Data,    /* since return may be negative    */
                     ULONG   Count,
                     lpPDBI  PDBInst,
                     ULONG   Written)

{

    BOOL    HadAbortDoc;
    USHORT  Result;
    USHORT  DlgReturn;
    PBYTE   pSource = Data;

#ifdef DEBUG_FSM

    OutputPair("FSM State.. : ", (ULONG)DCIData->State, HEX );
    OutputPrompt("..Entering prdn_PrtWrite");

#endif

    /**************************************************************************/
    /*  If AbortDoc issued, then do not try to write, just do abort.  This is */
    /*  not in our old code, but without this, an ap could issue an AbortDoc, */
    /*  and the driver would PrtWrite a block (successfully?) to the printer. */
    /**************************************************************************/
    if (DCIData->Flags & ABORTED)
    {

        /**********************************************************************/
        /*  Write zeroes, SIC, then tidy up.                                  */
        /**********************************************************************/
        prdn_write_abort_tidy_up(DCIData);
        DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

        OutputString("prdn_PrtWrite returns ERR_ABORT, WAIT_START");

#endif

        return(ERR_ABORT);

    }
    do
    {

#ifdef DEBUG_FSM

        OutputString("Call PrtWrite");

#endif

        Result = PrtWrite(PDBInst->PDBHandle, pSource, Count,
                          (PULONG)&Written);

#ifdef DEBUG_FSM

        OutputPair("PrtWrite returns ", (ULONG)Result, HEX);
        OutputPair("Count - Written = ", (ULONG)(Count-Written), HEX);

#endif

        if (Result || (Written == 0))  /* PrtWrite failed.                    */
        {
            /******************************************************************/
            /*  Prompt for retry/cancel                                       */
            /******************************************************************/
            DlgReturn = prdp_PrintError(Result, PDBInst);
            if (DlgReturn == PRINT_CANCEL)
            {

                /**************************************************************/
                /*  Abort the printer, then do AbortTidyUp to free band, reset*/
                /*  flag, close printer.  Save the abort state, which is reset*/
                /*  in TidyUp.                                                */
                /**************************************************************/
                HadAbortDoc = (BOOL)(DCIData->Flags & ABORTED);
                prdn_our_PrtAbort(DCIData);
                prdn_AbortTidyUp(DCIData);
                if (HadAbortDoc)
                {

                    /**********************************************************/
                    /*  Goto WAIT_START state, as ap has issued an AbortDoc,  */
                    /*  and can now start a new document.                     */
                    /**********************************************************/
                    DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

                    OutputString("prdn_PrtWrite returns ERR_ABORT,WAIT_START");

#endif

                    return(ERR_ABORT);
                }
                else
                {

                    /**********************************************************/
                    /*  Goto PURGE state, in which calls return OK, as the ap */
                    /*  knows nothing of the purge.                           */
                    /**********************************************************/
                    DCIData->State = PURGE;

#ifdef DEBUG_FSM

                    OutputString("prdn_PrtWrite returns ERR_PURGE,PURGE");

#endif

                    return(ERR_PURGE);
                }
            }
            else
            {

                /**************************************************************/
                /*  RETRY selected.  May have received AbortDoc though.       */
                /**************************************************************/
                if (DCIData->Flags & ABORTED)
                {
                    /**********************************************************/
                    /*  Since the user has selected RETRY (presumably having  */
                    /*  ensured the printer is online etc), we try to do the  */
                    /*  tidy up (writing zeroes, SIC to the printer) before   */
                    /*  AbortTidyUp.  Thus this call could take a long time if*/
                    /*  the PrtWrite fails in write_abort_tidy_up.  But if    */
                    /*  don't do this then there is no point in putting the   */
                    /*  RETRY/CANCEL box up in the first place.               */
                    /**********************************************************/
                    prdn_write_abort_tidy_up(DCIData);
                    DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

                    OutputString("prdn_PrtWrite returns ERR_ABORT,WAIT_START");

#endif

                    return(ERR_ABORT);
                }

            }
        }
        else
        {
            if (DCIData->Flags & ABORTED)
            {

                /**************************************************************/
                /*  Write zeroes & SIC, then PrtClose, FreeBand, etc.         */
                /**************************************************************/
                prdn_write_abort_tidy_up(DCIData);
                DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

                OutputString("prdn_PrtWrite returns ERR_ABORT,WAIT_START");

#endif

                return(ERR_ABORT);
            }
        }

        /**********************************************************************/
        /*  If we've got here, then no abort, and the PrtWrite was OK, or user*/
        /*  has hit Retry.                                                    */
        /*                                                                    */
        /*  PD00074 : adjust the data pointer and count by the number of bytes*/
        /*  succesfully written.                                              */
        /**********************************************************************/
        pSource += Written;
        Count   -= Written;

    } while (Count);

#ifdef DEBUG_FSM

    OutputPair("FSM State.. : ", (ULONG)DCIData->State, HEX );
    OutputString("prdn_PrtWrite returns OK");

#endif

    return(OK);

}



/******************************************************************************/
/*  FUNCTION: prdn_OpenPrinter                                                */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  lpDCI  DCIData                                                            */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Issues PrtOpen, returning ERR_PURGE, ERR_ABORT, OK.                       */
/******************************************************************************/
SHORT prdn_OpenPrinter(lpDCI DCIData) /* CON3201 make return value short      */
                                      /* negative might be returned           */
{
    ULONG     DosAction;               /* Action taken by PrtOpen             */
    USHORT    Result;                  /* Function return code                */
    USHORT    OpenFlag;                /* Parameter passed to PrtOpen         */
    lpPDBI    PDBIData;                /* Pointer to PDB Data                 */
    PBYTE     PrintName;               /* Printer device name (for call to    */
                                       /* PrtOpen)                            */

    PDBIData    = DCIData->DCIPdbInstance;

#ifdef DEBUG_FSM

    OutputString("Enter prdn_OpenPrinter");

#endif

//#ifdef PORT_SEMAPHORE
//
//      /**************************************************************/
//      /* if we are not using semaphores then the PDBDevice structure*/
//      /* does not exist, so we cant use it to determine if we are   */
//      /* opening to a port or not                                   */
//      /* Therefore assume that the value we normally use for a      */
//      /* file is also OK for a port                                 */
//      /**************************************************************/
//      if ( PDBIData->PDBDevice->DeviceName != FNULL )  /* i.e. port */
//      {
//          OpenFlag = 0x12;
//      }
//      else
//      {
//          OpenFlag = 0x11;
//      }
//
//#endif  /* ifdef PORT_SEMAPHORE */

    /**************************************************************************/
    /*  CODE1 : commented out the port semaphore code above.                  */
    /**************************************************************************/
    OpenFlag = 0x11;

    /**************************************************************************/
    /*  Get the string with the name of the Printer from the PDB Instance     */
    /*  Data.                                                                 */
    /**************************************************************************/
    PrintName = PDBIData->PDBLogAddress;

    /**************************************************************************/
    /*  If AbortDoc issued, then do not try to open printer, just abort This  */
    /*  is not in our old code, and may be incorrect - perhaps we should wait */
    /*  till after the PrtOpen.                                               */
    /**************************************************************************/
    if (DCIData->Flags & ABORTED)
    {

#ifdef DEBUG_FSM

        OutputString("Aborted flag set");

#endif

        /**********************************************************************/
        /*  Free the band and reset the Aborted flag.                         */
        /**********************************************************************/
        prdn_AbortTidyUp(DCIData);
        DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

        OutputString("State = WAIT_START: return ERR_ABORT");

#endif

        return(ERR_ABORT);
    }

    /**************************************************************************/
    /*  PD00614 : If the printer is already opened, don't open it!            */
    /**************************************************************************/
    if (!(DCIData->Flags & PRINTER_OPEN))
    {
        while ((Result = PrtOpen(PrintName,
                                 (PULONG)&(PDBIData->PDBHandle),
                                 &DosAction, 0L, 0, OpenFlag, 0x2042,
                                 0L)) != DOS_OK)
        {

            /******************************************************************/
            /*  PrtOpen failed: put up retry box.                             */
            /******************************************************************/

    #ifdef DEBUG_FSM

            OutputString("PrtOpen failed");

    #endif

            if (prdp_PrintError(Result, PDBIData) != PRINT_RETRY)
            {

                /**************************************************************/
                /*  User selected cancel: go to PURGE state, to throw         */
                /*  everything away until a StartDoc etc.                     */
                /**************************************************************/
                prdn_AbortTidyUp(DCIData);
                DCIData->State = PURGE;

    #ifdef DEBUG_FSM

                OutputString("User Purge: State = PURGE");

    #endif

                return(ERR_PURGE);
            }
            if (DCIData->Flags & ABORTED)
            {

                /**************************************************************/
                /*  Aborted, so don't worry about Prt failing.  Printer is not*/
                /*  open, so we just do abort tidying.                        */
                /**************************************************************/
                prdn_AbortTidyUp(DCIData);

                /**************************************************************/
                /*  Goto WAIT_START                                           */
                /**************************************************************/
                DCIData->State = WAIT_START;

    #ifdef DEBUG_FSM

                OutputString("Aborted: State = WAIT_START");

    #endif

                return(ERR_ABORT);
            }

        }
    }

    /**************************************************************************/
    /*  Here, printer successfully opened.                                    */
    /**************************************************************************/
    DCIData->Flags |= PRINTER_OPEN;
    if (DCIData->Flags & ABORTED)
    {

        /**********************************************************************/
        /*  PrtOpen succeeded, but AbortDoc issued.                           */
        /**********************************************************************/
        prdn_our_PrtAbort(DCIData);    /* do PrtAbort for completeness        */
        prdn_AbortTidyUp(DCIData);     /* will do PrtClose                    */
        DCIData->State = WAIT_START;

#ifdef DEBUG_FSM

        OutputString("Aborted: State = WAIT_START");

#endif

        return(ERR_ABORT);
    }

    /**************************************************************************/
    /*  If the logical device is a COM port then initialization must be done. */
    /**************************************************************************/
    if (((PrintName[0] == 'C') || (PrintName[0] == 'c')) &&
        ((PrintName[1] == 'O') || (PrintName[1] == 'o')) &&
        ((PrintName[2] == 'M') || (PrintName[2] == 'm')))
    {

        /**********************************************************************/
        /*  A return of error zero means that we are out of memory so return  */
        /*  an error.  Otherwise, it just means that the port does not exist  */
        /*  and the prtopen will redirect output to the file with the same    */
        /*  name as the port name.                                            */
        /**********************************************************************/
        if (prdp_InitialiseCOMPort(PrintName,
                                   PDBIData->PDBHandle) == ERROR_ZERO)
        {
            return(ERROR_NEG);
        }
    }
    return(OK);
}

/******************************************************************************/
/*  FUNCTION: ClosePrinter                                                    */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  lpDCI  DCIData                                                            */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Issue PrtClose.                                                           */
/******************************************************************************/
SHORT prdn_ClosePrinter(lpDCI DCIData) /* CON3201 changes return value to     */
                                       /* in case of negative value           */
{
    lpPDBI  PDBInst;                   /* Pointer to the PDB Instance         */
    USHORT  Result;

#ifdef DEBUG_FSM

    OutputString("Enter prdn_ClosePrinter");

#endif

    PDBInst = DCIData->DCIPdbInstance;

    /**************************************************************************/
    /*  PD00495: Do not close the printer if it is not already open.          */
    /*  Attempting this will cause a sharing violation and will cause the user*/
    /*  to get caught in a repeated error situation.                          */
    /**************************************************************************/
    if (DCIData->Flags & PRINTER_OPEN)
    {
        while ((Result = PrtClose(PDBInst->PDBHandle)) &&
               (Result != ERROR_INVALID_HANDLE))
        {

#ifdef DEBUG_FSM

            OutputString("prompt for retry of PrtClose");

#endif

            /******************************************************************/
            /*  Error occurred: prompt for retry                              */
            /******************************************************************/
            if (prdp_PrintError(Result, PDBInst) == PRINT_CANCEL)
            {

                /**************************************************************/
                /*  Selected cancel: issue PrtAbort to kill buffer.  PrtClose */
                /*  should now work.                                          */
                /**************************************************************/
                prdn_our_PrtAbort(DCIData);
            }
        }

#ifdef PORT_SEMAPHORE

        /**********************************************************************/
        /*  Free the Printer semaphore.  FSRSems cannot be cleared by a thread*/
        /*  other than that which requested it.  This may be needed here so   */
        /*  get around it by putting the current thread id into the semaphore */
        /*  tid field.  The engine guarantees that the pid will be the current*/
        /*  pid.                                                              */
        /**********************************************************************/
        PDBInst->PDBDevice->ControlSemaphore->ThrdID = DCIData->
                                                       LockDCInfo.tidLock;
        (VOID)prdg_ClearSemaphore(PDBInst->PDBDevice->ControlSemaphore);

#endif

        DCIData->Flags &= ~PRINTER_OPEN;

#ifdef DEBUG_FSM

        OutputString("Exit prdn_ClosePrinter (OK)");

#endif

    }
    return(OK);
}

/******************************************************************************/
/*  FUNCTION: prdn_our_PrtAbort                                               */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  lpDCI  DCIData                                                            */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Calls PrtAbort if it exists for the spooler version being run.            */
/******************************************************************************/
VOID prdn_our_PrtAbort(lpDCI DCIData)

{

    HMODULE  AbortModuleHandle;
    PFN      pPrtAbort;

#ifdef DEBUG_FSM

    OutputString("Enter prdn_our_PrtAbort");

#endif

#if 0
    /**************************************************************************/
    /*  At this point we know that the driver wants to issue a PrtAbort to    */
    /*  abort the job that has been opened on the printer.  However the       */
    /*  PrtAbort call was not available at release 1.1.  Therefore, we do not */
    /*  make the call directly since this prevents us from building the same  */
    /*  source at both 1.1 , 1.2.  Instead we get the address of the PrtAbort */
    /*  routine using DosLoadModule and DosGetProcAddress.  If these fail to  */
    /*  get us the address of the PrtAbort we cannot then make the call.      */
    /**************************************************************************/
    if (DOS_OK == DosLoadModule(FNULL, 0, "PMSPL", &AbortModuleHandle))
    {

        /**********************************************************************/
        /*  Now try and get the address of PrtAbort..                         */
        /**********************************************************************/
        if (DOS_OK == DosGetProcAddr(AbortModuleHandle, "PRTABORT", &pPrtAbort))
        {

            /******************************************************************/
            /*  Can now make the call                                         */
            /******************************************************************/
            pPrtAbort(DCIData->DCIPdbInstance->PDBHandle);
        }
        DosFreeModule(AbortModuleHandle);
    }
/* CON3201 End of if'd out code */
#endif
/* CON3201 */
/* The above reasoning is no longer valid since we will be running on 2.0+    */
/* so we can just issue the PrtAbort                                          */
    PrtAbort(DCIData->DCIPdbInstance->PDBHandle);

#ifdef DEBUG_FSM

    OutputString("Exit prdn_our_PrtAbort");

#endif

}

/******************************************************************************/
/*  FUNCTION: prdn_CloseDevice                                                */
/*                                                                            */
/*  PARAMETERS:                                                               */
/*                                                                            */
/*  lpDCI  DCIData                                                            */
/*                                                                            */
/*  DESCRIPTION:                                                              */
/*                                                                            */
/*  Close printer or spooler file.                                            */
/******************************************************************************/
SHORT prdn_CloseDevice(lpDCI DCIData) /* CON3201 change return value to SHORT */

{

/*  USHORT  us;    CON3201 */
    ULONG   ul;

    if (DCIData->Flags & DIRECT)
    {
        prdn_ClosePrinter(DCIData);
    }
    else
    {
        if (DCIData->Flags & QUEUED)
        {

#ifdef DEBUG_FSM

            OutputString("Issue SplQmEndDoc");

#endif

            if (!(ul = SplQmEndDoc((HSPL)DCIData->DCISplHandle)))
            {
                DCIData->JobId = 0;

#ifdef DEBUG_FSM

                OutputString("SplQmEndDoc returns error");

#endif

                return(ERROR);
            }

            /******************************************************************/
            /*  Save the JobId for EndDoc to return                           */
            /******************************************************************/
            DCIData->JobId  = ul;
            DCIData->Flags &= ~QM_START_ISSUED;

            /******************************************************************/
            /* PD00760 : Remove the following stmt to prevent multiple calls  */
            /* to SplQmOpen.                                                  */
            /******************************************************************/
//          DCIData->Flags &= ~QM_OPEN_ISSUED; /* PD00227                     */
        }
    }
    return(OK);
}
