/*M*
//*****************************************************************************
//*                   INTEL CORPORATION PROPRIETARY INFORMATION               *
//*     This software is supplied under the terms of a license agreement or   *
//*     nondisclosure agreement with Intel Corporation and may not be copied  *
//*     or disclosed except in accordance with the terms of that agreement.   *
//*           Copyright (c) 1995-2000 Intel Corporation.  All Rights Reserved.     *
//*****************************************************************************
//  PVCS:
//      $Workfile:   imb_nt.c  $
//      $Revision:   1.22  $
//      $Modtime:   12 Jan 2000 13:59:24  $
//      $Author:   jpaiasno  $
//
//  Purpose:    This is the main source file for the IMB IMB port driver..
//              
//  
//  Contents:
//              DriverEntry				The initialization entry point called by the NT kernel.
//
//              ImbDeviceControl		Driver IOCTL dispatch routine
//				ImbOpenCloseDispatch	Driver open/close dispatch function
//          
//            
//  Notes:
*M*/

/*
 * $Log:   //cospublic004/shareddata/isc/ISC2x/ntwork/src/imb/imb_nt.c.v  $
 * 
 *    Rev 1.22   12 Jan 2000 15:33:12   jpaiasno
 * added more checking for async named event object
 * pointer being NULL.
 * 
 *    Rev 1.21   12 Jan 2000 13:54:18   jpaiasno
 * Added checking for NULL before signalling the
 * named async object
 * 
 *    Rev 1.20   12 Jan 2000 13:51:12   jpaiasno
 * fixed the  typo error from assignment  to
 * equivalence for checking named async event
 * object
 * 
 * 
 *    Rev 1.19   28 Dec 1999 09:42:42   jpaiasno
 * Refined  logic related to creation of DPC
 * named event object and detection of IPMI version
 * 
 *    Rev 1.17   21 Dec 1999 14:34:46   rfogel
 * Resolved problem with UW build.
 * 
 *    Rev 1.16   14 Dec 1999 13:40:44   rfogel
 * Latest Win 2000 Driver Version (inc. WMI support)
 * 
 *    Rev 1.15   29 Nov 1999 15:23:58   spoolax
 * Fixed tracker item #9205
 * 
 *    Rev 1.14   15 Nov 1999 14:50:20   jpaiasno
 * Fixed a logic problem in determining
 * the IPMI version  supported by BMC firmware 
 * 
 *    Rev 1.13   27 Oct 1999 12:21:18   rfogel
 * Removed "DebugPrint" for UnixWare.
 * 
 *    Rev 1.12   12 Oct 1999 11:39:04   rfogel
 * Resolved conflicts between UnixWare and Win NT/2000 builds.
 * 
 *    Rev 1.11   09 Oct 1999 13:46:26   rfogel
 * Conversion to Win 2000 WDM (PnP and PM only, no WMI)
 * 
 *    Rev 1.10   27 Jul 1999 10:18:16   jpaiasno
 *  
 * 
 *    Rev 1.9   27 May 1999 10:27:06   jpaiasno
 * Added preprocessor directive to bypass
 * NT specific code in UnixWare environment
 * 
 *    Rev 1.8   May 07 1999 11:56:02   sganeshx
 * 1. added a new #define for response length
 *     to the IPMI 1.0 Get Device ID command
 * 2. modified the check in the GetDevice ID
 *    function to check for
 * (IPMI10_GET_DEVICE_ID_RESP_LENGTH-1)
 * 
 *    Rev 1.7   04 Mar 1999 09:34:36   jpaiasno
 * minor changes in comments section.
 * 
 *    Rev 1.6   04 Mar 1999 09:32:20   jpaiasno
 * fixed more logic problems related to detection
 * of IPMI  version of BMC controller. Size of the allocated response buffer was
 *  255 but 'SendImbRequest' function does not directly use the passed buffer. 
 * Also 'GetDeviceID' response length  for IPMI
 * 1.0 is 12 and not 13.
 * 
 * 
 *    Rev 1.5   21 Jan 1999 14:20:50   jpaiasno
 * Fixed a logic problem related to checking the IPMI 
 * version
 * 
 *    Rev 1.4   21 Jan 1999 11:44:24   jpaiasno
 *  
 * 
 *    Rev 1.3   21 Jan 1999 11:40:36   jpaiasno
 * Added Support for IPMI 1.0 firmware
 * 
 *    Rev 1.2   31 Dec 1998 14:42:16   jpaiasno
 * Fixed the 'Async DPC message notification
 * sync. object' set/reset   problem by resetting the object
 * in the driver code, where the DPC message is read by the
 *  win32 APP.
 * 
 *    Rev 1.1   08 Dec 1998 10:55:48   jpaiasno
 * Added code to support async.
 * DPC events from the EMP console.
 * 
 *    Rev 1.11   09 Apr 1998 12:46:50   djneuhen
 * Fixed ImbAsyncOsReqWait() to convert its timeout
 * value from uSec to 100 nSec units
 * 
 *    Rev 1.10   16 Mar 1998 14:22:54   fkhanx
 * Added more comments
 * 
 *    Rev 1.9   11 Mar 1998 16:02:56   djneuhen
 * Added histogram buffer support (commented out)
 * Moved things a little to reduce number of ifdef's.
 * Changed delayRetryFunc to allow certain number of
 * passes (retries) before sleeping.
 * 
 *    Rev 1.8   06 Mar 1998 09:45:16   mmysorex
 * Fixed compilation errors in unixware related code
 * 
 *    Rev 1.7   27 Feb 1998 18:11:30   mmysorex
 * Added Unixware related changes
 * 
 *    Rev 1.6   24 Feb 1998 17:56:00   fkhanx
 * Fixed the unload exception problem.Commented out
 * a Semaphore release call in ImbUnload
 * 
 *    Rev 1.5   20 Feb 1998 13:20:18   fkhanx
 * Fixed the Interface checking code Bug.This
 * was causing blue screen when all the Inteface
 * probes failed
 * 
 *    Rev 1.4   11 Feb 1998 11:56:22   fkhanx
 * Fixed the Symbolic Link deletion problem in
 * ImbUnload
 * 
 *    Rev 1.3   06 Feb 1998 15:59:06   djneuhen
 * New async model changes, added timeout to process thread wait
 * 
 *    Rev 1.2   30 Jan 1998 18:43:42   djneuhen
 * Added new OS utility routines and multi-interface support
 * 
 *    Rev 1.1   29 Jan 1998 18:10:14   djneuhen
 * General changes to compile
 * 
 *    Rev 1.0   27 Jan 1998 16:59:58   djneuhen
 *  
 */

//*****************************************************************************
//* INCLUDES
//*****************************************************************************

#ifndef UWSVR4

   #include "CompDiag.h"

#endif

//-----------------------------------------------------------------------------
// These defines are used by files included in "imb_os.h".
//-----------------------------------------------------------------------------

#define  D_InitWMI_Info               // Init WMI info in this module

//=============================================================================
// Local Includes
//=============================================================================

#include "imb_os.h" 
#include "imb_drv.h"

//*****************************************************************************
//* UnixWare SVR4?
//*****************************************************************************

#ifdef UWSVR4

   extern void AsyncMesgPoller(PDEVICE_EXTENSION);

   #define DriverEntry ImbDriverEntry

#else

   #define D_DriverRegistrySubkey   L"imbdrv"

#endif

#ifdef UWSVR4
   #define DebugPrintInit(arg)
   #define DebugPrintClose()
   #define DebugPrint(arg)
#endif

//*****************************************************************************
//* Prototypes
//*****************************************************************************

NTSTATUS          DriverEntry(   IN PDRIVER_OBJECT  atpDriverObject,
                                 IN PUNICODE_STRING atpRegistryPath
                                 );

static NTSTATUS   ImbOpenCloseDispatch(IN PDEVICE_OBJECT, IN PIRP);
static NTSTATUS   ImbDeviceControl(IN PDEVICE_OBJECT, IN PIRP);
static void       ImbProcessRequest(PIRP, PDEVICE_EXTENSION);
static NTSTATUS   ImbShutdown(IN PDEVICE_OBJECT, IN PIRP);
static VOID       TimerDpcFunc(IN PKDPC, IN PDEVICE_OBJECT, IN PVOID, IN PVOID);
VOID              getBmcIpmiVersion(PDEVICE_EXTENSION deviceExtension);

//-----------------------------------------------------------------------------
// Not UnixWare SVR4?
//-----------------------------------------------------------------------------

#ifndef UWSVR4

   extern NTSTATUS   MapTheMemory(  IN PDEVICE_OBJECT,
                                    IN OUT PVOID,
                                    IN ULONG,
                                    IN ULONG
                                    );

   static VOID       RequestProcessingThread(PVOID);

   static VOID ImbUnload(IN struct _DRIVER_OBJECT *DriverObject);

#endif

//*****************************************************************************
//* Global variables
//*****************************************************************************

//-----------------------------------------------------------------------------
// Insertion strings for error logging.
//-----------------------------------------------------------------------------

WCHAR   ErrorStorage[]        = L"Driver memory Allocation Error";
WCHAR   ErrorTimeout[]        = L"Timeout during access to IMB interface";
WCHAR   ErrorImbAbsent[]      = L"IMB interface not present";
WCHAR   ErrorInputBuffer[]    = L"Improper buffer size of input Parameters";
WCHAR   ErrorOutputBuffer[]   = L"Improper buffer size for output Parameters";
WCHAR   ErrorInterface[]      = L"Error occured during access to IMB";
WCHAR   ErrorDriver1[]        = L"Thread creation error";
WCHAR   ErrorDeviceObject[]   = L"Could not create IMB device object";

//-----------------------------------------------------------------------------
// Testing ... This array holds histogram data for tuning the
// delayretryfunc() behavior
//-----------------------------------------------------------------------------
//
// DWORD ImbDelayHist[100];

//-----------------------------------------------------------------------------
// Not UnixWare SVR4?
//-----------------------------------------------------------------------------
// Use the alloc_text pragma to specify the driver initialization routines
// (they can be paged out).
//-----------------------------------------------------------------------------

#ifndef UWSVR4

   #ifdef ALLOC_PRAGMA

      #pragma alloc_text(INIT, DriverEntry)

   #endif

#endif

/*F*
//*****************************************************************************
//* DriverEntry(...)
//*****************************************************************************
//* Name:       DriverEntry
//* Purpose:    Main driver initialization routine.
//* Context:    Called by NT kernel at boot time.
//* Returns:    NTSTATUS, standard NT DDK defined status codes (STATUS_SUCCESS
//*             or error code)
//* Parameters:
//*             DriverObject    Pointer to driver object created by system
//*             RegistryPath    Pointer to the Unicode name of the registry
//*                             path for this driver
//* Notes:      none
//*****************************************************************************
*F*/
 
NTSTATUS DriverEntry
  (
   IN PDRIVER_OBJECT  atpDriverObject,
   IN PUNICODE_STRING atpRegistryPath
   )
  {
   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"IMB Driver V3.0 \"DriverEntry(...)\"");

   //-----------------------------------------------------------------------
   // Set up the device driver entry points.
   //
   // Right now we do not need a start io routine.
   //-----------------------------------------------------------------------

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Setup standard Major Functions and entry points");

   atpDriverObject->MajorFunction[IRP_MJ_CREATE]         = ImbOpenCloseDispatch;
   atpDriverObject->MajorFunction[IRP_MJ_CLOSE]          = ImbOpenCloseDispatch;
   atpDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ImbDeviceControl;
   
   atpDriverObject->MajorFunction[IRP_MJ_SHUTDOWN]       = ImbShutdown;

   //-----------------------------------------------------------------------
   // Not UnixWare SVR4?
   //-----------------------------------------------------------------------

   #ifndef UWSVR4

      atpDriverObject->DriverUnload = ImbUnload;

   #endif

   //-----------------------------------------------------------------------
   // If the driver is a new configuration, i.e. not a legacy driver, then
   // assume that all that is necessary for the new driver configuration has
   // been done.
   //-----------------------------------------------------------------------

   #ifdef D_SupportWin2000

      if (gbNewDriverConfiguration( atpDriverObject,
                                    atpRegistryPath,
                                    D_DriverRegistrySubkey,
                                    D_DefaultMaxNumberOfDevices,
                                    &gtIMBDrvIF_GUID,
                                    gta_WMIGUIDREGINFO,
                                    (sizeof(gta_WMIGUIDREGINFO)/sizeof(WMIGUIDREGINFO))
                                    )
         )
        {
         return (STATUS_SUCCESS);
         }
      else
         D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Assuming legacy driver");

   #else

      D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Built as legacy driver");

   #endif

   return (gtInitializeDriver(atpDriverObject,NULL));
   }

/*F*
//*****************************************************************************
//* gtInitializeDriver(...)
//*****************************************************************************
//* Name:       gtInitializeDriver
//* Purpose:    Driver initialization that was previuosly done in
//*             "DriverEntry(...)".
//* Context:    Called by "DriverEntry(...)" in kernel mode at boot time except
//*             for Win 2000 inwhich it is called by "AddDevice(...)" when the
//*             PnP Manager detectes a new device associated with this driver
//*             object.
//* Returns:    NTSTATUS, standard NT DDK defined status codes (STATUS_SUCCESS
//*             or error code)
//* Parameters:
//*             DriverObject    Pointer to driver object created by system
//* Notes:      none
//*****************************************************************************
//* (*) The second function argument was added in order to pass back the device
//* object for the Win 2000 "AddDevice" entry point.
//*****************************************************************************
*F*/
 
NTSTATUS gtInitializeDriver
  (
   IN PDRIVER_OBJECT    DriverObject,
   PDEVICE_OBJECT      *atppDeviceObject
   )
  {
   PDEVICE_EXTENSION    deviceExtension = NULL;
   PIO_ERROR_LOG_PACKET errorLogEntry;
   PUCHAR               stringLoc;
   NTSTATUS             status = STATUS_SUCCESS;
   UNICODE_STRING       fullDeviceName;
   UNICODE_STRING       symbolicLinkName;
   UNICODE_STRING       eventName;

   WCHAR *              errorStringPtr;
   UNICODE_STRING       errorString;
   ULONG                uniqueErrorValue;
   ULONG                dumpCount;
   NTSTATUS             errorCode = STATUS_SUCCESS;
   ULONG                dumpData[DUMP_COUNT];
   DWORD                i;
   ImbInterface **      imbIf;
   extern ImbInterface *ImbInterfaces[];

   PDEVICE_OBJECT       tpDeviceObject    = NULL;
   PDEVICE_OBJECT      *tppDeviceObject   = NULL;

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"gtInitializeDriver(...)\"");

   //--------------------------------------------------------------------------
   // If caller does not specify a device object then use dummy one; otherwise,
   // use the caller-specified device object.
   //--------------------------------------------------------------------------

   if (!atppDeviceObject)
      tppDeviceObject = &tpDeviceObject;
   else
      tppDeviceObject = atppDeviceObject;

   //--------------------------------------------------------------------------
   // Zero-initialize various structures to aid failure exit handling.
   //--------------------------------------------------------------------------

   fullDeviceName.MaximumLength     = 0;
   fullDeviceName.Length            = 0;

   symbolicLinkName.MaximumLength   = 0;
   symbolicLinkName.Length          = 0;

   //--------------------------------------------------------------------------
   // Check to see if we have an interface that we recognize
   //--------------------------------------------------------------------------

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Check for IMB interfaces");

   for (imbIf = ImbInterfaces; *imbIf != NULL; imbIf++)
     {
      if ((*imbIf)->ifProbe() == TRUE)
         break;
      }
   
   if (*imbIf == NULL)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Hardware failure");

      errorCode = HARDWARE_FAILURE;
      status = STATUS_UNSUCCESSFUL;
      goto InitializeExit;
      }

   //--------------------------------------------------------------------------
   // TESTING - init the delay histogram array
   //
   // RtlZeroMemory(ImbDelayHist, sizeof(ImbDelayHist));
   //
   // Set up space for the full device object name.
   //--------------------------------------------------------------------------

   RtlInitUnicodeString(&fullDeviceName, NULL);
    
   fullDeviceName.MaximumLength = sizeof(UNICODE_FULL_DEVICE_NAME);
                  
   fullDeviceName.Buffer = ExAllocatePool(   PagedPool,
                                             fullDeviceName.MaximumLength
                                             );
    
   if (!fullDeviceName.Buffer)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Can't allocate pool for device name via \"ExAllocatePool(...)\"");

      status            = STATUS_UNSUCCESSFUL;
      errorCode         = INSUFFICIENT_RESOURCES;
      uniqueErrorValue  = IMB_ERROR_VALUE_BASE + 1;
      dumpData[0]       = (ULONG) fullDeviceName.MaximumLength;
      dumpCount         = 1;    
      errorStringPtr    = ErrorStorage;

      goto InitializeExit;
      }

   RtlZeroMemory(fullDeviceName.Buffer, fullDeviceName.MaximumLength);

   RtlAppendUnicodeToString(&fullDeviceName, UNICODE_FULL_DEVICE_NAME);

   //--------------------------------------------------------------------------
   // Set up space for the symbolic link name.
   //--------------------------------------------------------------------------

   RtlInitUnicodeString(&symbolicLinkName, NULL);
    
   symbolicLinkName.MaximumLength = sizeof(UNICODE_SYMBOLIC_LINK_NAME);                                      
    
   symbolicLinkName.Buffer = ExAllocatePool( PagedPool,
                                             symbolicLinkName.MaximumLength
                                             );
    
    if (!symbolicLinkName.Buffer)
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Can't allocate pool for Symbolic link via \"ExAllocatePool(...)\"");

      status            = STATUS_UNSUCCESSFUL;
      errorCode         = INSUFFICIENT_RESOURCES;
      uniqueErrorValue  = IMB_ERROR_VALUE_BASE + 2;
      dumpData[0]       = (ULONG) symbolicLinkName.MaximumLength;
      errorStringPtr    = ErrorStorage;
      dumpCount         = 1;    

      goto InitializeExit;
      }

    RtlZeroMemory(symbolicLinkName.Buffer, symbolicLinkName.MaximumLength);

    RtlAppendUnicodeToString(&symbolicLinkName,UNICODE_SYMBOLIC_LINK_NAME);

   //--------------------------------------------------------------------------
   // Create object for the IMB device.
   // Note that we specify that this is a non-exclusive device.
   // User code will be able to open this device.
   //--------------------------------------------------------------------------

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"Create device");

   status = IoCreateDevice(   DriverObject,
                              sizeof(DEVICE_EXTENSION),
                              &fullDeviceName,
                              FILE_DEVICE_IMB,
                              0,
                              FALSE,
                              tppDeviceObject
                              );

   if (!NT_SUCCESS(status))
     {
      D_DiagMsg0(D_Error,D_DC_Errors,"Can't create device via \"IoCreateDevice(...)\"");

      status            = STATUS_UNSUCCESSFUL;
      errorCode         = INSUFFICIENT_RESOURCES;
      uniqueErrorValue  = IMB_ERROR_VALUE_BASE + 3;
      dumpData[0]       = (ULONG) 1;
      dumpCount         = 1;
      errorStringPtr    = ErrorDeviceObject;

      goto InitializeExit;
      }

   //--------------------------------------------------------------------------
   // Set up the device extension.
   //--------------------------------------------------------------------------

   deviceExtension = (PDEVICE_EXTENSION)(*tppDeviceObject)->DeviceExtension;

   deviceExtension->DeviceObject    = *tppDeviceObject;
   deviceExtension->unloadingDriver = 0;
   deviceExtension->shutdownCode    = SD_NO_ACTION;
   deviceExtension->imbSeqNo        = 0;
   deviceExtension->imbIf           = *imbIf;
   
   
   //--------------------------------------------------------------------------
   // UnixWare SVR4?
   //--------------------------------------------------------------------------

   #ifndef UWSVR4

      //-----------------------------------------------------------------------
      // create semaphore for synchronizing  thread activity with 
      // IO control  function and for synchronizing request sending 
      //-----------------------------------------------------------------------

      KeInitializeSemaphore(&deviceExtension->dispatchSemaphor, 0L, MAXLONG);
      KeInitializeSemaphore(&deviceExtension->requestSemaphor,  1L, 1L);
      KeInitializeSemaphore(&deviceExtension->asyncQSemaphor,  1L, 1L);

      InitializeListHead(&deviceExtension->queueListEntry);

      //-----------------------------------------------------------------------
      // Initialize synchronization spinlock (stored in device extension)
      // This spinlock is used by request processing threads to
      // detach entries from  request queue.
      //-----------------------------------------------------------------------
   
      KeInitializeSpinLock(&deviceExtension->queueSpinLock);

   #else

      //-----------------------------------------------------------------------
      // initialize the request semaphore
      //-----------------------------------------------------------------------

      deviceExtension->requestSemaphor = NULL;
      deviceExtension->asyncQSema      = 0;

   #endif

   //-----------------------------------------------------------------------
   // initialize the async messaging subsystem
   // it would be nice to get the queue size from the registry
   //-----------------------------------------------------------------------

   deviceExtension->asyncQ.queue
      = (ImbAsyncEntry *)ExAllocatePool
                             (
                              PagedPool,
                              IMB_ASYNC_QUEUE_SIZE * sizeof(ImbAsyncEntry)
                              );

   deviceExtension->asyncQ.queueSize = IMB_ASYNC_QUEUE_SIZE;

   ImbAsyncInit(&deviceExtension->asyncQ);

   //-----------------------------------------------------------------------
   // Initialize the timer that will be used to timeout the message to be sent.
   //-----------------------------------------------------------------------

   KeInitializeTimer(&deviceExtension->mesgTimer);

   KeInitializeDpc(  &deviceExtension->mesgTimerDpc,
                     (PKDEFERRED_ROUTINE) TimerDpcFunc,
                     *tppDeviceObject
                     );

   //-----------------------------------------------------------------------
   // Create a symbolic link, e.g. a name that a Win32 app can specify to
   // open the device
   //-----------------------------------------------------------------------

   status = IoCreateSymbolicLink(&symbolicLinkName, &fullDeviceName);

   if (!NT_SUCCESS(status))
     {
      //-----------------------------------------------------------------------
      // Symbolic link creation failed- note this & then delete the
      // device object (it's useless if a Win32 app can't get at it).
      //-----------------------------------------------------------------------
   
      D_DiagMsg1(D_Error,D_DC_Errors,"\"IoCreateSymbolicLink(...)\" failed, %x",status);
      IoDeleteDevice(*tppDeviceObject);
      *tppDeviceObject = NULL;
      }

   //--------------------------------------------------------------------------
   // Not UnixWare SVR4?
   //--------------------------------------------------------------------------

   #ifndef UWSVR4

      //-----------------------------------------------------------------------
      // create thread for request processing
      //-----------------------------------------------------------------------

      status = PsCreateSystemThread(   &deviceExtension->threadHandle,
                                       (ACCESS_MASK) 0L,
                                       NULL,
                                       (HANDLE) 0L,
                                       NULL,
                                       RequestProcessingThread,
                                       *tppDeviceObject
                                       );

      if (!NT_SUCCESS(status))
        {
         errorCode         = DRIVER_FAILURE;
         uniqueErrorValue  = IMB_ERROR_VALUE_BASE + 7;
         dumpData[0]       = (ULONG) symbolicLinkName.MaximumLength;
         errorStringPtr    = ErrorDriver1;
         dumpData[0]       = (ULONG) 3;
         dumpCount         = 1;  
      
         goto InitializeExit;
         }

     

   #endif

   //-----------------------------------------------------------------------
   // (*) Driver entry-point setup is now located in "DriverEntry(...)".
   //-----------------------------------------------------------------------
   // (*)
   //-----------------------------------------------------------------------

   //--------------------------------------------------------------------------
   // Get the IPMI version of the BMC controller. The only way to check it is
   // by looking at the response length and byte 6 of the response data of the
   // BMC command 'GetDeviceId'. This method defeats the purpose of the IMB
   // driver being just a communication pipe since now it has to be made aware
   // of the  firmware functionality, but we do not have a choice here.
   // (pai 1/13/99)
   //--------------------------------------------------------------------------
   // Assume by default old IPMI version and later detect the actual one.
   // do not change the sequence of these two instructions
   // (pai 1/21)
   //--------------------------------------------------------------------------

   deviceExtension->bmcVer = IPMI_OLD_VER;
   getBmcIpmiVersion(deviceExtension);

   //--------------------------------------------------------------------------
   // Enable shutdown notifications for this driver.  Shutdown notifications
   // are dispatched t oentry point associated IRP_MJ_SHUTDOWN".
   //--------------------------------------------------------------------------

   IoRegisterShutdownNotification(*tppDeviceObject);

   #ifndef UWSVR4

	deviceExtension->asyncNEventObj = NULL;
   
      //-----------------------------------------------------------------------
      // Create DPC event messages synchronizing  object (pai 12/7)
      //-----------------------------------------------------------------------
  
      RtlInitUnicodeString (&eventName, L"\\BaseNamedObjects\\AsyncSignalEvt");

      deviceExtension->asyncNEventObj
	 = IoCreateNotificationEvent(  &eventName,
				       &deviceExtension->asyncNEventHandle
				       );

      if (deviceExtension->asyncNEventObj == NULL)
	{
	 goto InitializeExit;
	 }

      KeClearEvent (deviceExtension->asyncNEventObj);

#endif

   InitializeExit:

   if (errorCode != STATUS_SUCCESS)
     {
      RtlInitUnicodeString(&errorString, NULL);

      errorString.Length         = 0;
      errorString.MaximumLength  = 150;

      if (errorString.Buffer = ExAllocatePool(  NonPagedPool,
                                                errorString.MaximumLength
                                                )
          )
        {
         RtlZeroMemory(errorString.Buffer, errorString.MaximumLength);
         RtlAppendUnicodeToString(&errorString, errorStringPtr);
         }

      errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry
                          (
                           (*tppDeviceObject == NULL) ? 
                              (PVOID) DriverObject : (PVOID)*tppDeviceObject,
                                   (UCHAR) (sizeof(IO_ERROR_LOG_PACKET)
                                 + errorString.MaximumLength)
                           );
      
      if (errorLogEntry != NULL)
        {
         errorLogEntry->ErrorCode         = errorCode;
         errorLogEntry->DumpDataSize      = (USHORT) dumpCount * sizeof(ULONG);
         errorLogEntry->SequenceNumber    = 0;
         errorLogEntry->MajorFunctionCode = 0;
         errorLogEntry->IoControlCode     = 0;
         errorLogEntry->RetryCount        = 0;
         errorLogEntry->UniqueErrorValue  = uniqueErrorValue;
         errorLogEntry->FinalStatus       = status;

         for (i = 0; i < dumpCount; i++)
            errorLogEntry->DumpData[i] = dumpData[i];

         if (errorString.Buffer)
           {
            errorLogEntry->StringOffset   = (USHORT) sizeof(IO_ERROR_LOG_PACKET);
            stringLoc                     = ((PUCHAR) errorLogEntry)
                                             +  errorLogEntry->StringOffset;

            CopyMemory(stringLoc, errorString.Buffer, errorString.Length);

            stringLoc  += errorString.Length;
            *stringLoc  = (WCHAR ) 0;
            }

         IoWriteErrorLogEntry(errorLogEntry);

         if (errorString.Buffer)
            ExFreePool(errorString.Buffer);
         }
      }

   //--------------------------------------------------------------------------
   // Free the unicode strings for device name and symbolic link name.
   //--------------------------------------------------------------------------

   if (fullDeviceName.MaximumLength != 0)
      ExFreePool(fullDeviceName.Buffer);

   if (symbolicLinkName.MaximumLength != 0)
      ExFreePool(symbolicLinkName.Buffer);
 
   return(status);
   }

//*****************************************************************************
//* gspGetDefaultWin2000_DevExt(...)
//*****************************************************************************

#ifndef UWSVR4

#ifdef D_SupportWin2000

struct S_Win2000_DevExt *gspGetWin2000_DevExt
  ( 
   PDEVICE_OBJECT atpDevObj
   )
  {
   return (&((PDEVICE_EXTENSION)
             (atpDevObj->DeviceExtension))->s_Win2000_DevExt
           );
   }

#endif

#endif


//*****************************************************************************
//* getBmcIpmiVersion(...)
//*****************************************************************************

VOID getBmcIpmiVersion(PDEVICE_EXTENSION deviceExtension)
  {
   ImbRequestBuffer  *imbReq;
   ImbResponseBuffer *imbResp;

   DWORD          imbCmd;
   IMB_STATUS     status;
   DWORD          respLength;
    
   imbReq = (ImbRequestBuffer *)ExAllocatePool(PagedPool, (ULONG) 255);

   if (imbReq == NULL)
      return;
   
   imbResp= (ImbResponseBuffer *) ExAllocatePool(PagedPool, (ULONG) 255);

   if (imbResp == NULL)
     {
      ExFreePool((PVOID)imbReq);
      return;
      }

    respLength = MAX_BMC_RESPONSE_SIZE;

   imbReq->req.rsSa        = BMC_SA;
   imbReq->req.cmd         = GET_DEVICE_ID;
   imbReq->req.netFn       = APP_NETFN;
   imbReq->req.rsLun       = 0;
   imbReq->req.dataLength  = 0;
   imbReq->flags           = 0;
   imbReq->timeOut         = DEFAULT_MESSAGE_TIMEOUT; 

   ImbOsStartReq(DEFAULT_MESSAGE_TIMEOUT, deviceExtension);

   // This is a chicken and egg problem. To determine IPMI version we 
    // need to send a command to BMC. But to send a command to BMC we
   // have to know the version of IPMI.
   // So we try by faking  old version first (pai 11/15/99)

   deviceExtension->bmcVer = IPMI_OLD_VER;


   if ( (SendImbRequest(  imbReq, 
			imbResp,
			&respLength,
			deviceExtension
			)
       == IMB_SUCCESS ) && (!imbResp->cCode) && (respLength < (IPMI10_GET_DEVICE_ID_RESP_LENGTH-1))
       )  {

			// so it is the old IPMI version
	      ; 
     
			


		} else {
					  ImbOsEndReq(deviceExtension);   

					//old version check didn't work,                                                
					// check for new version
					deviceExtension->bmcVer = (UCHAR)IPMI_1_VER; 
					respLength = MAX_BMC_RESPONSE_SIZE;
					ImbOsStartReq(DEFAULT_MESSAGE_TIMEOUT, deviceExtension);  


					
					if (SendImbRequest(  imbReq, 
										imbResp,
										&respLength,
										deviceExtension
										)
										!= IMB_SUCCESS ) 
					{

								deviceExtension->bmcVer = (UCHAR) IPMI_UNKNOWN_VER; 

					}       else {


									//-----------------------------------------------------------------------
									// New version of IPMI?
									//-----------------------------------------------------------------------

										if ((!imbResp->cCode)
												&&
											(respLength >= (IPMI10_GET_DEVICE_ID_RESP_LENGTH-1))
											)
											{
												switch (imbResp->data[4])
		 
													{
														case IPMI_1_VER:
   
															deviceExtension->bmcVer = (UCHAR)IPMI_1_VER; 
															break;

														//-----------------------------------------------------------------
														// Future IPMI versions here.
														//-----------------------------------------------------------------

															default:
															//default right now assume the ipmi version to be 1.0   
															deviceExtension->bmcVer = (UCHAR)IPMI_1_VER; 
															break;

													}
											} else {



													deviceExtension->bmcVer = (UCHAR) IPMI_UNKNOWN_VER; 
		

											}
										
				}

					

				
	
		}



		
      

   ImbOsEndReq(deviceExtension);

   ExFreePool((PVOID)imbReq);
   ExFreePool((PVOID)imbResp);
  }




//*****************************************************************************
//* isIpmiOldVersion(...)
//*****************************************************************************

int isIpmiOldVersion(void *context)
  {
   PDEVICE_EXTENSION deviceExtension;

   deviceExtension = (PDEVICE_EXTENSION) context;

   if (deviceExtension->bmcVer == (UCHAR) IPMI_OLD_VER)
      return 1;
   else
      return 0;
   }


//*****************************************************************************
//* Not UnixWare SVR4?
//*****************************************************************************

#ifndef UWSVR4

/*F*
//*****************************************************************************
//* gvPrepareToUnload(...)
//*****************************************************************************
//* This function is called by 
//*****************************************************************************
*F*/

VOID gvPrepareToUnload(PDRIVER_OBJECT DriverObject)
  {
   NTSTATUS          t_NTSTATUS        = STATUS_SUCCESS;
   PDEVICE_OBJECT    thisDevice        = DriverObject->DeviceObject;
   PDEVICE_EXTENSION deviceExtension   = thisDevice->DeviceExtension;  
   UNICODE_STRING    symbolicLinkName;
   static BOOL       sAlreadyCalled    = FALSE;


   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"gvPrepareToUnload(...)\"");

   if (sAlreadyCalled)
     {
      D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"\"gvPrepareToUnload(...)\" has already been called");
      return;
      }

   sAlreadyCalled = TRUE;

   //--------------------------------------------------------------------------
   // Find our thread
   //--------------------------------------------------------------------------

   ObReferenceObjectByHandle( deviceExtension->threadHandle,
                              KernelMode,
                              NULL,
                              KernelMode,
                             &deviceExtension->threadObject,
                              NULL
                              );

   //--------------------------------------------------------------------------
   // Tell it to exit
   //--------------------------------------------------------------------------

   deviceExtension->unloadingDriver = 1;

   KeReleaseSemaphore(&deviceExtension->dispatchSemaphor, 0, 1, FALSE);

   //--------------------------------------------------------------------------
   // Wait for it to exit
   //--------------------------------------------------------------------------

   KeWaitForSingleObject(  deviceExtension->threadObject,
                           Executive,
                           KernelMode,
                           FALSE,
                           NULL
                           );
   //--------------------------------------------------------------------------
   // Clean up
   //--------------------------------------------------------------------------

   if (deviceExtension->asyncQ.queue)
     {
      ExFreePool(deviceExtension->asyncQ.queue);
      deviceExtension->asyncQ.queue = NULL;
      }

   //--------------------------------------------------------------------------
   // wake up anybody waiting for messages
   //--------------------------------------------------------------------------

   ImbAsyncOsReqWakeup (&deviceExtension->asyncQ);

   //--------------------------------------------------------------------------
   // The following commented statement causes the semaphore count
   // to exceed the semaphore limit and result in a blue screen -Fizal
   //--------------------------------------------------------------------------

   // KeReleaseSemaphore(& deviceExtension->requestSemaphor, 0, 1, FALSE);

   ObDereferenceObject(deviceExtension->threadObject);

   IoUnregisterShutdownNotification(thisDevice);

   //--------------------------------------------------------------------------
   // Close the DPC events sync. event object (pai 12/7)
   //--------------------------------------------------------------------------

   if (deviceExtension->asyncNEventObj != NULL)
	   ZwClose(deviceExtension->asyncNEventObj);

   //--------------------------------------------------------------------------
   // Now we delete the symbolic link - Fizal
   //
   // Set up space for the symbolic link name.
   //--------------------------------------------------------------------------

    RtlInitUnicodeString(&symbolicLinkName, NULL);
    
    symbolicLinkName.MaximumLength = sizeof(UNICODE_SYMBOLIC_LINK_NAME);                                      
    
    symbolicLinkName.Buffer
       = ExAllocatePool(   PagedPool,
                           symbolicLinkName.MaximumLength
                           );

   RtlZeroMemory(symbolicLinkName.Buffer, symbolicLinkName.MaximumLength);

   RtlAppendUnicodeToString(&symbolicLinkName, UNICODE_SYMBOLIC_LINK_NAME);

   t_NTSTATUS = IoDeleteSymbolicLink(&symbolicLinkName);

   if (!NT_SUCCESS(t_NTSTATUS))
      D_DiagMsg1(D_Error,D_DC_Errors,"\"IoDeleteSymbolicLink(...)\" failed, %x",t_NTSTATUS);

   if (symbolicLinkName.MaximumLength != 0)
      ExFreePool(symbolicLinkName.Buffer);
   }

/*F*
//*****************************************************************************
//* ImbUnload(...)
//*****************************************************************************
//* Name:       ImbUnload
//* Purpose:    This is the routine used to orchestrate IMB driver unloading
//* Context:    Called by NT kernel as part of driver unload process.
//* Returns:    Nothing
//* Parameters:
//*             DriverObject    Pointer to driver object created by system.
//* Notes:      none
//*****************************************************************************
*F*/

static VOID ImbUnload(IN struct _DRIVER_OBJECT *DriverObject)
  {
   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"ImbUnload(...)\"");

   //--------------------------------------------------------------------------
   // If not supporting Win 2000, i.e. legacy and/or NT 4.0?
   //--------------------------------------------------------------------------

   #ifdef D_SupportWin2000
      if (!(gulDriverConfiguration & E_DrvConf_Win2000Driver))
   #endif
      gvPrepareToUnload(DriverObject);

   //--------------------------------------------------------------------------
   // Delete the device object.
   //--------------------------------------------------------------------------

   #ifdef D_SupportWin2000
      if (!(gulDriverConfiguration & E_DrvConf_Win2000Driver))
   #endif
        {
         D_DiagMsg0(D_Info,D_DC_MidLevelInfo,"Deleting device object");
         IoDeleteDevice(DriverObject->DeviceObject);
         DriverObject->DeviceObject = NULL;
         }

   //--------------------------------------------------------------------------
   // Close "DebugPrint".
   //--------------------------------------------------------------------------

   D_DiagMsgClose
   }

#endif // Not UWSVR4?

/*F*
//*****************************************************************************
//* ImbOpenCloseDispatch(...)
//*****************************************************************************
//  Name:       ImbOpenCloseDispatch
//  Purpose:    This is the dispatch routine for create/open and close
//              requests.
//  Context:    Called by NT kernel as part of application OpenFile() function
//              for this device.
//  Returns:    STATUS_SUCCESS
//  Parameters:
//              DriverObject    Pointer to driver object created by system.
//              Irp             Pointer to the request packet.
//  Notes:      none
//*****************************************************************************
*F*/

static NTSTATUS ImbOpenCloseDispatch
  (
   IN PDEVICE_OBJECT deviceObject,
   IN PIRP           irp
   )
  {
   UNREFERENCED_PARAMETER(deviceObject);

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"ImbOpenCloseDispatch(...)\"");

   //--------------------------------------------------------------------------
   // Complete the request with successful status.
   //--------------------------------------------------------------------------

   irp->IoStatus.Status       = STATUS_SUCCESS;
   irp->IoStatus.Information  = 0;

   IoCompleteRequest(irp, IO_NO_INCREMENT);

   return(STATUS_SUCCESS);
   }

/*F*
//*****************************************************************************
//* ImbDeviceControl(...)
//*****************************************************************************
//  Name:       ImbDeviceControl
//  Purpose:    This routine is the dispatch routine for device control
//              requests.
//  Context:    Called by NT kernel as part of application DeviceControl()
//              function for this
//                              device.
//  Returns:    NTSTATUS        STATUS_SUCCESS or error code
//  Parameters:
//              DriverObject    Pointer to driver object created by system.
//              Irp             Pointer to the request packet.
//  Notes:      none
//*****************************************************************************
*F*/

static NTSTATUS ImbDeviceControl
  (
   IN PDEVICE_OBJECT deviceObject,
   IN PIRP           irp
   )
  {
   PIO_STACK_LOCATION   irpSp;
   PDEVICE_EXTENSION    deviceExtension;
   UNICODE_STRING       eventName;
   NTSTATUS             status;
   
   DWORD                inputBufferLength;
   DWORD                outputBufferLength;
   DWORD                imbCmd,i;
   PVOID               *ptr;

   //--------------------------------------------------------------------------
   // Get a pointer to the device extension.
   //--------------------------------------------------------------------------

   deviceExtension = deviceObject->DeviceExtension;

   //--------------------------------------------------------------------------
   // Initialize the returned Information field (actually response length).
   //--------------------------------------------------------------------------

   irp->IoStatus.Information = 0;

   //--------------------------------------------------------------------------
   // Get a pointer to the current parameters for this request.  The
   // information is contained in the current stack location.
   //--------------------------------------------------------------------------

   irpSp = IoGetCurrentIrpStackLocation(irp);

   inputBufferLength  = irpSp->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;

   //--------------------------------------------------------------------------
   // Switch on the device control subfunction that is being performed by the
   // requestor.
   //--------------------------------------------------------------------------

   imbCmd = irpSp->Parameters.DeviceIoControl.IoControlCode;

   switch (imbCmd)
     {
      //-----------------------------------------------------------------------
      // UnixWare SVR4?
      //-----------------------------------------------------------------------

      #ifdef UWSVR4

         case IOCTL_IMB_POLL_ASYNC:

            //-----------------------------------------------------------------
            // Start the message poller - implemented in imb_unix.c
            //-----------------------------------------------------------------

            AsyncMesgPoller(deviceExtension); //parameter to be passed
         break;

      #endif
   
      case IOCTL_IMB_GET_ASYNC_MSG:
   
            //-----------------------------------------------------------------
            // Input buffer is too small?
            //-----------------------------------------------------------------

            if (inputBufferLength < sizeof(ImbAsyncRequest))
              {
               status = INVALID_INPUT_BUFFER;

               }
            else
               //--------------------------------------------------------------
               // Output buffer is too small?
               //--------------------------------------------------------------

               if (outputBufferLength < MIN_ASYNC_RESP_SIZE)
                 {
                  status = INVALID_OUTPUT_BUFFER;
                  }
               else
                 {
                  //-----------------------------------------------------------
                  // get his request and try to get a queued message, If none
                  // are available and his timeout is non-zero, the request
                  // will sleep until timeout or message available
                  //-----------------------------------------------------------

                  ImbAsyncRequest *req
                     = (ImbAsyncRequest *)irp->AssociatedIrp.SystemBuffer;

                  ImbAsyncResponse *resp
                     = (ImbAsyncResponse *)irp->AssociatedIrp.SystemBuffer;

                  DWORD length = outputBufferLength;


                  status = ImbAsyncGetMessage(  req,
                                                resp,
                                               &length,
                                               &deviceExtension->asyncQ
                                               );
   
                  irp->IoStatus.Information = length;

                  //-----------------------------------------------------------
                  // NT won't let me reset the object from ring 3. Strange! you
                  // can wait on it in ring 3 but can't set or reset it. Is
                  // this an NT  bug or a feature? Right now it is best to reset
                  // it here. I will modify the code once I know more about this
                  // issue(pai 12/31)
                  //-----------------------------------------------------------

                  //-----------------------------------------------------------
                  // Not UnixWare SVR4?
                  //-----------------------------------------------------------

                  #ifndef UWSVR4

                     if ( (status == STATUS_SUCCESS) && (deviceExtension->asyncNEventObj != NULL) )
                        KeResetEvent(deviceExtension->asyncNEventObj);

                  #endif

                  }
            break;


			
	#ifndef UWSVR4:   

			 case  IOCTL_IMB_REGISTER_ASYNC_OBJ:

				 // upper level code could not create a reference to the 
				 // named event object. So we check if the corresponding
				 // kernel object is already created. If not then we create it.
				 // (pai 12/26/99). This should fix the win200 problem related to
				 // not being able to create the named object during driver load time.

			    if (deviceExtension->asyncNEventObj == NULL)

					{
   
      
  
					RtlInitUnicodeString (&eventName, L"\\BaseNamedObjects\\AsyncSignalEvt");

					deviceExtension->asyncNEventObj
									= IoCreateNotificationEvent(  &eventName,
				       &deviceExtension->asyncNEventHandle
				       );

					if (deviceExtension->asyncNEventObj != NULL)
						{
					
							KeClearEvent (deviceExtension->asyncNEventObj);
							status = STATUS_SUCCESS;
					} else  {
							 status = STATUS_INSUFFICIENT_RESOURCES;

					}



				} else {


							status = STATUS_SUCCESS;

				}




				break;


	#endif


      //-----------------------------------------------------------------------
      // UnixWare SVR4?
      //-----------------------------------------------------------------------

      #ifdef UWSVR4

         case  IOCTL_IMB_REGISTER_ASYNC_OBJ:

            //-----------------------------------------------------------------
            // create the lock for sync. DPC event messages. The application
            // thread in UnixWare will be blocked on this lock till an event is
            // received by the driver. (pai 12/8)
            //-----------------------------------------------------------------

            if (deviceExtension->asyncQSema != 0)
              {
               irp->IoStatus.Information  = 0;
               status = INVALID_INPUT_BUFFER;
               break;
               }

            i = ImbUnixAsyncInit (deviceExtension  );


            *((DWORD *)(irp->AssociatedIrp.SystemBuffer)) = i;

            irp->IoStatus.Information = sizeof(DWORD);

            if (i == STATUS_UNSUCCESSFUL)
                 status = STATUS_UNSUCCESSFUL;
            else
                 status = STATUS_SUCCESS;

            break;

         case  IOCTL_IMB_DEREGISTER_ASYNC_OBJ:
        
             i = *((DWORD *)(irp->AssociatedIrp.SystemBuffer));

             if (i != (DWORD)(deviceExtension->asyncQSema))
              {
               irp->IoStatus.Information  = 0;
               status = INVALID_INPUT_BUFFER;
               break;
               }

            ImbUnixAsyncDeallocate(deviceExtension);
            irp->IoStatus.Information = 0;

            status = STATUS_SUCCESS;
            break;


         case IOCTL_IMB_CHECK_EVENT:

            i =   *((DWORD *) (irp->AssociatedIrp.SystemBuffer));

            if (i != (DWORD) (deviceExtension->asyncQSema))
              {
               irp->IoStatus.Information  = 0;
               status = INVALID_INPUT_BUFFER;
               break;
               }

            //-----------------------------------------------------------------
            // app thread gets blocked for async. events here till an event is
            // received by the driver.
            //-----------------------------------------------------------------
       
            ImbUnixAsyncLock(deviceExtension);
            irp->IoStatus.Information = 0;
            status = STATUS_SUCCESS;
            break;

      #endif // UnixWare SVR4?

      case IOCTL_IMB_SEND_MESSAGE:

         //--------------------------------------------------------------------
         // Input buffer is too small?
         //--------------------------------------------------------------------

         if (inputBufferLength < MIN_IMB_REQ_BUF_SIZE)
           {
            status = INVALID_INPUT_BUFFER;

            }
         else
            //-----------------------------------------------------------------
            // Output buffer is too small?
            //-----------------------------------------------------------------

            if (outputBufferLength < MIN_IMB_RESP_BUF_SIZE)
              {
               status = INVALID_OUTPUT_BUFFER;

               }
            else
              {
               //--------------------------------------------------------------
               // Let the command processing check the request contents.
               //--------------------------------------------------------------

               status = STATUS_PENDING;
               }
         break;

      case IOCTL_IMB_SHUTDOWN_CODE:

        {
         ShutdownCmdBuffer *shutdownCmd
            = (ShutdownCmdBuffer *)irp->AssociatedIrp.SystemBuffer;

         //--------------------------------------------------------------------
         // Check input parameters.
         //--------------------------------------------------------------------

         if (inputBufferLength < sizeof(ShutdownCmdBuffer))
           {
            status = INVALID_INPUT_BUFFER;
            break;
            }

         //--------------------------------------------------------------------
         // Guarentee valid code.
         //--------------------------------------------------------------------

         if (   shutdownCmd->code != SD_POWER_OFF
             && shutdownCmd->code != SD_RESET
             && shutdownCmd->code != SD_NO_ACTION
             )
           {
            deviceExtension->shutdownCode = SD_NO_ACTION;
            status = INVALID_INPUT_BUFFER;
            break;
            }

         deviceExtension->shutdownCode = shutdownCmd->code;
         deviceExtension->delayTime    = shutdownCmd->delayTime;

         status = STATUS_SUCCESS;
         }

      break;

      //-----------------------------------------------------------------------
      // UnixWare SVR4?
      //-----------------------------------------------------------------------

      #ifndef UWSVR4

         case IOCTL_IMB_MAP_MEMORY :

            status = MapTheMemory(  deviceObject,
                                    irp->AssociatedIrp.SystemBuffer,
                                    inputBufferLength,
                                    outputBufferLength
                                    );

            if (NT_SUCCESS(status))
              {
               //---------------------------------------------------------------
               // Success! Set the following to sizeof(PVOID) to
               // indicate we're passing valid data back.
               //---------------------------------------------------------------

               irp->IoStatus.Information = sizeof(PVOID);
               }
            else
              {
               status = STATUS_INVALID_PARAMETER;
               }
            break;

         case IOCTL_IMB_UNMAP_MEMORY:

            if (inputBufferLength >= sizeof(PVOID))
              {
               status = ZwUnmapViewOfSection
                             ((HANDLE) -1,
                              *((PVOID *)irp->AssociatedIrp.SystemBuffer)
                              );
               }
            else
              {
               status = STATUS_INSUFFICIENT_RESOURCES;
               }
            break;

      #endif // UnixWare SVR4?

      default:
         status = INVALID_DRIVER_IOCTL;
         break;
      }

   //--------------------------------------------------------------------------
   // Finish up the request
   //--------------------------------------------------------------------------

   irp->IoStatus.Status = status; 

   if (status == STATUS_PENDING)
     {
      //-----------------------------------------------------------------------
      // Not UnixWare SVR4?
      //-----------------------------------------------------------------------

      #ifndef UWSVR4

         //--------------------------------------------------------------------
         // Queue the request for further processing by the driver thread
         //--------------------------------------------------------------------

         IoMarkIrpPending(irp);

         ExInterlockedInsertTailList( &deviceExtension->queueListEntry,
                                      &irp->Tail.Overlay.ListEntry,
                                      &deviceExtension->queueSpinLock
                                      );

         KeReleaseSemaphore(  &deviceExtension->dispatchSemaphor,
                              (KPRIORITY) 0,
                              1,
                              FALSE
                              );

      #else // UnixWare SVR4

         ImbProcessRequest(irp, deviceExtension);

         status = irp->IoStatus.Status;

      #endif // Not UnixWare SVR4?

      } else
           {
            //-----------------------------------------------------------------
            // If we're not defering processing, complete the I/O immediately
            //-----------------------------------------------------------------

            IoCompleteRequest(irp, IO_NO_INCREMENT);
            }

   return status;
   }

//*****************************************************************************
//* Not UnixWare SVR4?
//*****************************************************************************

#ifndef UWSVR4

/*F*
//*****************************************************************************
//* RequestProcessingThread(...)
//*****************************************************************************
//
//  Name:       RequestProcessingThread 
//  Purpose:    This is the code executed by the request processing system
//              thread.  It is created when  mismic port driver initializes.
//              This thread loops forever (or until a flag is set telling the
//              thread to kill itself) processing BMC request packets put into
//              the queue by the dispatch routine. For each packet, this thread
//              calls appropriate routines to process the request, and then
//              sets the completion status in the irp packet.
//                        
//  Context:  request processing. Scheduled by OS in preemptive manner
//  Returns:   none
//  Parameters: 
//              context  pointer to IMB device object 
//
//  Notes:      none
//*****************************************************************************
*F*/

static VOID RequestProcessingThread(PVOID Context)
  {
   PDEVICE_OBJECT    deviceObject = Context;
   PDEVICE_EXTENSION deviceExtension
                        = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;
   PIRP              irp;
   PLIST_ENTRY       request;
   NTSTATUS          status;
   LARGE_INTEGER     timeOutValue;

   //--------------------------------------------------------------------------
   // Set thread priority to lowest realtime level.
   //--------------------------------------------------------------------------

   KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);

   //--------------------------------------------------------------------------
   // setup the timer
   //--------------------------------------------------------------------------

   timeOutValue.u.HighPart = -1;
   timeOutValue.u.LowPart  = SMS_CHECK_PERIOD;

   while(1)
     {
      //-----------------------------------------------------------------------
      // Wait for a request from the dispatch routines.
      // KeWaitForSingleObject won't return error here - this thread
      // isn't alertable and won't take APCs, and we're not passing in
      // a timeout.
      //-----------------------------------------------------------------------

      status = KeWaitForSingleObject(  &deviceExtension->dispatchSemaphor,
                                       UserRequest,
                                       KernelMode,
                                       FALSE,
                                       &timeOutValue
                                       );

      //-----------------------------------------------------------------------
      // If we're told to unload, terminate this thread - never returns
      //-----------------------------------------------------------------------

      if (deviceExtension->unloadingDriver)
         PsTerminateSystemThread(STATUS_SUCCESS);

      //-----------------------------------------------------------------------
      // Check for work to do
      //-----------------------------------------------------------------------

      if (!IsListEmpty(&deviceExtension->queueListEntry))
        {
         request
            = ExInterlockedRemoveHeadList(   &deviceExtension->queueListEntry,
                                             &deviceExtension->queueSpinLock
                                             );

         irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);

         ImbProcessRequest(irp, deviceExtension);
         }

      //-----------------------------------------------------------------------
      // Check for Async SMS messages to retrieve and queue
      //-----------------------------------------------------------------------

      ImbAsyncMessageProcess(deviceExtension);
      }
   }

#endif   // Not UnixWare SVR4?

//*****************************************************************************
//* ImbProcessRequest(...)
//*****************************************************************************

static void ImbProcessRequest(PIRP  irp, PDEVICE_EXTENSION deviceExtension)
  {
   PIO_STACK_LOCATION   irpSp;

   ImbRequestBuffer  *imbReq
      = (ImbRequestBuffer *)irp->AssociatedIrp.SystemBuffer;

   ImbResponseBuffer *imbResp
      = (ImbResponseBuffer *)irp->AssociatedIrp.SystemBuffer;

   DWORD          imbCmd;
   IMB_STATUS     status;
   DWORD          inputBufferLength;
   DWORD          outputBufferLength;

   //--------------------------------------------------------------------------
   // Setup the timer for this request and get the request semaphore
   //--------------------------------------------------------------------------

   ImbOsStartReq(imbReq->timeOut, deviceExtension);
      
   irpSp = IoGetCurrentIrpStackLocation(irp);

   inputBufferLength  = irpSp->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;

   //--------------------------------------------------------------------------
   // Switch on the device control subfunction that is being performed by the
   // requestor.
   //--------------------------------------------------------------------------

   imbCmd = irpSp->Parameters.DeviceIoControl.IoControlCode;

   switch (imbCmd)
     {

      case IOCTL_IMB_SEND_MESSAGE:

         //--------------------------------------------------------------------
         // We've got an IMB message to send
         // Check the request
         //--------------------------------------------------------------------

//       if (  (DWORD)(imbReq->req.dataLength + MIN_IMB_REQ_BUF_SIZE)
//           > inputBufferLength
		if((DWORD)( inputBufferLength > MAX_BUFFER_SIZE))
           {
            status = INVALID_INPUT_BUFFER;
            }
         else 
           {
            DWORD respLength = outputBufferLength - MIN_IMB_RESP_BUF_SIZE;

            //-----------------------------------------------------------------
            // Do the work of sending the request
            //-----------------------------------------------------------------

            status = SendImbRequest(   imbReq, 
                                       imbResp,
                                       &respLength,
                                       deviceExtension
                                       );

            if (status == IMB_SUCCESS)
              {
               irp->IoStatus.Information = respLength;
               }
            }

         break;

      default:
         status = INVALID_INPUT_BUFFER;
         break;
      }

   //--------------------------------------------------------------------------
   // We're done processing the request, complete the I/O request
   //--------------------------------------------------------------------------

   if (status != IMB_SUCCESS)
      irp->IoStatus.Information = 0;

   irp->IoStatus.Status = status;

   ImbOsEndReq(deviceExtension);

   IoCompleteRequest(irp, IO_NO_INCREMENT);
   }



//*****************************************************************************
//* Not UnixWare SVR4?
//*****************************************************************************

#ifndef UWSVR4 // Implemented in imb_unix.c

//*****************************************************************************
//* IMB messaging support routines
//*****************************************************************************

/*F*
//*****************************************************************************
//* ImbOsStartReq(...)
//*****************************************************************************
//  Name:       ImbOsStartReq
//  Purpose:    This function takes care of OS Specific Serialization
//              of BMC access and starts the Timeout timer.  Initialize and
//              start the request timer, also, get the request semaphore to
//              avoid collisions.
//  Context:    called by functions which access the BMC
//  Returns:    void
//  Parameters: 
//    timeOut Timeout interval to be set in uSec
//    context  OS Specific context  
//  Notes:      none
//*****************************************************************************
*F*/

void ImbOsStartReq
  (
   DWORD    timeOut,    // Units in usec
   void    *context
   )
  {
   LARGE_INTEGER     timeOutValue;
   PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) context;

   //--------------------------------------------------------------------------
   // acquire the request semaphore
   //--------------------------------------------------------------------------

   (void) KeWaitForSingleObject( &deviceExtension->requestSemaphor,
                                 UserRequest,
                                 KernelMode,
                                 FALSE,
                                 NULL
                                 );

   //--------------------------------------------------------------------------
   // Setup the timer.
   //--------------------------------------------------------------------------

   timeOutValue.u.HighPart = -1;

   timeOutValue.u.LowPart
      = -((int)((timeOut <= 0) ? DEFAULT_MESSAGE_TIMEOUT : timeOut));

   timeOutValue.u.LowPart *= 10; // Convert to 100 nsec units

   deviceExtension->timeOutFlag = 1;

   KeSetTimer( &deviceExtension->mesgTimer,
               timeOutValue,
               &deviceExtension->mesgTimerDpc
               );
   }

/*F*
//*****************************************************************************
//* ImbOsEndReq(...)
//*****************************************************************************
//  Name:       ImbOsEndReq
//  Purpose:    This function takes care of OS Specific Serialization
//              of BMC access and kills the Timeout timer.  Cancel a pending
//              request timeout and release the request semaphore
//  Context:    called by functions which access the BMC
//  Returns:    void
//  Parameters: 
//    context  OS Specific context  
//  Notes:      none
//*****************************************************************************
*F*/

void ImbOsEndReq(void *context)
  {
   PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) context;

   if (deviceExtension->timeOutFlag)
     {
      KeCancelTimer(&deviceExtension->mesgTimer);
      deviceExtension->timeOutFlag = 0;
      }

   KeReleaseSemaphore(  &deviceExtension->requestSemaphor,
                        (KPRIORITY)0,
                        1,
                        FALSE
                        );
   }

#endif   // Not UnixWare SVR4?

/*F*
//*****************************************************************************
//* ImbShutdown(...)
//*****************************************************************************
//  Name:       ImbShutdown
//  Purpose:    This is the dispatch routine for processing shutdown request.
//  Context:    Called by operating system during system exit.
//  Returns:    STATUS_SUCCESS
//  Parameters:
//              driverObject    Pointer to driver object created by system.
//              irp           Pointer to the request packet (ignored).
//  Notes:      none
//*****************************************************************************
*F*/

static NTSTATUS ImbShutdown(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
  {
   NTSTATUS          status;
   PDEVICE_EXTENSION deviceExtension;

   D_DiagMsg0(D_Info,D_DC_HighLevelInfo,"\"ImbShutdown(...)\"");

   deviceExtension = deviceObject->DeviceExtension;

   switch (deviceExtension->shutdownCode)
     {
      case SD_POWER_OFF:
      case SD_RESET:
      
         //--------------------------------------------------------------------
         // Get the request semaphore, to  avoid colliding with the
         // main request thread. Also starts the request timer
         //--------------------------------------------------------------------

         ImbOsStartReq(DEFAULT_MESSAGE_TIMEOUT, deviceExtension);

         //--------------------------------------------------------------------
         // Execute the action
         //--------------------------------------------------------------------

         ImbShutdownSystem(   deviceExtension->shutdownCode,
                              deviceExtension->delayTime,
                              deviceExtension
                              );

         //--------------------------------------------------------------------
         // Release the request semaphore.
         //--------------------------------------------------------------------

         ImbOsEndReq(deviceExtension);

         status = STATUS_SUCCESS;
         break;

      case SD_NO_ACTION:

         status = STATUS_SUCCESS;
         break;

      default:
         status = INVALID_INPUT_BUFFER;
         break;
      }

   return status;
   }

/*F*
//*****************************************************************************
//* DelayRetryFunc(...)
//*****************************************************************************
//  Name:       DelayRetryFunc
//  Purpose:    This function inserts a delay into the execution of 
//          current thread
//  Context:    called by functions which access the BMC
//  Returns:    void
//  Parameters: 
//    retryCount Number of times to be spin before sleeping
//    delayTime Delay interval to be used in uSec
//    context  OS Specific context  
//  Notes:      none
//*****************************************************************************
*F*/
VOID DelayRetryFunc
  (
   DWORD retryCount,    // number of times we've been called, not used here
   DWORD delayTime,     // time to delay (in uSecs)
   void *context        // OS dependent handle,  not used here
   )
  {
   if (retryCount < NO_RETRY_DELAY_COUNT && retryCount != FORCE_DELAY )
      return;

   //--------------------------------------------------------------------------
   // Not UnixWare SVR4?
   //--------------------------------------------------------------------------

   #ifndef UWSVR4
     {
      //-----------------------------------------------------------------------
      // For NT we always delay the specified amount
      //-----------------------------------------------------------------------

      LARGE_INTEGER  timeOutValue;

      timeOutValue.u.HighPart = -1;
      timeOutValue.u.LowPart  = -((int)(delayTime * 10));   // 100ns units

      KeDelayExecutionThread(KernelMode, FALSE, &timeOutValue);
      }

   #else // UnixWare SVR4?
     {
      DWORD clockticks;

      //-----------------------------------------------------------------------
      // convert delaytime to clockticks, 1 clocktick is 10 milli sec
      //-----------------------------------------------------------------------

      clockticks = delayTime/(10 * 1000);

      //-----------------------------------------------------------------------
      // if we are asked to wait less than a clocktick wait at least
      // a clocktick
      //-----------------------------------------------------------------------

      delay(clockticks ? clockticks : 1);
      }

   #endif   // Not UnixWare SVR4?
}

/*F*
//*****************************************************************************
//* ImbReqTimedOut(...)
//*****************************************************************************
//  Name:       ImbReqTimedOut
//  Purpose:    This function determines if the Timeout Timer has timed out.
//              This function tells OS independent code if the request timer
//              for the current in-progress request has expired
//  Context:    called by functions which use the Timeout Timer.
//  Returns:    This function returns TRUE if the Timeout has expired
//          else returns FALSE.
//  Parameters: 
//    context  OS Specific context  
//  Notes:      none
//*****************************************************************************
*F*/

BOOL ImbReqTimedOut(void *context)
  {
   return (((PDEVICE_EXTENSION) context)->timeOutFlag == 0);
   }

//*****************************************************************************
//* GetNextImbSeq(...)
//*****************************************************************************
/*F*
//  Name:       GetNextImbSeq
//  Purpose:    This function gets the next Imb Sequence number.  Return the
//              next sequence number to be used when sending an IMB message.
//              Although the IMB sequence number is only a 6 bit field, we
//              ignore that and treat it as a BYTE. The IMB code wil mask it
//              appropriately.
//  Context:    called by functions sending IMB commands to devices
//  Returns:    This function returns the next possible sequence number
//  Parameters: 
//    context  OS Specific context  
//  Notes:      The Sequence number takes values from 0-255
//*****************************************************************************
*F*/

BYTE GetNextImbSeq(void *context)
  {
   return ((PDEVICE_EXTENSION)context)->imbSeqNo++;
   }

/*F*
//*****************************************************************************
//* ImbIf(...)
//*****************************************************************************
//  Name:       ImbIf
//  Purpose:    This function returns a pointer to the currently detected
//              Hardware Interface.  Return a pointer to the IMB interface's
//              function vector
//  Context:    called by  routines Interfacing to the SMIC or KCS layer
//  Returns:    Returns a pointer to the currently detected Hardware 
//          Interface
//  Parameters: 
//    context  OS Specific context  
//  Notes:      
//*****************************************************************************
*F*/

ImbInterface *ImbIf(void *context)
  {
   return ((PDEVICE_EXTENSION)context)->imbIf;
   }

//*****************************************************************************
//* TimerDpcFunc(...)
//*
//* Timer callback function Used by the request timer
//*****************************************************************************

static VOID TimerDpcFunc
  (
   IN PKDPC Dpc,
   IN PDEVICE_OBJECT DeviceObject,
   IN PVOID SystemContext1,
   IN PVOID SystemContext2
   )
  {
   PDEVICE_EXTENSION deviceExtension;

   //--------------------------------------------------------------------------
   // Get the device extension
   //--------------------------------------------------------------------------

   deviceExtension = DeviceObject->DeviceExtension;

   if (deviceExtension->timeOutFlag)
      deviceExtension->timeOutFlag = 0;
   }

//*****************************************************************************
//* Async messaging support
//*****************************************************************************

/*F*
//*****************************************************************************
//* ImbAsyncQueue(...)
//*****************************************************************************
//  Name:       ImbAsyncQueue
//  Purpose:    This function returns a pointer to the Async queue info
//  Context:    called by functions manipulating the Async Q
//  Returns:    returns a pointer to the Async queue info
//  Parameters: 
//    context  OS Specific context  
//  Notes:      none
//*****************************************************************************
*F*/

ImbAsyncQueueInfo *ImbAsyncQueue(void *context)
  {
   return (&((PDEVICE_EXTENSION)context)->asyncQ);
   }

//*****************************************************************************
//* Not UnixWare SVR4?
//*****************************************************************************

#ifndef UWSVR4 // implemented in imb_unix.c

/*F*
//*****************************************************************************
//* ImbAsyncOsInit(...)
//*****************************************************************************
//  Name:       ImbAsyncOsInit
//  Purpose:    This function initializes the OS dependent parts of the 
//              Async Queue
//  Context:    called by Async Initialization functions
//  Returns:    void
//  Parameters: 
//    asyncQ   pointer to Async Queue Specific Info
//  Notes:      none
//*****************************************************************************
*F*/

void ImbAsyncOsInit(ImbAsyncQueueInfo *asyncQ)
  {
   //--------------------------------------------------------------------------
   // init the semaphore
   //--------------------------------------------------------------------------

   KeInitializeSemaphore(&asyncQ->syncSem, 1L, 1L);
   KeInitializeEvent(&asyncQ->waitEvent, NotificationEvent, FALSE);
   }

/*F*
//*****************************************************************************
//* ImbAsyncOsReqWait
//*****************************************************************************
//  Name:       ImbAsyncOsReqWait
//  Purpose:    This function makes the incoming thread wait for a new 
//              SMS message to come in
//  Context:    called by Async functions
//  Returns:    void
//  Parameters: 
//    asyncQ   pointer to Async Queue Specific Info
//    timeOut Time to wait before timing out.
//  Notes:      none
//*****************************************************************************
*F*/

void ImbAsyncOsReqWait(ImbAsyncQueueInfo *asyncQ, DWORD timeOut)
  {
   NTSTATUS       status;
   LARGE_INTEGER  timeOutValue;

   //--------------------------------------------------------------------------
   // Setup the timer, convert from uS to 100 nS units
   //--------------------------------------------------------------------------

   timeOutValue.u.HighPart = -1;
   timeOutValue.u.LowPart  = -((int) timeOut * 10);

   KeClearEvent(&asyncQ->waitEvent);

   status = KeWaitForSingleObject(  &asyncQ->waitEvent,
                                    UserRequest,
                                    KernelMode,
                                    FALSE,
                                    &timeOutValue
                                    );
   }

//*****************************************************************************
//* ImbAsyncOsReqWakeup(...)
//*****************************************************************************
/*F*
//  Name:       ImbAsyncOsReqWakeup
//  Purpose:    This function wakes up all sleeping threads waiting for a 
//              message to come in
//  Context:    called by Async  functions
//  Returns:    void
//  Parameters: 
//    asyncQ   pointer to Async Queue Specific Info
//  Notes:      none
//*****************************************************************************
*F*/

void ImbAsyncOsReqWakeup (ImbAsyncQueueInfo *asyncQ)
  {
   (void) KeSetEvent(& asyncQ->waitEvent, (KPRIORITY) 0, FALSE);
   }

//*****************************************************************************
//* ImbAsyncOsNewWakeup(...)
//*
//* NEW async wakeup pai (9/8)
//*****************************************************************************

void ImbAsyncOsNewWakeup(void *context)
  {
   PDEVICE_EXTENSION deviceExtension;
   UCHAR             i;
       
   deviceExtension = (PDEVICE_EXTENSION)context;

   KeWaitForSingleObject(  &deviceExtension->asyncQSemaphor,
                           UserRequest,
                           KernelMode,
                           FALSE,
                           NULL
                           );
/*
   for (i = 0; i < MAX_EVENT_OBJ_NUMBER; ++i)
     {
      if (deviceExtension->asyncEventObj[i] != 0)
         KeSetEvent(deviceExtension->asyncEventObj[i],0, FALSE);
      }  
*/
   if (deviceExtension->asyncNEventObj != NULL)
	KeSetEvent (deviceExtension->asyncNEventObj, 0, FALSE);


   KeReleaseSemaphore(  &deviceExtension->asyncQSemaphor,
                        (KPRIORITY)0,
                        1,FALSE
                        );
   }

/*F*
//*****************************************************************************
//* ImbAsyncOsLock
//*****************************************************************************
//  Name:       ImbAsyncOsLock
//  Purpose:    This function obtains exclusive use of the async message 
//              queue
//  Context:    called by Async  functions
//  Returns:    returns true if able to obtain exclusive use of Async Q
//  Parameters: 
//    asyncQ   pointer to Async Queue Specific Info
//  Notes:      none
//*****************************************************************************
*F*/

BOOL ImbAsyncOsLock(ImbAsyncQueueInfo *asyncQ)
  {
   NTSTATUS status = KeWaitForSingleObject(  &asyncQ->syncSem,
                                             UserRequest,
                                             KernelMode,
                                             FALSE,
                                             NULL
                                             );

   return (status == STATUS_SUCCESS);
   }

/*F*
//*****************************************************************************
//* ImbAsyncOsUnLock
//*****************************************************************************
//  Name:       ImbAsyncOsUnLock
//  Purpose:    This function releases exclusive use of the async message 
//          queue
//  Context:    called by Async  functions
//  Returns:    returns true if able to release exclusive use of Async Q
//  Parameters: 
//    asyncQ   pointer to Async Queue Specific Info
//  Notes:      none
//*****************************************************************************
*F*/

BOOL ImbAsyncOsUnLock(ImbAsyncQueueInfo *asyncQ)
  {
   KeReleaseSemaphore(&asyncQ->syncSem, (KPRIORITY) 0, 1, FALSE);

   return TRUE;
   }

#endif   // Not UnixWare SVR4?
