/***************************************************************************

Copyright (c) 2000 Microsoft Corporation

Module Name:

        Parport.sys - Parallel port (IEEE 1284, IEEE 1284.3) driver.

File Name:

        driverEntry.c

Abstract:

        DriverEntry routine - driver initialization

Environment:

        Kernel mode only

Notes:

        THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
        KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
        IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
        PURPOSE.

        Copyright (c) 2000 Microsoft Corporation.  All Rights Reserved.

Revision History:

        2000-07-25 - Doug Fritz
         - code cleanup, add comments, add copyright

Author(s):

        Doug Fritz

****************************************************************************/

#include "pch.h"


/************************************************************************/
/* DriverEntry                                                          */
/************************************************************************/
//
// Routine Description:
//
//     This is the DriverEntry routine -- the first function called
//       after the driver has been loaded into memory.
//
// Arguments:
//
//     DriverObject - points to the DRIVER_OBJECT for this driver
//     RegPath      - the service registry key for this driver
//
// Return Value:
//
//     STATUS_SUCCESS   - on success
//     STATUS_NO_MEMORY - if unable to allocate pool
//
// Notes:
//
// Log:
//
/************************************************************************/
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT   DriverObject,
    IN PUNICODE_STRING  RegPath
    )
{
    //
    // Save a copy of *RegPath in driver global RegistryPath for future reference.
    //
    // UNICODE_NULL terminate the path so that we can safely use RegistryPath.Buffer
    //   as a PWSTR.
    //
    {
        USHORT size = RegPath->Length + sizeof(WCHAR);
        RegistryPath.Buffer = ExAllocatePool( (PagedPool | POOL_COLD_ALLOCATION), size );

        if( NULL == RegistryPath.Buffer ) {
            return STATUS_NO_MEMORY;
        }

        RegistryPath.Length        = 0;
        RegistryPath.MaximumLength = size;
        RtlCopyUnicodeString( &RegistryPath, RegPath );
        RegistryPath.Buffer[ size/sizeof(WCHAR) - 1 ] = UNICODE_NULL;
    }



    //
    // Initialize Driver Globals
    //

    // Non-zero means don't raise IRQL from PASSIVE_LEVEL to DISPATCH_LEVEL
    //   when doing CENTRONICS mode (SPP) writes.
    SppNoRaiseIrql = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"SppNoRaiseIrql", &SppNoRaiseIrql );

    // Non-zero means override CENTRONICS as the default Forward mode and/or NIBBLE as
    //   the default Reverse mode. Valid modes are those defined in ntddpar.h as
    //   parameters for IOCTL_IEEE1284_NEGOTIATE.
    // *** Warning: invalid settings and/or setting/device incompatibilities can render
    //       the port unusable until the settings are corrected
    DefaultModes = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DefaultModes", &DefaultModes );

    // Set tracing level for driver DbgPrint messages. Trace values defined in debug.h.
    // Zero means no trace output.
    Trace = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"Trace", &Trace );

    // Request DbgBreakPoint on driver events. Event values defined in debug.h.
    // Zero means no breakpoints requested.
    Break = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"Break", &Break );

    // Mask OFF debug spew for specific devices. See debug.h for flag definitions
    //  0 means allow debug spew for that device
    // ~0 means mask OFF all (show NO) debug spew for that device type
    DbgMaskFdo = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskFdo", &DbgMaskFdo );

    DbgMaskRawPort = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskRawPort", &DbgMaskRawPort );

    DbgMaskDaisyChain0 = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskDaisyChain0", &DbgMaskDaisyChain0 );

    DbgMaskDaisyChain1 = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskDaisyChain1", &DbgMaskDaisyChain1 );

    DbgMaskEndOfChain = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskEndOfChain", &DbgMaskEndOfChain );

    DbgMaskLegacyZip = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskLegacyZip", &DbgMaskLegacyZip );

    DbgMaskNoDevice = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgMaskNoDevice", &DbgMaskNoDevice );

#if 1 == DBG_SHOW_BYTES
    DbgShowBytes = 1;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"DbgShowBytes", &DbgShowBytes );
#endif

    //
    // Allow asserts? non-zero means allow assertions
    //
    AllowAsserts = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"AllowAsserts", &AllowAsserts );

    // Non-zero means enable detection of Iomega Legacy Zip-100 drives that use
    //   an Iomega proprietary Select/Deselect mechanism rather than the Select/Deselect
    //   mechanism defined by IEEE 1284.3. (These drives pre-date IEEE 1284.3)
    // *** Note: if zero, this registry setting is checked again during every PnP QDR/BusRelations
    //       query to see if the user has enabled detection via the Ports property page "Enable
    //       legacy Plug and Play detection" checkbox.
    ParEnableLegacyZip = 0;
    PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );

    // Default timeout when trying to acquire exclusive access to the (shared) port
    {
        const ULONG halfSecond  =  500; // in milliseconds
        const ULONG fiveSeconds = 5000;

        ULONG requestedTimeout  = halfSecond;

        PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"AcquirePortTimeout", &requestedTimeout );

        if( requestedTimeout < halfSecond ) {
            requestedTimeout = halfSecond;
        } else if( requestedTimeout > fiveSeconds ) {
            requestedTimeout = fiveSeconds;
        }

        PPT_SET_RELATIVE_TIMEOUT_IN_MILLISECONDS( AcquirePortTimeout, requestedTimeout );
    }

    {
        //
        // register for callbacks so that we can detect switch between
        // AC and battery power and tone done "polling for printers"
        // when machine switches to battery power.
        //
        OBJECT_ATTRIBUTES objAttributes;
        UNICODE_STRING    callbackName;
        NTSTATUS          localStatus;

        RtlInitUnicodeString(&callbackName, L"\\Callback\\PowerState");
        
        InitializeObjectAttributes(&objAttributes,
                                   &callbackName,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);
        
        localStatus = ExCreateCallback(&PowerStateCallbackObject,
                                       &objAttributes,
                                       FALSE,
                                       TRUE);
        
        if( STATUS_SUCCESS == localStatus ) {
            PowerStateCallbackRegistration = ExRegisterCallback(PowerStateCallbackObject,
                                                                PowerStateCallback,
                                                                NULL);
        }
    }



    //
    // Set dispatch table entries for IRP_MJ_* functions that we handle
    //
    DriverObject->MajorFunction[ IRP_MJ_CREATE                  ] = PptDispatchCreateOpen;
    DriverObject->MajorFunction[ IRP_MJ_CLOSE                   ] = PptDispatchClose;
    DriverObject->MajorFunction[ IRP_MJ_CLEANUP                 ] = PptDispatchCleanup;

    DriverObject->MajorFunction[ IRP_MJ_READ                    ] = PptDispatchRead;
    DriverObject->MajorFunction[ IRP_MJ_WRITE                   ] = PptDispatchWrite;

    DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL          ] = PptDispatchDeviceControl;
    DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = PptDispatchInternalDeviceControl;

    DriverObject->MajorFunction[ IRP_MJ_QUERY_INFORMATION       ] = PptDispatchQueryInformation;
    DriverObject->MajorFunction[ IRP_MJ_SET_INFORMATION         ] = PptDispatchSetInformation;

    DriverObject->MajorFunction[ IRP_MJ_PNP                     ] = PptDispatchPnp;
    DriverObject->MajorFunction[ IRP_MJ_POWER                   ] = PptDispatchPower;

    DriverObject->MajorFunction[ IRP_MJ_SYSTEM_CONTROL          ] = PptDispatchSystemControl;

    DriverObject->DriverExtension->AddDevice                      = P5AddDevice;
    DriverObject->DriverUnload                                    = PptUnload;



    //
    // Break on user request
    //   (typically via registry setting ...\Services\Parport\Parameters : Break : REG_DWORD : 0x1)
    //
    // This is a useful breakpoint in order to manually set appropriate breakpoints elsewhere in the driver.
    //
    PptBreakOnRequest( PPT_BREAK_ON_DRIVER_ENTRY, ("PPT_BREAK_ON_DRIVER_ENTRY - BreakPoint requested") );


    DD(NULL,DDT,"Parport DriverEntry - SUCCESS\n");

    return STATUS_SUCCESS;
}