/*++

Copyright (c) 1990-2000  Microsoft Corporation

Module Name:

    Lpt

Abstract:

    Takes care of request involving an LPT device

Author:

    Ramon Juan San Andres (ramonsa) 26-Jun-1991

Revision History:

--*/


#define _NTAPI_ULIB_

#include "mode.hxx"
#include "lpt.hxx"
#include "file.hxx"
#include "path.hxx"
#include "stream.hxx"
#include "redir.hxx"
#include "registry.hxx"
#include "regvalue.hxx"
#include "array.hxx"
#include "arrayit.hxx"

//
//  When an LPT port is set, mode only sends it an EPSON/IBM sequence.
//  The following macros define the EPSON sequences used.
//
#define     CODE_ESCAPE     0x27
#define     CODE_COLS_80    0x18
#define     CODE_COLS_132   0x15
#define     CODE_LINES_6    '2'
#define     CODE_LINES_8    '0'

#undef      LAST_COM
#define     LAST_COM        4


//
//  Local prototypes
//
BOOLEAN
LptStatus(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    );

BOOLEAN
LptCodePage(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    );

BOOLEAN
LptSetup(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    );

BOOLEAN
LptRedirect(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    );

BOOLEAN
LptEndRedir(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    );

PPATH
GetRedirection(
    IN  PCPATH          DevicePath,
    OUT PREDIR_STATUS   RedirStatus
    );



BOOLEAN
IsAValidLptDevice (
    IN  DEVICE_TTYPE    DeviceType,
    IN  ULONG           DeviceNumber,
    OUT PPATH           *DevicePathPointer
    )

/*++

Routine Description:

    Determines if a certain comm device exists and optionally
    creates a path for it.

Arguments:

    DeviceType      -   Supplies the type of device
    DeviceNumber    -   Supplies the device number
    DeviceName      -   Supplies a pointer to a pointer to the path for
                        the device.

Return Value:

    BOOLEAN -   TRUE if the device exists,
                FALSE otherwise.

Notes:

--*/

{
    DSTRING                 DeviceName;
    DSTRING                 AlternateName;
    DSTRING                 Number;
    BOOLEAN                 Valid = FALSE;
    REGISTRY                Registry;
    DSTRING                 ParentName;
    DSTRING                 KeyName;
    ARRAY                   ValueArray;
    PARRAY_ITERATOR         Iterator;
    ULONG                   ErrorCode;
    PCBYTE                  Data;
    DSTRING                 PortName;
    PREGISTRY_VALUE_ENTRY   Value;


    UNREFERENCED_PARAMETER( DeviceType );


    if ( DeviceName.Initialize( (LPWSTR)L"LPT" )&&
         Number.Initialize( DeviceNumber )      &&
         DeviceName.Strcat( &Number )           &&
         AlternateName.Initialize( (LPWSTR)L"\\DosDevices\\" ) &&
         AlternateName.Strcat( &DeviceName )    &&
         ParentName.Initialize( "" )            &&
         KeyName.Initialize( LPT_KEY_NAME )     &&
         ValueArray.Initialize()                &&
         Registry.Initialize()
       ) {


        //
        //  Get the names of all the serial ports
        //
        if ( Registry.QueryValues(
                        PREDEFINED_KEY_LOCAL_MACHINE,
                        &ParentName,
                        &KeyName,
                        &ValueArray,
                        &ErrorCode
                        ) ) {

            //
            //  See if the given name matches any of the serial ports
            //
            if ( Iterator = (PARRAY_ITERATOR)ValueArray.QueryIterator() ) {

                while ( Value = (PREGISTRY_VALUE_ENTRY)(Iterator->GetNext() ) ) {

                    if ( Value->GetData( &Data ) ) {

                        if ( PortName.Initialize( (PWSTR)Data ) ) {

                            if ( !DeviceName.Stricmp( &PortName ) ||
                                 !AlternateName.Stricmp( &PortName ) ) {

                                Valid = TRUE;

                                break;
                            }
                        }
                    }
                }

                DELETE( Iterator );
            }
        }

        if ( DevicePathPointer ) {

            if (!(*DevicePathPointer = NEW PATH)) {
                DisplayMessageAndExit( MODE_ERROR_NO_MEMORY,
                                       NULL,
                                       (ULONG)EXIT_ERROR );
                return FALSE;   // help lint
            }
            (*DevicePathPointer)->Initialize( &DeviceName );
        }

    }

    return Valid;
}






BOOLEAN
LptHandler(
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Handles LPT requests

Arguments:

    Request -   Supplies pointer to request

Return Value:

    None.

Notes:

--*/

{
    PPATH           DevicePath;     //  Name of Device
    BOOLEAN         Served = TRUE;  //  TRUE if request served OK.
    REDIR_STATUS    Status;

    DebugPtrAssert( Request );
    DebugAssert( Request->DeviceType == DEVICE_TYPE_LPT );

    //
    //  Make sure that the device exists, and at the same time get its
    //  name ( For calling APIs ).
    //
    if ( Request->DeviceName ) {

        if (!(DevicePath = NEW PATH)) {
            DisplayMessageAndExit( MODE_ERROR_NO_MEMORY,
                                   NULL,
                                   (ULONG)EXIT_ERROR );
            return FALSE;   // help lint
        }

        DevicePath->Initialize( Request->DeviceName );

    } else if ( (!IsAValidLptDevice( Request->DeviceType, Request->DeviceNumber, &DevicePath ) &&
                 Request->RequestType != REQUEST_TYPE_LPT_REDIRECT &&
                 !REDIR::IsRedirected( &Status, DevicePath )) ||
                Request->DeviceNumber > LAST_LPT ) {
        DisplayMessageAndExit( MODE_ERROR_INVALID_DEVICE_NAME,
                               DevicePath->GetPathString(),
                               (ULONG)EXIT_ERROR );
    }

    //
    //  So the device is valid. Now serve the request
    //
    switch( Request->RequestType ) {


    case REQUEST_TYPE_STATUS:

        //
        //  Display State of device
        //
        Served = LptStatus( DevicePath, Request );
        break;

    case REQUEST_TYPE_CODEPAGE_PREPARE:
    case REQUEST_TYPE_CODEPAGE_SELECT:
    case REQUEST_TYPE_CODEPAGE_REFRESH:
    case REQUEST_TYPE_CODEPAGE_STATUS:

        //
        //  Codepage request
        //
        Served = LptCodePage( DevicePath, Request );
        break;

    case REQUEST_TYPE_LPT_SETUP:

        //
        //  Printer setup
        //
        Served = LptSetup( DevicePath, Request );
        break;

    case REQUEST_TYPE_LPT_REDIRECT:

        //
        //  Redirect LPT to COM
        //
        Served = LptRedirect( DevicePath, Request );
        break;

    case REQUEST_TYPE_LPT_ENDREDIR:

        //
        //  End redirection of LPT
        //
        Served = LptEndRedir( DevicePath, Request );
        break;

    default:

        DisplayMessageAndExit( MODE_ERROR_INVALID_PARAMETER,
                               NULL,
                               (ULONG)EXIT_ERROR );

    }

    DELETE( DevicePath );

    return Served;

}

BOOLEAN
LptStatus(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Displays status if an LPT device

Arguments:

    DevicePath  -   Supplies pointer to path of device
    Request     -   Supplies pointer to request

Return Value:

    BOOLEAN -   TRUE if status displayed successfully,
                FALSE otherwise

Notes:

--*/

{

    UNREFERENCED_PARAMETER( DevicePath );
    UNREFERENCED_PARAMETER( Request );

    PPATH           RedirPath = NULL;
    REDIR_STATUS    RedirStatus;

    RedirPath = GetRedirection( DevicePath, &RedirStatus );

    if ( !RedirPath && (RedirStatus != REDIR_STATUS_NONEXISTENT) ) {
        //
        //  We cannot find out the status of the redirection.
        //  This is almost certainly due to lack of privileges.
        //  We won't display the LPT status
        //
        return TRUE;
    }

    //
    //  Write the Header
    //
    WriteStatusHeader( DevicePath );


    if ( !RedirPath ) {

        DisplayMessage( MODE_MESSAGE_STATUS_NOT_REROUTED, NULL );

    } else {

        DisplayMessage( MODE_MESSAGE_STATUS_REROUTED, RedirPath->GetPathString() );

        DELETE( RedirPath );
    }

    Get_Standard_Output_Stream()->WriteChar( '\r' );
    Get_Standard_Output_Stream()->WriteChar( '\n' );

    return TRUE;
}

BOOLEAN
LptCodePage(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Handles Codepage requests for LPT device

Arguments:

    DevicePath  -   Supplies pointer to path of device
    Request     -   Supplies pointer to request

Return Value:

    BOOLEAN -   TRUE if request handled successfully,
                FALSE otherwise

Notes:

--*/

{

    UNREFERENCED_PARAMETER( DevicePath );
    UNREFERENCED_PARAMETER( Request );

    DisplayMessage( MODE_ERROR_CODEPAGE_OPERATION_NOT_SUPPORTED, NULL );

    return TRUE;
}

BOOLEAN
LptSetup(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Sets LPT state

Arguments:

    DevicePath  -   Supplies pointer to path of device
    Request     -   Supplies pointer to request

Return Value:

    BOOLEAN -   TRUE if state set successfully,
                FALSE otherwise

Notes:

--*/

{

    PREQUEST_DATA_LPT_SETUP Data;
    PFSN_FILE               Lpt;
    PFILE_STREAM            LptStream;



    Data = (PREQUEST_DATA_LPT_SETUP)&(((PLPT_REQUEST)Request)->Data.Setup);

    if  (  ( Data->SetCol && (Data->Col != 132) && ( Data->Col != 80 ) ) ||
           ( Data->SetLines && (Data->Lines != 6) && (Data->Lines != 8) ) ) {

        //
        //  Invalid number of lines or columns
        //
        DisplayMessageAndExit( MODE_ERROR_LPT_CANNOT_SET, NULL, (ULONG)EXIT_ERROR );

    }


    Lpt = SYSTEM::QueryFile( DevicePath );
    DebugPtrAssert( Lpt );

    if ( Lpt ) {
        LptStream = Lpt->QueryStream( WRITE_ACCESS );
        DebugPtrAssert( LptStream );
    }

    if ( !Lpt || !LptStream ) {
        DisplayMessageAndExit( MODE_ERROR_NO_MEMORY, NULL, (ULONG)EXIT_ERROR );
    }

    if ( Data->SetCol ) {

        //
        //  Set number of columns. The sequence consists of one byte.
        //
        LptStream->WriteByte( (Data->Col == 80) ? CODE_COLS_80 : CODE_COLS_132 );
    }

    if ( Data->SetLines ) {

        //
        //  Set line spacing. The sequence consists of one escape byte
        //  followed by one CODE_LINES_6 or CODE_LINES 8 byte.
        //
        LptStream->WriteByte( CODE_ESCAPE );
        LptStream->WriteByte( (Data->Lines == 6) ? CODE_LINES_6 : CODE_LINES_8 );

    }

    DELETE( LptStream );
    DELETE( Lpt );

    return TRUE;
}

BOOLEAN
LptRedirect(
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Redirects LPT to a COMM port.

Arguments:

    DevicePath  -   Supplies pointer to path of device
    Request     -   Supplies pointer to request

Return Value:

    BOOLEAN -   TRUE if LPT redirected,
                FALSE otherwise

Notes:

--*/

{

    PREQUEST_DATA_LPT_REDIRECT  Data;
    PPATH                       RedirPath;

    Data = (PREQUEST_DATA_LPT_REDIRECT)&(((PLPT_REQUEST)Request)->Data.Redirect);

    //
    //  Verify that the serial device specified is valid
    //
    if ( !IsAValidDevice( Data->DeviceType, Data->DeviceNumber, &RedirPath )) {

        DisplayMessageAndExit( MODE_ERROR_INVALID_DEVICE_NAME,
                               RedirPath->GetPathString(),
                               (ULONG)EXIT_ERROR );

    }

    if ( !REDIR::Redirect( DevicePath, RedirPath ) ) {

        DisplayMessageAndExit( MODE_ERROR_LPT_CANNOT_REROUTE, RedirPath->GetPathString(), (ULONG)EXIT_ERROR );

    }

    //
    //  Display the status as confirmation
    //
    LptStatus( DevicePath, Request );

    DELETE( RedirPath );

    return TRUE;
}


BOOLEAN
LptEndRedir (
    IN  PCPATH          DevicePath,
    IN  PREQUEST_HEADER Request
    )

/*++

Routine Description:

    Ends the redirection of an LPT port

Arguments:

    DevicePath  -   Supplies pointer to path of device
    Request     -   Supplies pointer to request

Return Value:

    BOOLEAN -   TRUE

Notes:

--*/

{

    REDIR_STATUS    Status;

    //
    //  If the LPT is being redirected, end the redirection
    //
    if ( REDIR::IsRedirected( &Status, DevicePath ) ) {

        if ( !REDIR::EndRedirection( DevicePath )) {

            DisplayMessageAndExit( MODE_ERROR_LPT_CANNOT_ENDREROUTE, NULL, (ULONG)EXIT_ERROR );
        }
    }

    //
    //  Display status
    //
    LptStatus( DevicePath, Request );

    return TRUE;

}

PPATH
GetRedirection(
    IN  PCPATH          DevicePath,
    OUT PREDIR_STATUS   RedirStatus
    )

/*++

Routine Description:

    Determines to what device is the LPT redirected to

Arguments:

    DevicePath  -   Supplies pointer to path of device

    RedirStatus -   Supplies pointer to redirection status

Return Value:

    PPATH   -   Pointer to the redirected device

--*/

{

    ULONG   DeviceNumber    =   1;
    PPATH   DestPath        =   NULL;
    BOOLEAN ValidDevice     =   TRUE;

    if ( REDIR::IsRedirected( RedirStatus, DevicePath ) ) {

        for ( DeviceNumber = 1; DeviceNumber <= LAST_COM; DeviceNumber++ ) {

            IsAValidDevice( DEVICE_TYPE_COM, DeviceNumber, &DestPath );

            if ( REDIR::IsRedirected( RedirStatus, DevicePath, DestPath )) {

                break;

            }

            DELETE( DestPath );
            DestPath = NULL;

        }
    }

    return DestPath;

}