www.pudn.com > PROFIBUS_DP_protocol_source.ZIP > driverpn.c, change:2004-03-17,size:83168b


//----------------------------------------------------------------------------- 
// $Id: driverpnp.c,v 1.0.0                                         2004/01/13 
//----------------------------------------------------------------------------- 
// 
//      ProfiM - PROFIBUS MASTER DRIVER FOR WINDOWS NT/2000 
// 
// Author:   
//      Pavel Trnka, CTU FEE 
//      trnkap@seznam.cz 
// With help and advices from: 
//      Ing. Petr Smolik, CTU FEE 
//      Ing. Pavel Pisa, CTU FEE 
//      Ing. Pavel Burget, CTU FEE 
// 
//----------------------------------------------------------------------------- 
// 
// Popis: 
// ------ 
//   Cast ovladace pro preklad s podporou PnP. Do projektu je vkladana pokud 
// je nadefinovano falesne makro PnP (vardef.h). 
//   Zajistuje start ovladace (DriverEntry), ziskani hardwarovych prostredku 
// (AddDevice), zpracovavani PnP zprav (DispatchPnP) a vsechny potrebne funkce 
// pro zajisteni PnP kompatibilitu ovladace. 
// 
//----------------------------------------------------------------------------- 
//----------------------------------------------------------------------------- 
 
#include "DriverPnP.h" 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// Unicode String Manipulation routine 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS MyAllocUnicodeString( PUNICODE_STRING UStr, 
                               PUNICODE_STRING InUStr, 
                               USHORT Len ) 
{ 
  NTSTATUS  Status; 
  ULONG     Size; 
  if ( !Len && InUStr ) 
  { 
    Len = InUStr->Length; 
  } 
  RtlInitUnicodeString( UStr, NULL ); 
  if ( Len ) 
  { 
    Size = Len + 2; 
    UStr->Buffer = ExAllocatePool( PagedPool, Size ); 
    if ( !UStr->Buffer ) 
      return STATUS_UNSUCCESSFUL; 
    RtlZeroMemory( UStr->Buffer, Size ); 
    UStr->MaximumLength = Len; 
    if ( InUStr ) 
      RtlCopyUnicodeString( UStr, InUStr ); 
  } 
  return STATUS_SUCCESS; 
} 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  GetRegistryDWord 
// 
//    Reads a registry key value from an already opened registry key 
// 
//  INPUTS: 
//  Handle    - handle to the opened registry key 
//  KeyNameString - WCHAR string to the desired key 
//  OUTPUTS: 
//  Data    - buffer for the data 
 
// 
//  RETURNS: 
//  NT Status code 
// 
//  IRQL: 
//  IRQL PASSIVE_LEVEL 
// 
/////////////////////////////////////////////////////////////////////////////// 
BOOLEAN 
GetRegistryDWord ( 
    IN HANDLE Handle, 
    IN PWCHAR KeyNameString, 
    IN PULONG Data 
    ) 
{ 
  UNICODE_STRING   keyName; 
  ULONG            length; 
  PKEY_VALUE_FULL_INFORMATION fullInfo; 
  NTSTATUS         status = STATUS_INSUFFICIENT_RESOURCES; 
  ULONG            DataLength=4; 
 
  RtlInitUnicodeString (&keyName, KeyNameString); 
 
  length = sizeof(KEY_VALUE_FULL_INFORMATION)+keyName.Length+4+DataLength; 
  fullInfo = ExAllocatePool(PagedPool, length);  
 
  if (fullInfo)  
  { 
    status = ZwQueryValueKey (Handle,&keyName,KeyValueFullInformation, 
        fullInfo,length,&length); 
   
    if (NT_SUCCESS(status))  
    { 
      // If there is enough room in the data buffer, copy the output 
      if ( DataLength >= fullInfo->DataLength )  
      { 
        RtlCopyMemory (Data,((PUCHAR) fullInfo) + fullInfo->DataOffset,  
           fullInfo->DataLength); 
        ExFreePool(fullInfo);            
        return TRUE; 
      } 
    } 
     
    ExFreePool(fullInfo); 
  } 
 
  return FALSE; 
} 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////// 
// 
//                             PCI routines 
// 
/////////////////////////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////////////////////////////                                 
 
                                
 
#define PCI_ANY_ID 0xffff 
typedef struct pci_device_id { 
  USHORT vendor; 
  USHORT device; 
  USHORT subvendor; 
  USHORT subdevice; 
  USHORT class_val,clask_mask; 
  ULONG  driver_data; 
} pci_device_id_t; 
 
//----------------------------------------------------------------------------- 
 
 
static struct pci_device_id pb_pci_tbl[] /*__devinitdata*/ = { 
  /* Advantech PCI-1602A, negated DTR */ 
  { /* PCI_VENDOR_ID */ 0x13fe, /* PCI_DEVICE_ID */ 0x1600, 
    /* PCI_VENDOR_ID */ PCI_ANY_ID, /* PCI_SUBDEVICE_ID */ PCI_ANY_ID, 
    0, 0, 0x16954000|U950PCI_CHOPT_TXDTRNEG|U950PCI_CHOPT_HSPDOSC}, 
  /* Tedia PCI-1482 */ 
  { /* PCI_VENDOR_ID */ 0x1415, /* PCI_DEVICE_ID */ 0x950A, 
    /* PCI_VENDOR_ID */ PCI_ANY_ID, /* PCI_SUBDEVICE_ID */ PCI_ANY_ID, 
    0, 0, 0x16954000|U950PCI_CHOPT_RXDONRI|U950PCI_CHOPT_HSPDOSC}, 
  /* Tedia PCI-1482 with PiKRON signature */ 
  { /* PCI_VENDOR_ID */ 0x1760, /* PCI_DEVICE_ID */ 0x8004, 
    /* PCI_VENDOR_ID */ PCI_ANY_ID, /* PCI_SUBDEVICE_ID */ PCI_ANY_ID, 
    0, 0, 0x16954000|U950PCI_CHOPT_RXDONRI}, 
  { 0, } /* terminate list */ 
}; 
 
//----------------------------------------------------------------------------- 
 
BOOLEAN FindPciHWID(PWCHAR HWID, 
        OUT pci_device_id_t **Ppci_device_id 
             ) 
{ 
  PWCHAR pwcH, pwcM; 
  WCHAR  fldName; 
  USHORT vendorID=0; 
  USHORT deviceID=0; 
  USHORT subvendorID=0; 
  USHORT subdeviceID=0; 
  ULONG  val, i, c; 
  static WCHAR PciHWIDMask[]=L"PCI\\VEN_vvvv&DEV_xxxx&SUBSYS_sssswwww"; 
  pci_device_id_t *device_id; 
 
  *Ppci_device_id=NULL; 
  if(HWID==NULL) return FALSE; 
 
  pwcH=HWID; 
  pwcM=PciHWIDMask; 
  while(*pwcH&&*pwcM) { 
    if(*pwcH==*pwcM){ 
      pwcH++; pwcM++; 
      continue; 
    } 
    fldName=*pwcM; 
    if((fldName!='v')&&(fldName!='x')&& 
       (fldName!='s')&&(fldName!='w')) break; 
    val=0; 
    for(i=4;i--;){ 
      c=*(pwcH++); pwcM++; 
      if(c<='9') c-='0'; 
      else if(c<='F') c=c-'A'+10; 
      else if(c<='f') c=c-'a'+10; 
      if(c>15) {val=0;break;} 
      val<<=4; 
      val+=c; 
    } 
    switch (fldName) { 
      case 'v': 
        vendorID=(USHORT)val; 
  break; 
      case 'x': 
        deviceID=(USHORT)val; 
  break; 
      case 'w': 
        subvendorID=(USHORT)val; 
  break; 
      case 's': 
        subdeviceID=(USHORT)val; 
  break; 
    } 
  } 
#if DBG 
  DbgPrint("PB: Ven %04X Dev %04X SubVen %04X SubDev %04X\n",  
                 vendorID,deviceID,subvendorID,subdeviceID); 
#endif 
 
  for(device_id=pb_pci_tbl;device_id->vendor;device_id++){ 
    if((device_id->vendor!=PCI_ANY_ID)&&(device_id->vendor!=vendorID)) continue; 
    if((device_id->device!=PCI_ANY_ID)&&(device_id->device!=deviceID)) continue; 
    if((device_id->subvendor!=PCI_ANY_ID)&&(device_id->vendor!=subvendorID)) continue; 
    if((device_id->subdevice!=PCI_ANY_ID)&&(device_id->device!=subdeviceID)) continue; 
    *Ppci_device_id=device_id; 
    return TRUE; 
  } 
 
  return FALSE; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////// 
 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  DriverEntry 
// 
//      This routine is called when the driver is first loaded.   
// 
//  INPUTS: 
// 
//      DriverObj - Address of the DRIVER_OBJECT created by NT for this driver. 
// 
//      RegistryPath - UNICODE_STRING which represents this drivers KEY in the 
//                   Registry.   
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      STATUS_SUCCESS. Otherwise an error indicating why the driver could not 
//                    Load. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObj, PUNICODE_STRING RegistryPath ) 
{ 
  DbgPrint( "ProfiM PnP: Enter the driver!\n" ); 
  DbgPrint( "ProfiM PnP: Version 0.025 with PnP support (%s  %s)\n",  
            __TIME__,  
            __DATE__ ); 
  DbgPrint( "ProfiM PnP: Umisteni v registrech: \"%ws\" ", 
            RegistryPath->Buffer ); 
 
 
  // 
  // Establish dispatch entry points for the functions we support 
  // 
  DriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchRoutine; 
  DriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchRoutine; 
  DriverObj->MajorFunction[IRP_MJ_READ] = DispatchRoutine; 
  DriverObj->MajorFunction[IRP_MJ_WRITE] = DispatchRoutine; 
  DriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchRoutine; 
  DriverObj->MajorFunction[IRP_MJ_CLEANUP] = DispatchRoutine; 
 
  // 
  // PnP and Power entry points 
  // 
  DriverObj->MajorFunction[IRP_MJ_PNP] = DispatchPnp; 
  /// DriverObj->MajorFunction[IRP_MJ_POWER]          =  DispatchPower; 
 
  // 
  // WMI entry point 
  // 
  DriverObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchSystemControl; 
 
  // 
  // AddDevice Function 
  // 
  DriverObj->DriverExtension->AddDevice = AddDevice; 
 
  // 
  // Unload function 
  // 
  DriverObj->DriverUnload = UnloadDriver; 
 
  // 
  // Save the registry path for later use. 
  // 
  MyAllocUnicodeString( &PB_RegistryPath, RegistryPath, 0 ); 
 
 
 
  // 
  // Initial reasonable value for driver spinlock 
  // 
  //**    uL_SpinLock_Irql=DISPATCH_LEVEL+1; 
 
 
  DbgPrint( "ProfiM PnP: All initialized!\n" ); 
 
  return( STATUS_SUCCESS ); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  DriverUnload 
// 
//      This routine is our dynamic unload entry point.  We are called here when 
//    the OS wants to unload our driver.  It is our responsibility to release any 
//    resources we allocated. 
// 
//  INPUTS: 
// 
//      DriverObj - Address of our DRIVER_OBJECT. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    No doubt we pool leak at this entry point by not properly returning everything. 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID UnloadDriver( PDRIVER_OBJECT DriverObject ) 
{ 
  PPROFIM_DEVICE_EXTENSION      devExt; 
  PDEVICE_OBJECT                devObj, nextDevObj; 
  IO_RESOURCE_REQUIREMENTS_LIST reqList; 
  NTSTATUS                      code; 
  UNICODE_STRING                linkName; 
  CM_RESOURCE_LIST              returnResources; 
  BOOLEAN                       conflictDetected; 
 
  DbgPrint( "ProfiM PnP: DriverUnload\n" ); 
 
  // 
  // For THIS driver, there will only ever be a single device object. 
  // Because of this, we just get it from the DriverObj.  If this were 
  // a multiple device driver, we would do this in a while loop... 
  // 
  nextDevObj = DriverObject->DeviceObject; 
  while ( nextDevObj ) 
  { 
    devObj = nextDevObj; 
    nextDevObj = devObj->NextDevice; 
 
    // Find ProfiM Device Extension 
    devExt = ( PPROFIM_DEVICE_EXTENSION ) devObj->DeviceExtension; 
 
    if ( !devExt ) 
    { 
      DbgPrint( "PnP: UnloadDriver Error - DeviceExtension is NULL!" ); 
      return; 
    }         
    if ( devExt->magic != PROFIM_MAGIC ) 
    { 
      DbgPrint( "PnP: UnloadDriver Error - Device Extension MAGIC is invalid!" ); 
      return; 
    }       
 
    // Set State 
    devExt->State = STATE_REMOVED; 
 
    // Return ProfiM system resources 
    ReturnResources( devExt ); 
 
    // Destroy DeviceObject 
    RemoveDevice( devObj ); 
  } 
 
  if ( PB_RegistryPath.Buffer ) 
    ExFreePool( PB_RegistryPath.Buffer ); 
} 
 
 
 
 
 
 
 
 
 
VOID  PrintState( PPROFIM_DEVICE_EXTENSION devExt ); 
VOID  RequestIncrement( PPROFIM_DEVICE_EXTENSION devExt ); 
VOID  RequestDecrement( PPROFIM_DEVICE_EXTENSION devExt ); 
VOID  WaitForStop( PPROFIM_DEVICE_EXTENSION devExt ); 
VOID  WaitForRemove( PPROFIM_DEVICE_EXTENSION devExt ); 
VOID ClearQueues( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  /*stub*/ 
}; 
VOID ProcessQueuedRequests( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  /*stub*/ 
}; 
NTSTATUS        StartDevice( IN PPROFIM_DEVICE_EXTENSION devExt, 
                             IN PIO_STACK_LOCATION IrpSp ); 
NTSTATUS        CanStopDevice( PPROFIM_DEVICE_EXTENSION devExt, PIRP Irp ); 
NTSTATUS        CanRemoveDevice( PPROFIM_DEVICE_EXTENSION devExt, PIRP Irp ); 
static NTSTATUS PnpComplete( IN PDEVICE_OBJECT DeviceObject, 
                             IN PIRP Irp, 
                             IN PVOID Context ); 
 
 
static PSTR pnpMinorCodes[] ={"IRP_MN_START_DEVICE", 
                              "IRP_MN_QUERY_REMOVE_DEVICE", 
                              "IRP_MN_REMOVE_DEVICE", 
                              "IRP_MN_CANCEL_REMOVE_DEVICE", 
                              "IRP_MN_STOP_DEVICE", 
                              "IRP_MN_QUERY_STOP_DEVICE", 
                              "IRP_MN_CANCEL_STOP_DEVICE", 
                              "IRP_MN_QUERY_DEVICE_RELATIONS", 
                              "IRP_MN_QUERY_INTERFACE", 
                              "IRP_MN_QUERY_CAPABILITIES", 
                              "IRP_MN_QUERY_RESOURCES", 
                              "IRP_MN_QUERY_RESOURCE_REQUIREMENTS", 
                              "IRP_MN_QUERY_DEVICE_TEXT", 
                              "IRP_MN_FILTER_RESOURCE_REQUIREMENTS", 
                              "***** FUNCTION 0x0e", 
                              "IRP_MN_READ_CONFIG", 
                              "IRP_MN_WRITE_CONFIG", 
                              "IRP_MN_EJECT", 
                              "IRP_MN_SET_LOCK", 
                              "IRP_MN_QUERY_ID", 
                              "IRP_MN_QUERY_PNP_DEVICE_STATE", 
                              "IRP_MN_QUERY_BUS_INFORMATION", 
                              "IRP_MN_DEVICE_USAGE_NOTIFICATION", 
                              "IRP_MN_SURPRISE_REMOVAL", 
                              "IRP_MN_QUERY_LEGACY_BUS_INFORMATION"}; 
 
 
#if PnP_DBG 
VOID            PrintResourceList( PCM_RESOURCE_LIST ); 
VOID            PrintConfig( PPCI_COMMON_CONFIG  configInfo ); 
#endif 
 
// 
// Global variable 
// 
int         ProfiMInstanceCounter = 0; 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  AddDevice 
// 
//      We are called at this entry point by the Plug and Play Manager 
//      to add a Functional Device Object for a Physical Device Object. 
//      Note that we may NOT access the device in this routine, as the 
//      Plug and Play Manager has not yet given us any hardware resoruces. 
//      We get these hardware resources via the IRP_MJ_PNP IRP with 
//      a minor function IRP_MN_START_DEVICE. 
// 
// 
//  INPUTS: 
// 
//      DriverObj - Address of our DRIVER_OBJECT. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS AddDevice( IN PDRIVER_OBJECT DriverObject, 
                    IN PDEVICE_OBJECT PhysicalDeviceObject ) 
{ 
  PPROFIM_DEVICE_EXTENSION  devExt; 
  PDEVICE_OBJECT            functionalDeviceObject; 
  UNICODE_STRING            devName; 
  UNICODE_STRING            linkName; 
  UNICODE_STRING            tempName; 
  NTSTATUS                  code  = STATUS_SUCCESS; 
  HANDLE                    regKeyHandle;   
  KEY_FULL_INFORMATION     *KeyInfo; 
  ULONG                     length; 
  char                      Buffer[256]; 
 
  DbgPrint( "ProfiM PnP: AddDevice: entered for instance %d\n", 
            ProfiMInstanceCounter ); 
 
  PB_DbgPrintL1( "ProfiM PnP: AddDevice: PDO = 0x%0x\n", PhysicalDeviceObject ); 
 
  // Only up to 10 instances are handled  
  if ( ProfiMInstanceCounter >= 10 ) 
  { 
    DbgPrint( "ProfiM PnP: Too many instances, no more added\n" ); 
    return( STATUS_UNSUCCESSFUL ); 
  } 
 
  // 
  // Initialize the UNICODE device name.  This will be the "native NT" name 
  // for our device. 
  // 
 
  RtlInitUnicodeString( &tempName, NT_DEVICE_NAME ); 
  code = MyAllocUnicodeString( &devName, &tempName, (USHORT) (tempName.Length + 4) ); 
  if ( ProfiMInstanceCounter ) 
  { 
    devName.Buffer[devName.Length / 2] = '0' + ProfiMInstanceCounter; 
    devName.Length += 2; 
  } 
  PB_DbgPrintL1( "ProfiM PnP: Ready to Call IoCreateDevice for DevName=\"%ws\" Len=%d\n", 
                 devName.Buffer, 
                 devName.Length ); 
 
 
  PB_DbgPrintL1( "ProfiM PnP: Size of Device Extension = %dB", 
                 sizeof( PROFIM_DEVICE_EXTENSION ) ); 
 
  // 
  // Ask the I/O Manager to create the device object and 
  // device extension.  In PnP terms, this is the FUNCTIONAL 
  // Device Object (FDO) for the device. 
  //     
  code = IoCreateDevice( DriverObject, 
                         sizeof( PROFIM_DEVICE_EXTENSION ), 
                         &devName, 
                         FILE_DEVICE_UNKNOWN, 
                         0, 
                         FALSE, 
                         &functionalDeviceObject ); 
 
 
  if ( !NT_SUCCESS( code ) ) 
  { 
    if ( devName.Buffer ) 
      ExFreePool( devName.Buffer ); 
    DbgPrint( "ProfiM PnP: IoCreateDevice failed. Dev=%ws Status = 0x%0x\n", 
              devName.Buffer, 
              code ); 
    return( STATUS_UNSUCCESSFUL ); 
  }     
 
 
  // 
  // Get a pointer to our device extension 
  // 
  devExt = ( PPROFIM_DEVICE_EXTENSION ) 
           functionalDeviceObject->DeviceExtension; 
 
  if ( !devExt ) 
  { 
    DbgPrint( "ProfiM PnP: AddDevice Error - DeviceExtension is NULL!" ); 
    return STATUS_UNSUCCESSFUL; 
  }         
 
 
  PB_DbgPrintL1( "ProfiM PnP: AddDevice: FDO = 0x%0x\n", 
                 functionalDeviceObject ); 
 
  // 
  // Zero out the device extension.  While not strictly necessary 
  // (the documentation says the device extension is zeroed) it's 
  // better to be safe. 
  // 
  RtlZeroMemory( devExt, sizeof( PROFIM_DEVICE_EXTENSION ) ); 
 
  // 
  // Initialize structure control identificator 
  // 
  devExt->magic = PROFIM_MAGIC;  
  devExt->PB.AllInitialized = FALSE;              
  devExt->ContinueFrame = FALSE; 
 
  // 
  // Save the device object pointer away for future reference 
  // 
  devExt->DeviceObject = functionalDeviceObject; 
 
  // 
  // Save the address of the physical device object away for future reference 
  // 
  devExt->PhysicalDeviceObject = PhysicalDeviceObject; 
 
 
   
  //************************************************     
  // 
  // Get Starup configuration from reqistry 
  // 
  code = GetPnPConfiguration( devExt );   
 
  if ( !NT_SUCCESS( code ) ) 
  { 
    RS_DbgPrint( "PnP: GetConfiguration failed\n" ); 
    return code; 
  }     
   
  //************************************************ 
 
  // 
  //  Clear the Device Initializing bit since the Device Object was created 
  //  outside of DriverEntry. 
  // 
  functionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 
 
  // 
  // Create a familiar name for this device, so that non-kernel mode 
  // programs can open the device. 
  // 
  // NOTE: WDM Drivers on Win98 MUST create this link in the  
  // \DosDevices directory.  \?? will NOT work. 
  // 
 
  RtlInitUnicodeString( &tempName, DOS_DEVICE_NAME ); 
  code = MyAllocUnicodeString( &linkName, &tempName, (USHORT) (tempName.Length + 4) ); 
  if ( ProfiMInstanceCounter ) 
  { 
    linkName.Buffer[linkName.Length / 2] = '0' + ProfiMInstanceCounter; 
    linkName.Length += 2; 
  } 
 
 
  PB_DbgPrintL1( "ProfiM PnP: AddDevice: Dev=%ws Link=%ws\n", 
                 devName.Buffer, 
                 linkName.Buffer ); 
 
  // 
  // IoCreateSymbolicLink IS a WDM function... 
  // 
  code = IoCreateSymbolicLink( &linkName, &devName ); 
 
  if ( !NT_SUCCESS( code ) ) 
  { 
    DbgPrint( "ProfiM PnP: IoCreateSymbolicLink failed.  Status = 0x%x\n", 
              code ); 
 
    code = STATUS_UNSUCCESSFUL; 
 
    // 
    // Clean up the mess 
    // 
    IoDeleteDevice( functionalDeviceObject ); 
    if ( linkName.Buffer ) 
      ExFreePool( linkName.Buffer ); 
    if ( devName.Buffer ) 
      ExFreePool( devName.Buffer );; 
 
    // 
    // Indicate load failure to the I/O manager; driver image is deleted... 
    // 
    return( code ); 
  } 
 
  devExt->link_name = linkName; 
  devExt->ntdev_name = devName; 
 
  // 
  // Ask the I/O Manager to use buffered I/O 
  // 
  functionalDeviceObject->Flags |= DO_BUFFERED_IO; 
 
  // 
  // Set up the "Remove Event" and "Stop Event". 
  // 
  // Note that we can't use an official "Remove Lock" here, because 
  // the Remove Lock related calls are not in WDM. 
  // 
  KeInitializeEvent( &devExt->RemoveEvent, NotificationEvent, FALSE ); 
  // KeInitializeEvent(&devExt->StopEvent, NotificationEvent, TRUE); 
 
  // 
  // Init the count of in-progress I/O requests to zero.  We use this 
  // to keep track of when we can remove the device. 
  // 
  // devExt->OutstandingIO = 0; 
 
  // 
  // Internal device state flags, used for managing PnP state of device 
  // 
  // devExt->Started = FALSE; 
  // devExt->HoldNewRequests = TRUE; 
  // devExt->Removed = FALSE; 
 
  // 
  // Set initial state 
  // 
  devExt->State = STATE_NEVER_STARTED; 
 
  // Initial ProfiM state 
  //**    ul_drv_new_init_state(devExt,2); 
 
  // 
  // Attach our FDO to the underlying PDO 
  // 
  devExt->DeviceToSendIrpsTo = IoAttachDeviceToDeviceStack( functionalDeviceObject, 
                                                            PhysicalDeviceObject ); 
 
  // 
  // If that didn't work... 
  // 
  if ( !devExt->DeviceToSendIrpsTo ) 
  { 
    DbgPrint( "ProfiM PnP: IoAttachDeviceToDeviceStack failed to attach to Target Device" ); 
 
    // 
    // Clean up the mess 
    // 
    IoDeleteDevice( functionalDeviceObject ); 
 
    // 
    // Indicate load failure to the I/O manager; driver image is deleted... 
    // 
    return( STATUS_UNSUCCESSFUL ); 
  } 
 
  // Successfully added device, prepare number for next instance 
  ProfiMInstanceCounter++; 
 
  PB_DbgPrintL1( "ProfiM PnP: AddDevice: done\n" ); 
 
  return code; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  DispatchPnp 
// 
//    This is the dispatch entry point for IRP_MJ_PNP requests.  The 
//    driver processes these requets, based on the current state of 
//    the device. 
// 
// 
//  INPUTS: 
// 
//      DeviceObject - Address of the Functional DEVICE_OBJECT for our device. 
//   
//      Irp - Address of the IRP representing the IRP_MJ_PNP request. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//      There are several difficulties implementing Plug and Play. 
//      Perhaps the greatest difficulty is deciding precisely how you 
//      want your device to work, given the various requests the driver 
//      can receive and the states the device can be in.  In our driver, 
//      we've decided to implement the following policies: 
// 
//      1) When a removal of the device is requested, we will reject 
//         any new IRPs we receive (completing them with an error 
//         status in the dispatch routine).  We will wait until all IRPs 
//         that are already present on the device's queue are complete 
//         and then allow the remove. 
// 
//      2) When a stop of the device is requested, we'll queue any 
//         newly received IRPs, but not initiate them.  We will wait 
//         until any IRPs that are presently ACTIVE in progress on the 
//         device complete, and then allow the stop. 
// 
//      3) When a SUPRISE removal of the device is indicated, we 
//         immediately cancel any requests that are queued, reject any 
//         newly arriving requests. 
// 
//      Of course, the second complexity in implementing plug and play 
//      is getting the logic in your driver correct, so that it works 
//      as you intend. According to our experience, this is easier said 
//      than done. 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS DispatchPnp( PDEVICE_OBJECT DeviceObject, PIRP Irp ) 
{ 
  PIO_STACK_LOCATION        ioStackLocation; 
  NTSTATUS                  code  = STATUS_SUCCESS; 
  PPROFIM_DEVICE_EXTENSION  devExt; 
  KEVENT                    eventWaitLowerDrivers; 
  PDEVICE_OBJECT            targetDevice; 
 
  PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: called\n" ); 
 
  // 
  // Get a pointer to our (FUNCTIONAL) device object's device 
  // extension. 
  // 
  devExt = ( PPROFIM_DEVICE_EXTENSION ) DeviceObject->DeviceExtension; 
 
  // 
  // Up the count of in-progress requests 
  // 
  RequestIncrement( devExt ); 
 
  ioStackLocation = IoGetCurrentIrpStackLocation( Irp ); 
 
  KeInitializeEvent( &eventWaitLowerDrivers, NotificationEvent, FALSE ); 
 
  PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Current state: " ); 
  PrintState( devExt ); 
 
  PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: MINOR 0x%0X\n", 
                 ioStackLocation->MinorFunction ); 
  if ( ioStackLocation->MinorFunction <= IRP_MN_SURPRISE_REMOVAL + 1 ) 
    PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: *** PNP Minor Function is %s\n", 
                   pnpMinorCodes[ioStackLocation->MinorFunction] ); 
 
 
  switch ( devExt->State + ioStackLocation->MinorFunction ) 
  { 
      // 
      // STATE:   STOPPED or NEVER_STARTED 
      // IRP_MN:  _START_DEVICE 
      // 
      // We're here if we've received an AddDevice() call, but we 
      // do not have a set of hardware resources from the PnP Manager. 
      // 
      // The PnP Manager is now giving us a set of resources, and  
      // asking us to start the device. 
      // 
      // In this case, we pass the IRP all the way down.  When it's 
      // done (and our completion routine is called) we can then 
      // read the list of device resources pointed to in the IRP 
      // Stack Location. 
      // 
    case STATE_STOPPED + IRP_MN_START_DEVICE: 
    case STATE_NEVER_STARTED + IRP_MN_START_DEVICE: 
      DbgPrint( "ProfiM PnP: DispatchPnp: PROCESSING START_DEVICE\n" ); 
 
      // 
      // The BUS DRIVER handles this IRP before we do 
      // 
      IoCopyCurrentIrpStackLocationToNext( Irp ); 
 
      // 
      // Call PnpComplete() when this IRP is done... 
      // 
      IoSetCompletionRoutine( Irp, 
                              PnpComplete, 
                              &eventWaitLowerDrivers, 
                              TRUE, 
                              TRUE, 
                              TRUE ); 
 
      // 
      // Send the IRP to the bus driver.  Let's see what HE 
      // thinks. 
      // 
      code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
 
      if ( STATUS_PENDING == code ) 
      { 
        KeWaitForSingleObject( &eventWaitLowerDrivers, 
                               Executive, 
                               KernelMode, 
                               FALSE, 
                               NULL ); 
 
        code = Irp->IoStatus.Status; 
      } 
 
      // 
      // Can the bus driver do the start? 
      // 
      if ( NT_SUCCESS( code ) ) 
      { 
        // 
        // Yup.  Go initialize the device.  The CmResourceLists 
        // are pointed to by the IoStackLocation. 
        // 
        code = StartDevice( devExt, ioStackLocation ); 
 
        // 
        // If the our StartDevice function succeeded, the 
        // device is now "officially" started! 
        // 
        if ( NT_SUCCESS( code ) ) 
        { 
          devExt->State = STATE_STARTED; 
        } 
      } 
      else 
      { 
        // 
        // The bus driver has declined to start the device. 
        // Oh well... 
        // 
#if PnP_DBG 
        DbgPrint( "ProfiM PnP: DispatchPnp: IoCallDriver() for START fails! 0x%0x \n", 
                  code ); 
#endif 
      } 
 
      // 
      // We must now complete the IRP, since we stopped it in the 
      // completetion routine with MORE_PROCESSING_REQUIRED. 
      // 
      Irp->IoStatus.Status = code; 
 
      Irp->IoStatus.Information = 0; 
 
      IoCompleteRequest( Irp, IO_NO_INCREMENT ); 
 
      break; 
 
 
 
      // 
      // STATE:   STARTED 
      // IRP_MN:  _QUERY_REMOVE 
      // 
      // We're here because we're running, and the PnP Manager wants 
      // to "nicely" remove our device.  This is the orderly way 
      // PnP Manager handles device disconnections (as opposed to 
      // doing a SURPRISE_REMOVAL). 
      // 
      // What we do here is (a) change the state of the device such 
      // that newly arriving requests will be rejected, (b) wait for 
      // all requests to complete on the device, and (c) pass the 
      // request on down. 
      // 
    case STATE_STARTED + IRP_MN_QUERY_REMOVE_DEVICE: 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: PROCESSING QUERY_REMOVE_DEVICE\n" ); 
#endif 
      // 
      // WE process this request FIRST 
      // 
 
      // 
      // See if we're OK with removing the device at this point. 
      // We do NOT actually RETURN the resources here... we 
      // just affirm or deny that we're OK with returning them. 
      // 
      code = CanRemoveDevice( devExt, Irp ); 
 
      // 
      // Replace status code that's in the IRP to indicate our 
      // opinion about stopping the device.  If we're 
      // OK with returning the resources, this will be 
      // STATUS_SUCCESS. 
      // 
      Irp->IoStatus.Status = code; 
 
      if ( !NT_SUCCESS( code ) ) 
      { 
        // 
        // NOPE.  Can't remove the device because, for some 
        // reason (perhaps because we can't return our 
        // resources).  Too bad. Tell the PnP Manager that 
        // stopping right now is not an option for us. 
        // 
        // NOTE: NO NEED to pass IRP down if WE can't stop 
        // it doesn't matter if the Bus Driver can. 
        // 
        IoCompleteRequest( Irp, IO_NO_INCREMENT ); 
      } 
      else 
      { 
#if PnP_DBG 
        DbgPrint( "ProfiM PnP: DispatchPnp: Waiting for pending requests to complete. (%d. remain)\n", 
                  devExt->OutstandingIO ); 
#endif 
 
        // 
        // Set new state -- This state results in any new 
        // requests received at our dispatch entry points 
        // being REJECTED... any request still on the queues 
        // are allowed to complete, however. 
        // 
        devExt->State = STATE_REMOVE_PENDING; 
 
        // 
        // Decrement our reference on the device here, and then 
        // wait until there are no requests active or on the 
        // device's queues. 
        // 
        RequestDecrement( devExt ); 
 
        WaitForRemove( devExt ); 
 
        // 
        // OK.  No further requests remain.  Let the bus driver 
        // know. 
 
        // 
        // DECREMENTED REQUEST COUNT above.  Return immediately 
        // below. 
        // 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Leaving With state: " ); 
        PrintState( devExt ); 
        PB_DbgPrintL1( "\n" ); 
#endif 
        // 
        // Pass this request on down to the bus driver 
        // 
        IoSkipCurrentIrpStackLocation( Irp ); 
 
        return( IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ) ); 
      } 
      break; 
 
      // 
      // STATE:   REMOVE_PENDING 
      // IRP_MN:  _REMOVE_DEVICE 
      // 
      // We're here because we've previously received notification 
      // of the intention to remove the device in an orderly way. 
      // We return our resources here, and then tear down our device 
      // object.  Note that there can be no requests pending at this 
      // point, because we finished them all during QUERY_REMOVE 
      // processing (before entering RemovePending state) 
      // 
    case STATE_REMOVE_PENDING + IRP_MN_REMOVE_DEVICE: 
      // 
      // All queued and active IRPs are complete at this point. 
      // 
 
      // 
      // Fall through... 
      // 
 
      // 
      // STATE:   SURPRISE_REMOVED 
      // IRP_MN:  _REMOVE_DEVICE 
      // 
      // We're here because we've previously received notification 
      // of a "surprise" removal.  At this point, we just tear down 
      // our device object 
      // 
    case STATE_SURPRISE_REMOVED + IRP_MN_REMOVE_DEVICE: 
      // 
      // Removing the device at this point is NOT optional. 
      // 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: REMOVE_DEVICE\n" ); 
#endif 
 
      // 
      // Device has been removed 
      // 
      devExt->State = STATE_REMOVED; 
 
      // 
      // Return any resources we're using. 
      // 
      ReturnResources( devExt ); 
 
      // 
      // Decrement our reference on the device here, and then 
      // wait until we can remove the device.  Because we would 
      // have gotten a prior warning of this removal (via an 
      // IRP_MN_SUPRISE_REMOVAL or an IRP_MN_QUERY_REMOVE) and 
      // at that time transitioned state appropriately, there 
      // should be no requests outstanding here. 
      // 
      RequestDecrement( devExt ); 
 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: Waiting for pending requests to complete. %d. remain\n", 
                devExt->OutstandingIO ); 
#endif 
      WaitForRemove( devExt ); 
 
      // 
      // WE process this request first 
      // 
 
      // Remember next target of IRP 
      targetDevice = devExt->DeviceToSendIrpsTo; 
 
      // 
      // Remove and return our device object 
      // 
      RemoveDevice( devExt->DeviceObject ); 
 
      // 
      // Indidcate that we've successfully processed the IRP 
      // 
      Irp->IoStatus.Status = STATUS_SUCCESS; 
 
      // 
      // Pass this request on down to the bus driver 
      // 
      IoSkipCurrentIrpStackLocation( Irp ); 
 
      code = IoCallDriver( targetDevice, Irp ); 
 
      ASSERT( code != STATUS_PENDING ); 
 
      // 
      // IMPORTANT: We decremented our I/O In Progress count 
      // above... We don't want to decrement it again, so we 
      // return right here. 
      // 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Leaving with state: STATE_REMOVE\n" ); 
#endif 
      return( code ); 
 
      break; 
 
      // 
      // STATE:   STARTED 
      // IRP_MN:  _SURPRISE_REMOVAL 
      // 
      // We're here when the device is running, and a device is 
      // forcibly removed.  PnP Manager will send us a remove device 
      // IRP when we're supposed to actually return our resources 
      // and the like. 
      // 
      // Note that this is a "best effort" activity.  It is quite 
      // possible, due to timing issues etc, that we'll crash the 
      // system because the device is gone.  While we'll TRY not 
      // to do this, it is within the rules. 
      // 
      // THIS IRP IS NOT SENT ON Win9x. 
      // 
    case STATE_STARTED + IRP_MN_SURPRISE_REMOVAL: 
      devExt->State = STATE_SURPRISE_REMOVED; 
 
      // 
      // We handle this request before the bus driver 
      // 
 
      // 
      // Cancel any pending requests... make sure 
      // the active requests get stopped within a second (with 
      // no hardware access, as a result of setting the state 
      // above). 
      // 
      ClearQueues( devExt ); 
 
      // 
      // We're happy... sort of.  Note that it's not "legal" 
      // to fail this request.  Afterall, what woudl that MEAN? 
      // 
      Irp->IoStatus.Status = STATUS_SUCCESS; 
 
      // 
      // Pass this request on down to the bus driver 
      // 
      IoSkipCurrentIrpStackLocation( Irp ); 
 
      code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
 
      break; 
 
      // 
      // STATE:   STARTED 
      // IRP_MN:  _REMOVE_DEVICE 
      // 
      // We're here when the device is running, and a device is 
      // forcibly removed. 
      // 
      // Note that like getting a SUPRISE_REMOVAL IRP on NT trying 
      // to handle a removal directly from started state is a "best 
      // effort" type of activity.  We'll do what we can but it's 
      // possible, due to timing issues etc, that we'll crash the 
      // system because the device is gone.  While we'll TRY not 
      // to do this, it could be unavoidable.  Oh well. 
      // 
      // ON NT, we'd get an IRP_MN_SURPRISE_REMOVAL instead of this 
      // IRP in this state.  We get this only on Win9x systems. 
      // 
    case STATE_STARTED + IRP_MN_REMOVE_DEVICE: 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: REMOVE_DEVICE\n" ); 
#endif 
      devExt->State = STATE_REMOVED; 
 
      // 
      // We handle this request before the bus driver 
      // Note that this is another one of those requests we're 
      // not "allowed" to fail. 
      // 
 
      // 
      // Cancel any pending requests... make sure 
      // the active requests get stopped within a second (with 
      // no hardware access, as a result of setting the state 
      // above). 
      // 
      ClearQueues( devExt ); 
 
      // 
      // Return any resources we're using. 
      // 
      ReturnResources( devExt ); 
 
      // 
      // Removing the device at this point is NOT optional. 
      // 
 
      // 
      // Detach from the PDO  
      // 
      IoDetachDevice( devExt->DeviceToSendIrpsTo ); 
      targetDevice = devExt->DeviceToSendIrpsTo; 
      devExt->DeviceToSendIrpsTo = NULL; 
 
      // 
      // Decrement our reference on the device here, and then 
      // wait until we can remove the device. 
      // 
      RequestDecrement( devExt ); 
 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Waiting for pending requests to complete. %d. remain\n", 
                     devExt->OutstandingIO ); 
#endif 
 
      WaitForRemove( devExt ); 
 
      // 
      // OK!  Now we can return our device object 
      // 
      RemoveDevice( devExt->DeviceObject ); 
 
      // 
      // Tell the underlying driver we're cool 
      // 
      Irp->IoStatus.Status = STATUS_SUCCESS; 
 
      // 
      // Pass this request on down to the bus driver 
      // 
      IoSkipCurrentIrpStackLocation( Irp ); 
 
      code = IoCallDriver( targetDevice, Irp ); 
 
      ASSERT( code != STATUS_PENDING ); 
 
      // 
      // IMPORTANT: We decremented our I/O In Progress count 
      // above... We don't want to decrement it again, so we 
      // return right here. 
      // 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Leaving with state: STATE_REMOVED\n" ); 
#endif 
      return( code ); 
 
      break; 
 
      // 
      // STATE:   STARTED 
      // IRP_MN:  _QUERY_STOP_DEVICE 
      // 
      // We're here if we're running and the PnP Manager sends us 
      // a QUERY_STOP_DEVICE request.  He'll do this if he wants to 
      // rebalance resources, to see if we're willing to give up the 
      // hardware resources that were allocated for us when we got 
      // our IRP_MN_START_DEVICE. 
      // 
      // To proess this QUERY_STOP, we check to see if the stop is 
      // acceptable to us (in this driver it always is), and then 
      // we just transition the device to STOP_PENDING state.  In 
      // this state, new requests that arrive are queued.  When a 
      // currently in-progress request is completed, a new request 
      // is NOT started.  Thus, ON OUR DEVICE, we sort of hope that 
      // between the QUERY_STOP IRP arrive and actual STOP IRP arriving 
      // that any in-progress I/O will complete of its own accord. 
      // We like this scheme, particularly because at least on NT 
      // during startup the device seems to get lots of QUERY_STOP 
      // IRPs, that are just followed by CANCEL_STOP.  Thus, we think 
      // it would be unfortunate to do anything radical with the 
      // outstanding requests (like cancel them) when we receive a 
      // QUERY_STOP. 
      // 
    case STATE_STARTED + IRP_MN_QUERY_STOP_DEVICE: 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: PROCESSING QUERY_STOP_DEVICE\n" ); 
#endif 
      // 
      // WE process this request before the BUS DRIVER 
      // 
 
      // 
      // See if we're OK with stopping the device at this point. 
      // We do NOT actually RETURN the resources here... we 
      // just affirm or deny that we're OK with returning them. 
      // 
      code = CanStopDevice( devExt, Irp ); 
 
      // 
      // Replace status code that's in the IRP to indicate our 
      // opinion about stopping the device.  If we're 
      // OK with returning the resources, this will be 
      // STATUS_SUCCESS. 
      // 
      Irp->IoStatus.Status = code; 
 
      if ( !NT_SUCCESS( code ) ) 
      { 
        // 
        // NOPE.  Can't stop the device because, for some 
        // reason (perhaps because we can't return our 
        // resources).  Too bad. Tell the PnP Manager that 
        // stopping right now is not an option for us. 
        // 
        // NOTE: NO NEED to pass IRP down if WE can't stop 
        // it doesn't matter if the Bus Driver can. 
        // 
        IoCompleteRequest( Irp, IO_NO_INCREMENT ); 
      } 
      else 
      { 
        // 
        // We CAN stop our device and return the resources. 
        // Pass the IRP down to see if the bus driver is 
        // equally ammenable to the request. 
        // 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Agreeing to stop device.\n" ); 
#endif 
 
        // 
        // Set new state. This state results in no new 
        // requests being started on the device, but incoming 
        // requests are still allowed and queued. 
        // 
        devExt->State = STATE_STOP_PENDING; 
 
        // 
        // Pass this request on down to the bus driver 
        // 
        IoSkipCurrentIrpStackLocation( Irp ); 
 
        code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
      } 
      break; 
 
      // 
      // STATE:   STOP_PENDING 
      // IRP_MN:  _STOP_DEVICE 
      // 
      // We're in this state because we previously received a  
      // QUERY_STOP_DEVICE, that we agreed that we could grant. 
      // Thus, we're waiting to receive a STOP_DEVICE request. 
      // To process this request, we first wait for any in-progress 
      // requests to complete (note that no NEW requests have been 
      // started since the transition to STOP_PENDING state as a 
      // result of receiving the QUERY_STOP IRP).  After all in- 
      // progress requests are complete, we return our resources 
      // and wait for further instructions from the PnP Manager ( 
      // which better include a START_DEVICE someplace down the line!). 
      //  
      // While in this state, any IRPs we receive will be queued 
      // for processing after we get the START_DEVICE. 
      // 
    case STATE_STOP_PENDING + IRP_MN_STOP_DEVICE: 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: PROCESSING STOP_DEVICE\n" ); 
#endif 
      // 
      // Assume success 
      // 
      devExt->State = STATE_STOPPED; 
 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: Waiting for in-progress requests to complete\n" ); 
#endif 
 
      // 
      // We process this request before passing it to the bus 
      // driver 
      // 
 
      // 
      // Wait until all active requests on the device have 
      // completed... 
      // 
      WaitForStop( devExt ); 
 
      // 
      // There are NO ACTIVE IRPs on the device at this point. 
      // 
 
      // 
      // Return any resources we're using. 
      // 
      ReturnResources( devExt ); 
 
      // 
      // NOTE: We DO NOT delete our device object here.  This is 
      // one difference between a _STOP_DEVICE and _REMOVE_DEVICE. 
      // 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Passing along request\n" ); 
#endif 
 
      // 
      // Pass this request on down to the bus driver 
      // 
      IoSkipCurrentIrpStackLocation( Irp ); 
 
      // 
      // We're cool with the stop... 
      // 
      Irp->IoStatus.Status = STATUS_SUCCESS; 
 
      code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
      break;         
 
 
      // 
      // STATE:   STOP_PENDING 
      // IRP_MN:  _CANCEL_STOP_DEVICE 
      // 
      // We're here because we've already received a QUERY_STOP, 
      // that we've agreed to.  We've completed any pending I/O 
      // requests.  Now we've received a CANCEL_STOP_DEVICE 
      // IRP, that sort of says "never mind" about that stop. 
      // 
      // We restart our queues and return to Started state. 
      // 
    case STATE_STOP_PENDING + IRP_MN_CANCEL_STOP_DEVICE: 
#if PnP_DBG 
      DbgPrint( "ProfiM PnP: DispatchPnp: PROCESSING CANCEL_STOP\n" ); 
#endif 
 
      // 
      // fall through... 
      // 
 
      // 
      // STATE:   REMOVE_PENDING 
      // IRP_MN:  _CANCEL_REMOVE_DEVICE 
      // 
      // We're here because we've already received a QUERY_REMOVE, 
      // that we've agreed to.  We've completed any pending I/O 
      // requests.  Now we've received a CANCEL_REMOVE_DEVICE 
      // IRP, that sort of says "never mind" about that remove. 
      // 
      // We restart our queues and return to Started state. 
      // 
    case STATE_REMOVE_PENDING + IRP_MN_CANCEL_REMOVE_DEVICE: 
#if PnP_DBG 
      if ( devExt->State == STATE_REMOVE_PENDING ) 
      { 
        DbgPrint( "ProfiM PnP: DispatchPnp: PROCESSING CANCEL_REMOVE\n" ); 
      } 
#endif 
 
      // 
      // The Underlying BUS DRIVER must handle these IRPs 
      // before we do... 
      // 
 
      // 
      // We need to wait for the underlying bus driver to 
      // get restarted, before we can continue processing. 
      // 
      IoCopyCurrentIrpStackLocationToNext( Irp ); 
 
      IoSetCompletionRoutine( Irp, 
                              PnpComplete, 
                              &eventWaitLowerDrivers, 
                              TRUE, 
                              TRUE, 
                              TRUE ); 
 
      code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
 
      if ( code == STATUS_PENDING ) 
      { 
        KeWaitForSingleObject( &eventWaitLowerDrivers, 
                               Executive, 
                               KernelMode, 
                               FALSE, 
                               NULL ); 
      } 
 
 
      // 
      // We're now in STARTED state 
      // 
      devExt->State = STATE_STARTED; 
 
      // 
      // Go see if there's a request that we can start now 
      // 
      ProcessQueuedRequests( devExt ); 
 
      code = STATUS_SUCCESS; 
 
      Irp->IoStatus.Status = code; 
 
      Irp->IoStatus.Information = 0; 
 
      IoCompleteRequest( Irp, IO_NO_INCREMENT ); 
 
      break; 
 
    default: 
      // 
      // DEFAULT CASE 
      // Just pass the request to the lower driver 
      // 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Default case: Just pass it along...\n" ); 
#endif 
      IoSkipCurrentIrpStackLocation( Irp ); 
 
      code = IoCallDriver( devExt->DeviceToSendIrpsTo, Irp ); 
 
      break; 
  } 
 
  // 
  // Adjust in-progress request count 
  // 
  RequestDecrement( devExt ); 
 
#if PnP_DBG 
  PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: Leaving with new state " ); 
 
  PrintState( devExt ); 
 
  PB_DbgPrintL1( "ProfiM PnP: DispatchPnp: exit (%d.).\n", 
                 devExt->OutstandingIO ); 
#endif 
 
  // 
  // We always finish our work in this function 
  // 
  ASSERT( code != STATUS_PENDING ); 
 
  return( code ); 
} 
 
// 
// PnpComplete 
//  
// This is the completion routine for IRP_MJ_PNP requests 
// 
NTSTATUS PnpComplete( IN PDEVICE_OBJECT DeviceObject, 
                      IN PIRP Irp, 
                      IN PVOID Context ) 
{ 
  PIO_STACK_LOCATION  iostack; 
  PKEVENT             pEvent  = ( PKEVENT ) Context; 
  NTSTATUS            status  = STATUS_SUCCESS; 
 
  UNREFERENCED_PARAMETER( DeviceObject ); 
 
  iostack = IoGetCurrentIrpStackLocation( Irp ); 
 
  // 
  // Driver Writers, please note: 
  // 
  //  The following code is only necessary IF (a) WE have a completion 
  //  routine, AND (b) WE return STATUS_PENDING from our dispatch entry 
  //  point after re-claiming the IRP.  Since neither of these things 
  //  is true... this code does not belong here. 
  // 
  //    if (Irp->PendingReturned) { 
  // 
  //        IoMarkIrpPending( Irp ); 
  // 
  //    } 
 
  switch ( iostack->MajorFunction ) 
  { 
    case IRP_MJ_PNP: 
      KeSetEvent( pEvent, 0, FALSE ); 
 
      // 
      // Take the IRP back so that we can continue using it during 
      // the IRP_MN_START_DEVICE dispatch routine. 
      // NB: we will have to call IoCompleteRequest 
      // 
      return STATUS_MORE_PROCESSING_REQUIRED; 
 
      break; 
 
    case IRP_MJ_POWER: 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: PnpComplete: NOT MJ_POWER support in this driver!\n" ); 
#endif 
 
      break; 
 
    default: 
#if PnP_DBG 
      PB_DbgPrintL1( "ProfiM PnP: PnpComplete: NOT MJ_PNP or MJ_POWER IRP??\n" ); 
#endif 
 
      break; 
  } 
 
  return status; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// StartDevice 
// 
//      This function is called from the DispatchPnp Entry Point to 
//      actually start the hardware. 
// 
//  INPUTS: 
// 
//      DevExt  - Address of our device extension. 
//      IoStackLocation -- Pointer to I/O Stack Location containing 
//                  configuration information 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS StartDevice( IN PPROFIM_DEVICE_EXTENSION DevExt, 
                      IN PIO_STACK_LOCATION IoStackLocation ) 
{ 
  NTSTATUS                        code            = STATUS_SUCCESS; 
  ULONG                           index; 
  PDEVICE_DESCRIPTION             deviceDescription; 
  PCM_RESOURCE_LIST               pResourceList, pResourceListTranslated; 
  PCM_PARTIAL_RESOURCE_LIST       prl, prlTranslated; 
  PCM_PARTIAL_RESOURCE_DESCRIPTOR prd, prdTranslated; 
  PCM_FULL_RESOURCE_DESCRIPTOR    frd, frdTranslated; 
  HANDLE                          regKeyHandle; 
  PWCHAR                          dpHWID; 
  ULONG                           dpHWID_len; 
  PWCHAR                          pwc; 
  pci_device_id_t                 *device_id; 
  int                             ChipOptions     = 0; 
  ULONG                           ProfiMBaudrate  = 19200; 
  ULONG                           ProfiMMyAddress = 2; 
  ULONG                           Data; 
  int                             i; 
 
 
  if ( !DevExt ) 
  { 
    DbgPrint( "PnP: StartDevice Error - DeviceExtension is NULL!" ); 
    return STATUS_UNSUCCESSFUL; 
  }         
  if ( DevExt->magic != PROFIM_MAGIC ) 
  { 
    DbgPrint( "PnP: StartDevice Error - Device Extension MAGIC is invalid!" ); 
    return STATUS_UNSUCCESSFUL; 
  } 
 
 
  pResourceList = IoStackLocation->Parameters.StartDevice.AllocatedResources; 
  pResourceListTranslated = IoStackLocation->Parameters.StartDevice.AllocatedResourcesTranslated; 
 
  frd = &pResourceList->List[0]; 
  frdTranslated = &pResourceListTranslated->List[0]; 
 
  prl = &frd->PartialResourceList; 
  prlTranslated = &frdTranslated->PartialResourceList; 
 
#if PnP_DBG 
  PrintResourceList( pResourceList ); 
  PrintResourceList( pResourceListTranslated ); 
#endif 
 
  // 
  // Ensure the base address starts as NULL 
  // 
  ///DevExt->AmccBaseRegisterAddress = NULL; 
  DevExt->port = 0; 
  DevExt->irq = 0; 
 
 
  // Try to find chip category from  
  // IoGetDeviceProperty(DeviceObject,DeviceProperty,BufferLength,PropertyBuffer,ResultLength); 
  // subfunctions DevicePropertyHardwareID and DevicePropertyLegacyBusType  
  // ProfiM: HWID : PCI\VEN_1415&DEV_950A&SUBSYS_00001415&REV_00 
  // ProfiM: HWID : PCI\VEN_1415&DEV_950A&SUBSYS_00001415 
  // ProfiM: HWID : PCI\VEN_1415&DEV_950A&CC_070006 
  // ProfiM: HWID : PCI\VEN_1415&DEV_950A&CC_0700 
 
  dpHWID_len = 0; 
  code = IoGetDeviceProperty( DevExt->PhysicalDeviceObject, 
                              DevicePropertyHardwareID, 
                              0, 
                              NULL, 
                              &dpHWID_len ); 
  if ( dpHWID_len ) 
  { 
    dpHWID = ExAllocatePool( PagedPool, dpHWID_len ); 
    if ( dpHWID == NULL ) 
      return STATUS_INSUFFICIENT_RESOURCES; 
    code = IoGetDeviceProperty( DevExt->PhysicalDeviceObject, 
                                DevicePropertyHardwareID, 
                                dpHWID_len, 
                                dpHWID, 
                                &dpHWID_len ); 
    if ( !NT_SUCCESS( code ) ) 
    { 
      #if PnP_DBG 
        DbgPrint( "ProfiM PnP: IoGetDeviceProperty failed with error 0x%x", code ); 
      #endif 
      ExFreePool( dpHWID ); 
      return( code ); 
    } 
    pwc = dpHWID; 
    while ( *pwc ) 
    { 
      #if PnP_DBG 
        PB_DbgPrintL3( "ProfiM PnP: HWID : %ws\n", pwc ); 
      #endif 
      while ( *pwc ) 
        pwc++; 
      pwc++; 
    } 
     
 
    DevExt->PiKRON = FALSE;               
    if(FindPciHWID(dpHWID,&device_id)) 
    { 
      ChipOptions=device_id->driver_data; 
      //#if PnP_DBG 
        PB_DbgPrintL3("ProfiM PnP: ChipOptions %08X\n", ChipOptions); 
      //#endif 
       
      if (device_id->vendor==0x1760 && device_id->device==0x8004) 
        DevExt->PiKRON = TRUE;    // specialni typ karty pro firmu PiKRON   
    } 
           
 
    ExFreePool( dpHWID ); 
  } 
   
  DevExt->ChipOptions = ChipOptions; 
 
/*   
  // 
  // Read values from registry  
  // 
  code = IoOpenDeviceRegistryKey( DevExt->PhysicalDeviceObject, 
                                  PLUGPLAY_REGKEY_DEVICE, 
                                  STANDARD_RIGHTS_WRITE, 
                                  ®KeyHandle ); 
  if ( NT_SUCCESS( code ) ) 
  { 
          if ( GetRegistryDWord ( regKeyHandle, L"Baud Rate", &Data) ) 
            DevExt->BaudRate = Data; 
          else 
            DevExt->BaudRate = DEF_BAUD_RATE; 
             
//          if ( GetRegistryDWord ( regKeyHandle, L"Buffer Size", &Data) ) 
//            DevExt->BufferSize = Data; 
//          else 
            DevExt->BufferSize = DEF_BUFFER_SIZE; 
             
          if ( DevExt->BufferSize < 10 ) DevExt->BufferSize=10; 
             
          DbgPrint("ProfiM PnP: Parametry z registru BaduRate=%d BufferSize=%d", DevExt->BaudRate, DevExt->BufferSize); 
 
    ZwClose( regKeyHandle ); 
  } 
*/   
 
  // 
  // Walk through the partial resource descriptors to find the 
  // hardware resources that have been allocated to us 
  // 
  // We need one range of port addresses (0x08 bytes long) and 
  // and interrupt resource. 
  // 
  for ( index = 0, 
                  prd = &prl->PartialDescriptors[index], 
                  prdTranslated = &prlTranslated->PartialDescriptors[index]; 
        index < prl->Count && NT_SUCCESS( code ); 
        index++, prd++, prdTranslated++ ) 
  { 
    switch ( prd->Type ) 
    { 
      case CmResourceTypePort: 
        // 
        // Newer AMCC Demo Boards have more than just one BAR 
        // programmed in the PCI configuration ROM.  We want 
        // the FIRST BAR, which is the base address of the 
        // device itself.  So, we ignore any ports reported to 
        // us after the first one. 
        // 
        ///if (DevExt->AmccBaseRegisterAddress) { 
        if ( DevExt->port ) 
        { 
#if PnP_DBG 
          PB_DbgPrintL1( "ProfiM PnP: Ignoring additional port resource ...\n" ); 
 
          if ( prdTranslated->Type == CmResourceTypePort ) 
          { 
            PB_DbgPrintL1( "ProfiM PnP: (Translated port 0x%0x)\n", 
                           prdTranslated->u.Port.Start.LowPart ); 
          } 
          else 
          { 
            PB_DbgPrintL1( "ProfiM PnP: (Translated memory 0x%0x)\n", 
                           prdTranslated->u.Memory.Start ); 
          } 
 
#endif 
          break; 
        } 
 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: Configuring port resource ...\n" ); 
#endif 
        // 
        // Should only get ONE port resources 
        // 
        ///ASSERT(DevExt->AmccBaseRegisterAddress == NULL); 
 
        // 
        // Our port space on this card is 0x40 bytes longs 
        // 
        ///ASSERT(prd->u.Memory.Length == 0x40); 
 
        // 
        // Do the device ports appear in port I/O space or 
        // in memory space on this machine. 
        // 
        if ( prdTranslated->Type == CmResourceTypePort ) 
        { 
          // 
          // The port is in port space on this machine.  Just 
          // store away the address 
          // 
          ///DevExt->MappedPorts = FALSE; 
 
          ///DevExt->AmccBaseRegisterAddress = 
          ///    (PVOID) prdTranslated->u.Port.Start.LowPart; 
          DevExt->port = prdTranslated->u.Port.Start.LowPart; 
 
#if PnP_DBG 
          PB_DbgPrintL1( "ProfiM PnP: Translated resource is a port at 0x%0x\n", 
                         DevExt->port ); 
#endif 
        } 
        else 
        { 
          ASSERT( prdTranslated->Type == CmResourceTypeMemory ); 
 
          // 
          // The port is in memory space on this machine.  We 
          // need to map some virtual addresses over the physical 
          // address provided us, and remember to do an UNMAP 
          // if/when we have to return the resources. 
          // 
          DevExt->MappedPorts = TRUE; 
 
          ///DevExt->AmccBaseRegisterAddress = 
          DevExt->port = ( ULONG ) 
                         MmMapIoSpace( prdTranslated->u.Memory.Start, 
                                       DEF_PORT_RANGE, /*prdTranslated->u.Memory.Length,*/ 
                                       MmNonCached ); 
 
 
#if PnP_DBG 
          PB_DbgPrintL1( "ProfiM PnP: Translated resource is MEMORY at 0x%0x\n", 
                         DevExt->port ); 
#endif 
        } 
 
        break; 
 
 
      case CmResourceTypeInterrupt: 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: Configuring Interrupt resource ...\n" ); 
#endif 
        // 
        // Be sure we get only ONE interrupt resource 
        // 
        ASSERT( DevExt->irq == 0 ); 
 
        // 
        // Again, assume that the translated and raw resources 
        // are in the same order and number 
        // 
        ASSERT( CmResourceTypeInterrupt == prdTranslated->Type ); 
 
        ///DevExt->InterruptLevel       = (UCHAR)prdTranslated->u.Interrupt.Level; 
        ///DevExt->InterruptVector      = prdTranslated->u.Interrupt.Vector; 
        ///DevExt->InterruptAffinity    = prdTranslated->u.Interrupt.Affinity; 
        DevExt->Irql = ( UCHAR ) prdTranslated->u.Interrupt.Level; 
        DevExt->irq = prdTranslated->u.Interrupt.Vector; 
        DevExt->InterruptAffinity = prdTranslated->u.Interrupt.Affinity; 
 
        if ( prdTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED ) 
        { 
          DevExt->InterruptMode = Latched; 
          //DbgPrint("Profim PnP: ERROR InterruptMode = Latched??? - LevelSensitive EXPECTED!"); 
        } 
        else 
        { 
          DevExt->InterruptMode = LevelSensitive; 
        } 
 
        // 
        // Because this is a PCI device, we KNOW it must be 
        // a LevelSensitive Interrupt 
        // 
        ///ASSERT(DevExt->InterruptMode == LevelSensitive); 
 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: Interrupt level: 0x%0x, Vector: 0x%0x, Affinity: 0x%0x\n", 
                       DevExt->Irql, 
                       DevExt->irq, 
                       DevExt->InterruptAffinity ); 
#endif 
        break; 
 
      default: 
#if PnP_DBG 
        PB_DbgPrintL1( "ProfiM PnP: Unhandled Resource -- CmResourceType received 0x%x\n", 
                       prd->Type ); 
#endif 
        break; 
    } 
  } 
 
  // 
  // We NEED the interrupt info AND one port 
  // 
  if ( !DevExt->irq || !DevExt->port ) 
  { 
    DbgPrint( "ProfiM PnP: Port (0x%0lX) or Irq (%d) undefined\n", 
              DevExt->port, 
              DevExt->irq ); 
    return STATUS_UNSUCCESSFUL; 
  } 
 
 
  // 
  // Register our DPCforISR routine.  This is the routine which will 
  // be used to complete our interrupt processing. 
  // 
  ///IoInitializeDpcRequest(DevExt->DeviceObject, DpcForIsr); 
  /*     
  //**     
  KeInitializeDpc(&DevExt->bottom_dpc,ProfiM_bottom_dpc,DevExt); 
  KeInitializeDpc(&DevExt->wd_timer_dpc,ProfiM_wd_dpc,DevExt); 
  KeInitializeTimer(&DevExt->wd_timer); 
  */     
 
 
  // Increase spinlock level 
  //**    if(uL_SpinLock_Irql<DevExt->Irql) 
  //**  uL_SpinLock_Irql=DevExt->Irql; 
  //**    PB_DbgPrintL1("ProfiM PnP: spin lock irql=%d\n",uL_SpinLock_Irql); 
 
 
  DevExt->PortAddress = (PUCHAR) DevExt->port; 
  DevExt->IRQLine = (KIRQL) DevExt->irq;   
 
   
  KeInitializeDpc(&DevExt->CompleteDPC, ProfiM_CompleteDPC, DevExt); 
       
   
  KeInitializeSpinLock( &(DevExt->InterruptSpinLock) ); 
   
 
  // 
  // Connect to interrupt from the device.  After this call, 
  // interrupts from the device will result in calls to our ProfiM HandleInterrupt 
  // function. 
  // 
  code = IoConnectInterrupt( &DevExt->InterruptObject, 
                             ProfiM_Isr,     // ServiceRoutine  
                             DevExt->DeviceObject,         // ServiceContext 
                             &(DevExt->InterruptSpinLock),         // SpinLock 
                             DevExt->irq,        // Vector 
                             DevExt->Irql,       // Irql  
                             DevExt->Irql,       // SynchronizeIrql      
                             DevExt->InterruptMode,      // InterruptMode 
                             TRUE,         // ShareVector       
                             DevExt->InterruptAffinity,  // ProcessorEnableMask  
                             FALSE );          // FloatingSave  
                              
                                                            
  DevExt->InterruptRunning = FALSE;      /*DEBUG*/                            
  DevExt->SecondInterrupt  = FALSE;      /*DEBUG*/                              
 
 
  if ( !NT_SUCCESS( code ) ) 
  { 
    DbgPrint( "ProfiM PnP: IoConnectInterrupt failed with error 0x%x", code ); 
    // 
    // We're outa here  
    // 
    return( code ); 
  } 
 
 
  DbgPrint( "ProfiM PnP: Pouzivane prostredky Port= (0x%0lX) and Irq= (%d)\n", 
            DevExt->port, 
            DevExt->irq );     
 
  // 
  // Now we are ready to start ProfiM communication. 
  // 
  ///ResetAdapter(DevExt->DeviceObject, FALSE); 
 
  /* 
  //**     
      DevExt->baud_val=(int)ProfiMBaudrate; 
      DevExt->my_adr=(int)ProfiMMyAddress; 
      if(DevExt->State==STATE_NEVER_STARTED) { 
       #if PnP_DBG 
        DbgPrint("ProfiM PnP: StartDevice: Calling ul_drv_init_ext\n"); 
       #endif 
        code = ul_drv_init_ext(DevExt, DevExt->port, DevExt->irq, 
     (int)ProfiMBaudrate, ChipOptions,  
     0x10000, (int)ProfiMMyAddress); 
      }else{ 
       #if PnP_DBG 
        DbgPrint("ProfiM PnP: StartDevice: Calling ul_drv_new_start\n"); 
       #endif 
        ul_drv_new_init_state(DevExt,(int)ProfiMMyAddress); 
        if(ul_drv_new_start(DevExt,0x10000)<0) 
    code = STATUS_INSUFFICIENT_RESOURCES; 
        else 
    code = STATUS_SUCCESS; 
      } 
      if(!NT_SUCCESS(code)) { 
       #if PnP_DBG 
        DbgPrint("ProfiM PnP: StartDevice: Start of hardware failed\n"); 
       #endif 
        IoDisconnectInterrupt(DevExt->InterruptObject); 
        DevExt->InterruptObject = NULL; 
        ul_drv_done_ext(DevExt); 
      } 
  */ 
 
 
  PB_DbgPrintL1( "ProfiM PnP: *********  A muzeme jet *************\n" ); 
 
  // 
  // Setup the Dpc for ISR routine 
  // 
  IoInitializeDpcRequest( DevExt->DeviceObject, ProfiM_Dpc_Routine ); 
  //KeInitializeDpc(&DevExt->bottom_dpc,ProfiM_bottom_dpc,DevExt); 
 
  /// Nastaveni priznaku vysilani                         
  DevExt->Sending = FALSE;   
  // priznak k vymazani vsech casovacich znaku ze zacatku vysilaci fronty                    
  DevExt->FlushTCH = FALSE; 
   
  // umozni rozbeh ovladace nez se aktivuje hlidaci rutina watchdogu 
  DevExt->WatchDogTrigger = 0; 
 
  // pocitadlo instanci Handlu pripojenych na ovladac 
  DevExt->HIDCounter = 1; 
 
#ifdef PISA_IO             
  DevExt->ModemInterruptState = MI_Disabled; 
#endif 
 
  DevExt->PB.PhysicalDeviceObject = DevExt->PhysicalDeviceObject; 
 
  // 
  // Initialize the device (enable IRQ's, hit the hardware) 
  //   
  Initialize_ProfiM( DevExt, ChipOptions );   
 
  RS_DbgPrint( "ProfiM PnP: All initialized!\n" ); 
   
  PB_DbgPrintL1( "ProfiM PnP: ACR_RX=%x   ACR_TX=%x ",  
                 U950PCI_RX_ACR( DevExt ),U950PCI_TX_ACR( DevExt ) ); 
  PB_DbgPrintL1( "ProfiM PnP: MCR_RX=%x   MCR_TX=%x ",  
                 U950PCI_RX_MCR( DevExt ),U950PCI_TX_MCR( DevExt ) );         
  PB_Init( &( DevExt->PB ), DevExt, &PB_RegistryPath ); 
   
 
  code = STATUS_SUCCESS; 
 
 
 
  return code; 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// CanStopDevice 
// 
//      This routine determines if the device cab be safely stopped.  In 
//      our case we'll assume you can always stop the device.  A device 
//      might not be able to be stopped, for example, if it doesn't have 
//      a queue for incoming requests or if it was notified that it is 
//      in the paging path. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
//      Irp     - Address of the input IRP. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS CanStopDevice( PPROFIM_DEVICE_EXTENSION devExt, PIRP Irp ) 
{ 
  UNREFERENCED_PARAMETER( devExt ); 
  UNREFERENCED_PARAMETER( Irp ); 
 
  return STATUS_SUCCESS; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// CanRemoveDevice 
// 
//      This routine determines if the device cab be safely removed.  In our case 
//      we'll assume you can always remove the device.  A device might not be able to 
//      be removed, for example, if it has opened handles or removing the device would 
//      result in the lost of data. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
//      Irp         - Address of the input IRP. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS CanRemoveDevice( PPROFIM_DEVICE_EXTENSION devExt, PIRP Irp ) 
{ 
  UNREFERENCED_PARAMETER( devExt ); 
  UNREFERENCED_PARAMETER( Irp ); 
 
  return STATUS_SUCCESS; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// ReturnResources 
// 
//      This function is called to return any hardware resources which 
//      have been allocated for the device.  In our case, these are 
//      the Interrupt Object, and any ports we might have mapped through 
//      memory.  Note that we do NOT return our Device Object here. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
//      Irp         - Address of the input IRP. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      VOID 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID ReturnResources( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  // 
  // Stop port activity 
  //   
  devExt->PB.AllInitialized=FALSE; 
 
  // 
  // Deactivate all of the MCR interrupt sources. 
  // 
 
  if((devExt->ChipOptions&~0xff)==0x16954000) 
  { 
    u950pci_pdone( devExt ); 
  } 
  else 
  { 
    WRITE_PORT_UCHAR( devExt->ComPort.MCR, MCR_DEACTIVATE_ALL ); 
  } 
 
  // 
  // Disconnect from any interrupts 
  // 
  if ( devExt->InterruptObject ) 
  { 
    // 
    // Disconnect from the interrupt 
    // 
    IoDisconnectInterrupt( devExt->InterruptObject ); 
 
    devExt->InterruptObject = NULL; 
  }   
   
  PB_Close( &( devExt->PB ) );     
 
 
 
  // 
  // Unmap any ports that were mapped 
  // 
  if ( devExt->MappedPorts ) 
  { 
    MmUnmapIoSpace( ( PVOID ) devExt->port, DEF_PORT_RANGE ); 
 
    devExt->MappedPorts = FALSE; 
    devExt->port = 0; 
  } 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// RemoveDevice 
// 
//      This function is called ater ReturnResources to destroy and 
//      remove ProfiM DeviceObject. Again resources must be already 
//      returned before this call 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      VOID 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID RemoveDevice( IN PDEVICE_OBJECT devObj ) 
{ 
  PPROFIM_DEVICE_EXTENSION  devExt; 
 
  devExt = ( PPROFIM_DEVICE_EXTENSION ) devObj->DeviceExtension; 
 
  // 
  // Detach from the PDO  
  // 
  if ( devExt->DeviceToSendIrpsTo ) 
    IoDetachDevice( devExt->DeviceToSendIrpsTo ); 
  devExt->DeviceToSendIrpsTo = NULL; 
 
  // Delete Symbolic Link 
  if ( devExt->link_name.Length && devExt->link_name.Buffer ) 
    IoDeleteSymbolicLink( &devExt->link_name ); 
  if ( devExt->link_name.Buffer ) 
    ExFreePool( devExt->link_name.Buffer ); 
  if ( devExt->ntdev_name.Buffer ) 
    ExFreePool( devExt->ntdev_name.Buffer ); 
 
  // Destroy DeviceObject 
  IoDeleteDevice( devObj ); 
} 
 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// RequestIncrement 
// RequestDecrement 
// 
//      These functions are used to track the number of requests that 
//      are presently queued the device indicated by the passed 
//      Device Extension. 
// 
//      This code is based heavily on the PnP Sample Driver in the 
//      \General directory of the NTDDK. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID RequestIncrement( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  LONG  result; 
 
  result = InterlockedIncrement( &devExt->OutstandingIO ); 
 
  ASSERT( result >= 0 ); 
 
  // 
  // Need to clear Remove 
  // 
  if ( result == 1 ) 
  { 
    // 
    // The remove event is cleared when the first request is 
    // added to the queue. 
    // 
    KeClearEvent( &devExt->RemoveEvent ); 
  } 
 
  return; 
} 
 
VOID RequestDecrement( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  LONG  result; 
 
  result = InterlockedDecrement( &devExt->OutstandingIO ); 
 
  ASSERT( result >= 0 ); 
 
  if ( result == 0 ) 
  { 
    // 
    // The remove event is set when the queue is totally EMPTY. 
    // 
    KeSetEvent( &devExt->RemoveEvent, IO_NO_INCREMENT, FALSE ); 
  } 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// WaitForStop 
// 
//      Using the event set by the RequestIncrement() and  
//      RequestDecrement() functions, this function waits until 
//      the device can be stopped. 
// 
//      On our device, STOP implies there are no active requests on 
//      the device, even though there are queued requests. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID WaitForStop( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
#if 0 /* No need to wait for end of IO */ 
  devExt->HoldNewRequests = TRUE; 
  ASSERT(devExt->State != STATE_STARTED); 
  KeWaitForSingleObject(&devExt->StopEvent, 
     Executive, 
                         KernelMode, 
                         FALSE, 
     NULL); 
#endif 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// WaitForRemove 
// 
//      Using the event set by the RequestIncrement() and  
//      RequestDecrement() functions, this function waits until 
//      the device can be removed. 
// 
//      On our device, REMOVE implies there are no active or queued 
//      requests on the device. 
// 
//  INPUTS: 
// 
//      devExt  - Address of our device extension. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//    STATUS_SUCCESS; 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID WaitForRemove( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
#if PnP_DBG 
  LARGE_INTEGER timeout; 
  NTSTATUS      status; 
 
  timeout.QuadPart = -5 * 10 * 1000 * 1000;     // 5 seconds 
#endif 
 
  ///devExt->Removed = TRUE; 
 
  ASSERT( devExt->State < STATE_ALL_BELOW_FAIL ); 
 
#if PnP_DBG 
 
  wait_again: 
 
  status = KeWaitForSingleObject( &devExt->RemoveEvent, 
                                  Executive, 
                                  KernelMode, 
                                  FALSE, 
                                  &timeout ); 
 
  if ( status == STATUS_TIMEOUT ) 
  { 
    DbgPrint( "ProfiM PnP: *** Still Waiting for activity to stop on device??  %d. requests reamin\n", 
              devExt->OutstandingIO ); 
    goto wait_again; 
  }     
 
  if ( status != STATUS_SUCCESS ) 
  { 
    DbgPrint( "ProfiM PnP: *** Wait not ended with success, and not with timeout?? Status = 0x%0x\n", 
              status ); 
  } 
 
#else 
 
  KeWaitForSingleObject( &devExt->RemoveEvent, 
                         Executive, 
                         KernelMode, 
                         FALSE, 
                         NULL ); 
#endif 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
// DispatchSystemControl 
// 
//      This dispatch entry point is used to pass any WMI IRPs that we 
//      receive on to the underlying bus driver. 
// 
//  INPUTS: 
// 
///     DriverObject - Pointer to driver object. 
//      Irp         - Address of the input IRP. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      VOID 
// 
//  IRQL: 
// 
// 
//  NOTES: 
// 
// 
/////////////////////////////////////////////////////////////////////////////// 
NTSTATUS DispatchSystemControl( PDEVICE_OBJECT DeviceObject, PIRP Irp ) 
{ 
  // 
  // Since we don't play WMI, pass this request on down to the 
  // driver below us... 
  // 
  IoSkipCurrentIrpStackLocation( Irp ); 
 
  return( IoCallDriver( ( ( PPROFIM_DEVICE_EXTENSION ) 
                          ( DeviceObject->DeviceExtension ) )->DeviceToSendIrpsTo, 
                        Irp ) ); 
} 
 
 
 
VOID PrintState( PPROFIM_DEVICE_EXTENSION devExt ) 
{ 
  if ( devExt->State == STATE_REMOVED ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_REMOVED\n" ); 
    return; 
  } 
 
  if ( devExt->State == STATE_STARTED ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_STARTED\n" ); 
    return; 
  } 
 
  if ( devExt->State == STATE_REMOVE_PENDING ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_REMOVE_PENDING\n" ); 
    return; 
  } 
 
  if ( devExt->State == STATE_SURPRISE_REMOVED ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_SURPRISE_REMOVED\n" ); 
    return; 
  } 
 
 
  if ( devExt->State == STATE_STOP_PENDING ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_STOP_PENDING\n" ); 
    return; 
  } 
 
  if ( devExt->State == STATE_STOPPED ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_STOPPED\n" ); 
    return; 
  } 
 
  if ( devExt->State == STATE_NEVER_STARTED ) 
  { 
    PB_DbgPrintL1( "ProfiM PnP: STATE_NEVER_STARTED\n" ); 
    return; 
  } 
 
  PB_DbgPrintL1( "ProfiM PnP: *********UNKNOWN STATE Value 0x%0x\n", 
                 devExt->State ); 
} 
 
 
#if PnP_DBG 
// 
// Some static string tables we use as part of debugging 
// 
static PSTR CmResourceTypeStrings[]       ={"CmResourceTypeNull", 
                                            "CmResourceTypePort", 
                                            "CmResourceTypeInterrupt", 
                                            "CmResourceTypeMemory", 
                                            "CmResourceTypeDma", 
                                            "CmResourceTypeDeviceSpecific", 
                                            "CmResourceTypeBusNumber"}; 
 
static PSTR CmResourceTypeStringsAt128[]  ={"CmResourceTypeConfigData", 
                                            "CmResourceTypeDevicePrivate", 
                                            "CmResourceTypePcCardConfig"}; 
static PSTR CmShareDispositionStrings[]   ={"CmResourceShareUndetermined", 
                                            "CmResourceShareDeviceExclusive", 
                                            "CmResourceShareDriverExclusive", 
                                            "CmResourceShareShared"}; 
 
/////////////////////////////////////////////////////////////////////////////// 
// 
//  PrintResourceList 
// 
//      This routine is called to print out the Resource descriptor list containing 
//    the resources allocated for our device by NT. 
// 
//  INPUTS: 
// 
//      Resources - Address of the CM_RESOURCE_LIST information for our device. 
// 
//  OUTPUTS: 
// 
//      None. 
// 
//  RETURNS: 
// 
//      None. 
// 
//  IRQL: 
// 
//    This routine is called at IRQL_PASSIVE_LEVEL. 
// 
//  NOTES: 
// 
//    We only use this for debugging purposes. 
// 
/////////////////////////////////////////////////////////////////////////////// 
VOID PrintResourceList( PCM_RESOURCE_LIST Resources ) 
{ 
  ULONG index, index2; 
 
  DbgPrint( "%d. resource descriptor list(s) returned\n", Resources->Count ); 
 
  for ( index = 0; index < Resources->Count; index++ ) 
  { 
    DbgPrint( "\t[%d] Version 0x%x\n", 
              index, 
              Resources->List[index].PartialResourceList.Version ); 
    DbgPrint( "\t[%d] Revision 0x%x\n", 
              index, 
              Resources->List[index].PartialResourceList.Revision ); 
 
    DbgPrint( "\t[%d] Partial Resource Descriptors %d.\n", 
              index, 
              Resources->List[index].PartialResourceList.Count ); 
    for ( index2 = 0; 
          index2 < Resources->List[index].PartialResourceList.Count; 
          index2++ ) 
    { 
      PCM_PARTIAL_RESOURCE_DESCRIPTOR prd; // Too much to type! 
 
      prd = &Resources->List[index].PartialResourceList.PartialDescriptors[index2]; 
 
      DbgPrint( "\t\t[%d] Type 0x%x (%s)\n", 
                index2, 
                prd->Type, 
                ( prd->Type < 
                  128 ? 
                  CmResourceTypeStrings[prd->Type] : 
                  CmResourceTypeStringsAt128[prd->Type - 128] ) ); 
      DbgPrint( "\t\t[%d] Share Disposition 0x%x (%s)\n", 
                index2, 
                prd->ShareDisposition, 
                CmShareDispositionStrings[prd->ShareDisposition] ); 
      DbgPrint( "\t\t[%d] Flags 0x%x\n", index2, prd->Flags ); 
      DbgPrint( "\t\t[%d] Raw 0x%x %x %x\n", 
                index2, 
                prd->u.DeviceSpecificData.DataSize, 
                prd->u.DeviceSpecificData.Reserved1, 
                prd->u.DeviceSpecificData.Reserved2 ); 
 
      switch ( prd->Type ) 
      { 
        case CmResourceTypePort: 
          if ( prd->Flags == CM_RESOURCE_PORT_MEMORY ) 
            DbgPrint( "\t\t[%d] port memory starting at 0x%x length 0x%x\n", 
                      index2, 
                      prd->u.Port.Start.LowPart, 
                      prd->u.Port.Length ); 
          if ( prd->Flags == CM_RESOURCE_PORT_IO ) 
            DbgPrint( "\t\t[%d] port i/o starting at 0x%x length 0x%x\n", 
                      index2, 
                      prd->u.Port.Start.LowPart, 
                      prd->u.Port.Length ); 
          break; 
 
        case CmResourceTypeInterrupt: 
          if ( prd->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE ) 
            DbgPrint( "\t\t[%d] level interrupt at lvl 0x%x vector 0x%x affinity 0x%x\n", 
                      index2, 
                      prd->u.Interrupt.Level, 
                      prd->u.Interrupt.Vector, 
                      prd->u.Interrupt.Affinity ); 
          if ( prd->Flags == CM_RESOURCE_INTERRUPT_LATCHED ) 
            DbgPrint( "\t\t[%d] latched interrupt at lvl 0x%x vector 0x%x affinity 0x%x\n", 
                      index2, 
                      prd->u.Interrupt.Level, 
                      prd->u.Interrupt.Vector, 
                      prd->u.Interrupt.Affinity ); 
          break; 
 
        case CmResourceTypeMemory: 
          if ( prd->Flags == CM_RESOURCE_MEMORY_READ_WRITE ) 
            DbgPrint( "\t\t[%d] r/w memory starting at 0x%x length 0x%x\n", 
                      index2, 
                      prd->u.Memory.Start.LowPart, 
                      prd->u.Memory.Length ); 
          if ( prd->Flags & CM_RESOURCE_MEMORY_READ_ONLY ) 
            DbgPrint( "\t\t[%d] r/o memory starting at 0x%x length 0x%x\n", 
                      index2, 
                      prd->u.Memory.Start.LowPart, 
                      prd->u.Memory.Length ); 
          if ( prd->Flags & CM_RESOURCE_MEMORY_WRITE_ONLY ) 
            DbgPrint( "\t\t[%d] w/o memory starting at 0x%x length 0x%x\n", 
                      index2, 
                      prd->u.Memory.Start.LowPart, 
                      prd->u.Memory.Length ); 
          break; 
 
        case CmResourceTypeDma: 
          DbgPrint( "\t\t[%d] DMA on channel 0x%x\n", 
                    index2, 
                    prd->u.Dma.Channel ); 
          break; 
 
        case CmResourceTypeDeviceSpecific: 
          DbgPrint( "\t\t[%d] Device specific data at 0x%x length 0x%x\n", 
                    index2, 
                    ( ( ULONG ) & prd->u.DeviceSpecificData.Reserved2 ) + 
                    ( ULONG )sizeof( ULONG ), 
                    prd->u.DeviceSpecificData.DataSize ); 
          break; 
 
        case CmResourceTypeBusNumber: 
          DbgPrint( "\t\t[%d] Bus Number 0x%x length 0x%x, Reserved 0x%0x\n", 
                    index2, 
                    prd->u.BusNumber.Start, 
                    prd->u.BusNumber.Length, 
                    prd->u.BusNumber.Reserved );  
 
          break; 
 
        case CmResourceTypeDevicePrivate: 
          DbgPrint( "\t\t[%d] Device Private data 0x%0x 0x%0x 0x%0x\n", 
                    prd->u.DevicePrivate.Data[0], 
                    prd->u.DevicePrivate.Data[1], 
                    prd->u.DevicePrivate.Data[2] ); 
 
          break; 
 
        default: 
          // 
          // Say what?!!  Unknown resource type.  Something is pretty wierd here. 
          // 
          DbgPrint( "Other resource type %d.\n", prd->Type ); 
          break; 
      } 
    } 
  } 
  DbgPrint( "\t[%d] ***** End dump ******\n", index ); 
} 
#endif /* PnP_DBG */