2616 lines
88 KiB
C
2616 lines
88 KiB
C
#include "pch.h"
|
||
|
||
VOID
|
||
P5WorkItemFreePort( PDEVICE_OBJECT Fdo, PFDO_EXTENSION Fdx ) {
|
||
PIO_WORKITEM workItem;
|
||
|
||
UNREFERENCED_PARAMETER( Fdo );
|
||
|
||
workItem = InterlockedExchangePointer( &Fdx->FreePortWorkItem, NULL );
|
||
if( workItem ) {
|
||
IoFreeWorkItem( workItem );
|
||
}
|
||
|
||
PptFreePort( Fdx );
|
||
}
|
||
|
||
BOOLEAN
|
||
P5SelectDaisyChainDevice(
|
||
IN PUCHAR Controller,
|
||
IN UCHAR DeviceId
|
||
)
|
||
{
|
||
const ULONG maxRetries = 4;
|
||
ULONG retryCount = 0;
|
||
BOOLEAN selected = FALSE;
|
||
DD(NULL,DDE,"P5SelectDaisyChainDevice %x %d\n",Controller,DeviceId);
|
||
while( !selected && retryCount < maxRetries ) {
|
||
selected = PptSend1284_3Command( Controller, (UCHAR)(CPP_SELECT | DeviceId) );
|
||
++retryCount;
|
||
}
|
||
return selected;
|
||
}
|
||
|
||
BOOLEAN
|
||
P5DeselectAllDaisyChainDevices(
|
||
IN PUCHAR Controller
|
||
)
|
||
{
|
||
const ULONG maxRetries = 4;
|
||
ULONG retryCount = 0;
|
||
BOOLEAN deselected = FALSE;
|
||
DD(NULL,DDE,"P5DeselectAllDaisyChainDevices %x\n",Controller);
|
||
while( !deselected && retryCount < maxRetries ) {
|
||
deselected = PptSend1284_3Command( Controller, (UCHAR)CPP_DESELECT );
|
||
++retryCount;
|
||
}
|
||
return deselected;
|
||
}
|
||
|
||
VOID
|
||
P5DeletePdoSymLink(
|
||
IN PDEVICE_OBJECT Pdo
|
||
)
|
||
//
|
||
// clean up symbolic link so we can reuse it immediately for a new PDO
|
||
//
|
||
{
|
||
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
|
||
|
||
if( pdx->SymLinkName ) {
|
||
UNICODE_STRING uniSymLinkName;
|
||
NTSTATUS status;
|
||
|
||
DD((PCE)pdx,DDE,"P5DeletePdoSymLink\n");
|
||
|
||
RtlInitUnicodeString( &uniSymLinkName, pdx->SymLinkName );
|
||
status = IoDeleteSymbolicLink( &uniSymLinkName );
|
||
PptAssert( STATUS_SUCCESS == status );
|
||
ExFreePool( pdx->SymLinkName );
|
||
pdx->SymLinkName = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
P5MarkPdoAsHardwareGone(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN enum _PdoType PdoType,
|
||
IN ULONG DaisyChainId OPTIONAL // ignored if PdoType != PdoTypeDaisyChain
|
||
)
|
||
{
|
||
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
||
PPDO_EXTENSION pdx;
|
||
PDEVICE_OBJECT pdo;
|
||
|
||
switch( PdoType ) {
|
||
|
||
case PdoTypeRawPort:
|
||
|
||
DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeRawPort\n");
|
||
pdo = fdx->RawPortPdo;
|
||
fdx->RawPortPdo = NULL;
|
||
break;
|
||
|
||
case PdoTypeEndOfChain:
|
||
|
||
DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeEndOfChain\n");
|
||
pdo = fdx->EndOfChainPdo;
|
||
fdx->EndOfChainPdo = NULL;
|
||
break;
|
||
|
||
case PdoTypeDaisyChain:
|
||
|
||
PptAssert( (0 == DaisyChainId) || (1 == DaisyChainId) );
|
||
DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeDaisyChain - %d\n",DaisyChainId);
|
||
pdo = fdx->DaisyChainPdo[ DaisyChainId ];
|
||
fdx->DaisyChainPdo[ DaisyChainId ] = NULL;
|
||
break;
|
||
|
||
case PdoTypeLegacyZip:
|
||
|
||
DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - PdoTypeLegacyZip\n");
|
||
pdo = fdx->LegacyZipPdo;
|
||
fdx->LegacyZipPdo = NULL;
|
||
break;
|
||
|
||
default:
|
||
|
||
DD((PCE)fdx,DDE,"P5MarkPdoAsHardwareGone - Invalid PdoType parameter\n",FALSE);
|
||
PptAssertMsg("P5MarkPdoAsHardwareGone - Invalid PdoType parameter",FALSE);
|
||
return;
|
||
|
||
}
|
||
|
||
pdx = pdo->DeviceExtension;
|
||
P5DeletePdoSymLink( pdo );
|
||
InsertTailList( &fdx->DevDeletionListHead, &pdx->DevDeletionList );
|
||
pdx->DeleteOnRemoveOk = TRUE;
|
||
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
P5IsDeviceStillThere(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PDEVICE_OBJECT Pdo
|
||
)
|
||
//
|
||
// Is the Pdo device still connected to the port represented by the Fdo?
|
||
//
|
||
// N.B. Fdo must own (have locked for exclusive access) the port before calling this function
|
||
// or we can corrupt the data stream and hang devices connected to the port
|
||
//
|
||
{
|
||
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
||
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
|
||
BOOLEAN deviceStillThere = FALSE;
|
||
PCHAR devIdString = NULL;
|
||
PUCHAR controller = fdx->PortInfo.Controller;
|
||
|
||
PptAssert( DevTypeFdo == fdx->DevType );
|
||
PptAssert( DevTypePdo == pdx->DevType );
|
||
|
||
//
|
||
// Select device if needed, pull a fresh 1284 device ID string
|
||
// from the device, and compare the Mfg and Mdl from the fresh
|
||
// device ID with those stored in our extension. If the Mfg and
|
||
// Mdl fields match then the device is still there.
|
||
//
|
||
|
||
switch( pdx->PdoType ) {
|
||
|
||
case PdoTypeRawPort:
|
||
|
||
// raw port is always present - it's a virtual device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeRawPort - StillThere\n");
|
||
deviceStillThere = TRUE;
|
||
break;
|
||
|
||
case PdoTypeLegacyZip:
|
||
|
||
deviceStillThere = P5LegacyZipDetected( fdx->PortInfo.Controller );
|
||
if( deviceStillThere ) {
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeLegacyZip - StillThere\n");
|
||
} else {
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeLegacyZip - Gone\n");
|
||
}
|
||
break;
|
||
|
||
case PdoTypeDaisyChain:
|
||
|
||
//
|
||
// Select device, pull a fresh 1284 device ID string
|
||
// from the device, and compare the Mfg and Mdl from the fresh
|
||
// device ID with those stored in our extension. If the Mfg and
|
||
// Mdl fields match then the device is still there.
|
||
//
|
||
|
||
{
|
||
UCHAR daisyChainId = pdx->Ieee1284_3DeviceId;
|
||
|
||
// select device
|
||
if( P5SelectDaisyChainDevice( controller, daisyChainId ) ) {
|
||
|
||
BOOLEAN bBuildStlDeviceId = FALSE;
|
||
PPDO_EXTENSION dummyPdx = NULL;
|
||
|
||
devIdString = NULL;
|
||
|
||
// do a check to see if this is an SCM Micro device
|
||
dummyPdx = ExAllocatePool( PagedPool, sizeof(PDO_EXTENSION) );
|
||
if( dummyPdx != NULL ) {
|
||
RtlZeroMemory( dummyPdx, sizeof(PDO_EXTENSION) );
|
||
dummyPdx->Controller = fdx->PortInfo.Controller;
|
||
bBuildStlDeviceId = ParStlCheckIfStl( dummyPdx, daisyChainId );
|
||
|
||
if( bBuildStlDeviceId ) {
|
||
// SCM Micro device
|
||
ULONG DeviceIdSize;
|
||
devIdString = ParStlQueryStlDeviceId( dummyPdx, NULL, 0,&DeviceIdSize, TRUE );
|
||
} else {
|
||
// non-SCM Micro device
|
||
devIdString = P4ReadRawIeee1284DeviceId( controller );
|
||
}
|
||
ExFreePool( dummyPdx );
|
||
}
|
||
|
||
if( devIdString ) {
|
||
// got a 1284 device ID string from the device
|
||
PCHAR mfg, mdl, cls, des, aid, cid;
|
||
ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, devIdString+2 );
|
||
if( mfg && mdl ) {
|
||
// we have a device, is it the same device?
|
||
if( (0 == strcmp( mfg, pdx->Mfg )) && (0 == strcmp( mdl, pdx->Mdl )) ) {
|
||
// same device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - StillThere\n",daisyChainId);
|
||
deviceStillThere = TRUE;
|
||
} else {
|
||
// different device - IDs don't match
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - diff 1284 ID\n",daisyChainId);
|
||
deviceStillThere = FALSE;
|
||
}
|
||
} else {
|
||
// either mfg or mdl field not found
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - bad 1284 ID\n",daisyChainId);
|
||
deviceStillThere = FALSE;
|
||
}
|
||
// don't forget to free temp pool
|
||
ExFreePool( devIdString );
|
||
|
||
} else {
|
||
// unable to get a 1284 device ID string from the device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - no 1284 ID\n",daisyChainId);
|
||
deviceStillThere = FALSE;
|
||
}
|
||
// don't forget to deselect device
|
||
P5DeselectAllDaisyChainDevices( controller );
|
||
|
||
} else {
|
||
// unable to select device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeDaisyChain %d - Gone - unable to select\n",daisyChainId);
|
||
deviceStillThere = FALSE;
|
||
}
|
||
} // end new scope for case PdoTypeDaisyChain
|
||
break;
|
||
|
||
case PdoTypeEndOfChain:
|
||
|
||
//
|
||
// Pull a fresh 1284 device ID string from the device, and
|
||
// compare the Mfg and Mdl from the fresh device ID with
|
||
// those stored in our extension. If the Mfg and Mdl
|
||
// fields match then the device is still there.
|
||
//
|
||
{
|
||
ULONG tryNumber = 0;
|
||
const ULONG maxTries = 5; // arbitrary number
|
||
|
||
do {
|
||
|
||
++tryNumber;
|
||
|
||
devIdString = P4ReadRawIeee1284DeviceId( controller );
|
||
|
||
if( devIdString ) {
|
||
PCHAR mfg, mdl, cls, des, aid, cid;
|
||
ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, devIdString+2 );
|
||
if( mfg && mdl ) {
|
||
// we have a device, is it the same device?
|
||
if( (0 == strcmp( mfg, pdx->Mfg )) && (0 == strcmp( mdl, pdx->Mdl )) ) {
|
||
// same device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - StillThere\n");
|
||
deviceStillThere = TRUE;
|
||
} else {
|
||
// different device - IDs don't match
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - diff 1284 ID\n");
|
||
deviceStillThere = FALSE;
|
||
}
|
||
} else {
|
||
// either mfg or mdl field not found
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - bad 1284 ID\n");
|
||
deviceStillThere = FALSE;
|
||
}
|
||
// don't forget to free temp pool
|
||
ExFreePool( devIdString );
|
||
} else {
|
||
// unable to get a 1284 device ID string from the device
|
||
DD((PCE)pdx,DDE,"P5IsDeviceStillThere - PdoTypeEndOfChain - Gone - no 1284 ID\n");
|
||
deviceStillThere = FALSE;
|
||
}
|
||
|
||
if( (FALSE == deviceStillThere ) && (PASSIVE_LEVEL == KeGetCurrentIrql()) ) {
|
||
LARGE_INTEGER delay;
|
||
delay.QuadPart = - 10 * 1000 * 120; // 120 ms - 3x the usual arbitrary delay
|
||
KeDelayExecutionThread( KernelMode, FALSE, &delay);
|
||
}
|
||
|
||
} while( (FALSE == deviceStillThere) && (tryNumber < maxTries) );
|
||
|
||
}
|
||
break;
|
||
|
||
default:
|
||
|
||
PptAssertMsg("P5IsDeviceStillThere - invalid PdoType",FALSE);
|
||
DD((PCE)Fdo,DDE,"P5IsDeviceStillThere - invalid PdoType\n");
|
||
deviceStillThere = TRUE; // don't know what to do here - so, guess
|
||
}
|
||
|
||
return deviceStillThere;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PptAcquirePortViaIoctl(
|
||
IN PDEVICE_OBJECT PortDeviceObject,
|
||
IN PLARGE_INTEGER Timeout OPTIONAL
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires the specified parallel port from the parallel
|
||
port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE.
|
||
|
||
Arguments:
|
||
|
||
PortDeviceObject - points to the ParPort device to be acquired
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the port was successfully acquired
|
||
!STATUS_SUCCESS - otherwise
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER localTimeout;
|
||
|
||
if( Timeout ) {
|
||
localTimeout = *Timeout; // caller specified
|
||
} else {
|
||
localTimeout = AcquirePortTimeout; // driver global variable default
|
||
}
|
||
|
||
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE,
|
||
PortDeviceObject, NULL, 0, NULL, 0, &localTimeout);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PptReleasePortViaIoctl(
|
||
IN PDEVICE_OBJECT PortDeviceObject
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine releases the specified parallel port back to the the parallel
|
||
port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_FREE.
|
||
|
||
Arguments:
|
||
|
||
PortDeviceObject - points to the ParPort device to be released
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the port was successfully released
|
||
!STATUS_SUCCESS - otherwise
|
||
|
||
--*/
|
||
{
|
||
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_FREE,
|
||
PortDeviceObject, NULL, 0, NULL, 0, NULL);
|
||
}
|
||
|
||
|
||
VOID
|
||
PptWriteMfgMdlToDevNode(
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN PCHAR Mfg,
|
||
IN PCHAR Mdl
|
||
)
|
||
{
|
||
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
|
||
|
||
if( Mfg && Mdl ) {
|
||
|
||
NTSTATUS status;
|
||
HANDLE handle;
|
||
LONG mfgLen = strlen( Mfg );
|
||
LONG mdlLen = strlen( Mdl );
|
||
LONG maxLen = mfgLen > mdlLen ? mfgLen : mdlLen;
|
||
LONG bufLen = ( maxLen + sizeof(CHAR) ) * sizeof(WCHAR);
|
||
PWSTR buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
|
||
|
||
if( buffer ) {
|
||
|
||
status = IoOpenDeviceRegistryKey( Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &handle );
|
||
|
||
if( STATUS_SUCCESS == status ) {
|
||
UNICODE_STRING uniValueName;
|
||
LONG wcharCount;
|
||
|
||
//
|
||
// Write MFG to DevNode
|
||
//
|
||
RtlInitUnicodeString( &uniValueName, L"IEEE_1284_Manufacturer" );
|
||
wcharCount = _snwprintf( buffer, bufLen/sizeof(WCHAR), L"%S", Mfg );
|
||
if( (wcharCount > 0) && (wcharCount < (LONG)(bufLen/sizeof(WCHAR))) ){
|
||
// no buffer overflow - continue
|
||
status = ZwSetValueKey( handle, &uniValueName, 0, REG_SZ, buffer, (wcharCount+1)*sizeof(WCHAR) );
|
||
PptAssert( STATUS_SUCCESS == status );
|
||
} else {
|
||
// buffer overflow - skip writing this value to devnode
|
||
PptAssert(!"PptWriteMfgMdlToDevNode - buffer overflow on Mfg");
|
||
DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - buffer overflow on Mfg\n");
|
||
}
|
||
|
||
//
|
||
// Write MDL to DevNode
|
||
//
|
||
RtlInitUnicodeString( &uniValueName, L"IEEE_1284_Model" );
|
||
wcharCount = _snwprintf( buffer, bufLen/sizeof(WCHAR), L"%S", Mdl );
|
||
if( (wcharCount > 0) && (wcharCount < (LONG)(bufLen/sizeof(WCHAR))) ){
|
||
// no buffer overflow - continue
|
||
status = ZwSetValueKey( handle, &uniValueName, 0, REG_SZ, buffer, (wcharCount+1)*sizeof(WCHAR) );
|
||
PptAssert( STATUS_SUCCESS == status );
|
||
} else {
|
||
// buffer overflow - skip writing this value to devnode
|
||
PptAssert(!"PptWriteMfgMdlToDevNode - buffer overflow on Mdl");
|
||
DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - buffer overflow on Mdl\n");
|
||
}
|
||
|
||
ZwClose( handle );
|
||
|
||
} else {
|
||
DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - IoOpenDeviceRegistryKey FAILED - status = %x\n",status);
|
||
}
|
||
|
||
ExFreePool( buffer );
|
||
|
||
} // end if( buffer )
|
||
|
||
} else {
|
||
PptAssert(!"PptWriteMfgMdlToDevNode - Mfg or Mdl is NULL - calling function should catch this!");
|
||
DD((PCE)pdx,DDW,"PptWriteMfgMdlToDevNode - Mfg or Mdl is NULL - calling function should catch this!");
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
PptFdoHandleBusRelations(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
||
ULONG deviceCount = 0;
|
||
ULONG daisyChainDevCount;
|
||
PDEVICE_RELATIONS devRel;
|
||
ULONG devRelSize;
|
||
NTSTATUS status;
|
||
LARGE_INTEGER acquirePortTimeout;
|
||
BOOLEAN acquiredPort;
|
||
PUCHAR controller = fdx->PortInfo.Controller;
|
||
BOOLEAN changeDetected;
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - enter\n");
|
||
|
||
//
|
||
// acquire exclusive access to bus
|
||
//
|
||
|
||
// timeout is in 100 ns units
|
||
acquirePortTimeout.QuadPart = -(10 * 1000 * 1000 * 2); // 2 seconds
|
||
|
||
// RMT - is it valid to send this IOCTL to FDO from here?
|
||
status = PptAcquirePortViaIoctl( Fdo, &acquirePortTimeout );
|
||
|
||
if( STATUS_SUCCESS == status ) {
|
||
// we have the port
|
||
acquiredPort = TRUE;
|
||
} else {
|
||
// failed to aquire port
|
||
acquiredPort = FALSE;
|
||
// skip rescanning port - just report same thing we reported during previous scan
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - failed to acquire port for rescan\n");
|
||
goto target_failed_to_acquire_port;
|
||
}
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Acquired\n");
|
||
|
||
//
|
||
// Rescan the bus, note changes, create new PDOs or mark existing
|
||
// PDOs for removal as required
|
||
//
|
||
|
||
|
||
//
|
||
// Handle Raw Port Legacy Interface LPTx device
|
||
//
|
||
if( !fdx->RawPortPdo ) {
|
||
// first time through this - create our LPTx legacy interface PDO
|
||
DD((PCE)fdx,DDT,"PptFdoHandleBusRelations - attempting to create RawPortPdo\n");
|
||
fdx->RawPortPdo = P4CreatePdo( Fdo, PdoTypeRawPort, 0, NULL );
|
||
}
|
||
|
||
|
||
//
|
||
// Handle End of Chain Device
|
||
//
|
||
|
||
// make sure all 1284.3 daisy chain devices are deselected
|
||
P5DeselectAllDaisyChainDevices( controller );
|
||
|
||
{
|
||
// A small delay here seems to improve reliablility of 1284 device ID queries below.
|
||
LARGE_INTEGER delay;
|
||
delay.QuadPart = -1;
|
||
KeDelayExecutionThread( KernelMode, FALSE, &delay );
|
||
}
|
||
|
||
if( fdx->EndOfChainPdo ) {
|
||
|
||
if( fdx->DisableEndOfChainBusRescan ) {
|
||
|
||
//
|
||
// Pretend that the LPTx.4 device from previous rescan is still present.
|
||
//
|
||
// This is needed to work around firmware state machines that can't handle a
|
||
// 1284 Device ID query while a print job is active.
|
||
//
|
||
|
||
; // do nothing
|
||
|
||
} else {
|
||
|
||
//
|
||
// we had an end of chain device - verify that it's still there
|
||
//
|
||
if( !P5IsDeviceStillThere( Fdo, fdx->EndOfChainPdo ) ) {
|
||
// End of chain device is gone - do some cleanup and mark the PDO for removal/deletion
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - EndOfChain device gone\n");
|
||
// note - P5MarkPdoAsHardwareGone sets fdx->EndOfChainPdo to NULL
|
||
P5MarkPdoAsHardwareGone( Fdo, PdoTypeEndOfChain, 0 );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( NULL == fdx->EndOfChainPdo ) {
|
||
//
|
||
// we don't have an EndOfChain device - check for EndOfChain device arrival
|
||
//
|
||
PCHAR devId = P4ReadRawIeee1284DeviceId( controller );
|
||
if( devId ) {
|
||
// RawIeee1284 string includes 2 bytes of length data at beginning, omit these 2 bytes in call to P4CreatePdo
|
||
PDEVICE_OBJECT EndOfChainPdo = P4CreatePdo( Fdo, PdoTypeEndOfChain, 0, (devId+2) );
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - EndOfChain device detected <%s>\n",(devId+2));
|
||
if( EndOfChainPdo ) {
|
||
fdx->EndOfChainPdo = EndOfChainPdo;
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - created EndOfChainPdo\n");
|
||
} else {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - FAILED to create EndOfChainPdo\n");
|
||
}
|
||
ExFreePool( devId );
|
||
}
|
||
}
|
||
|
||
|
||
#ifdef _X86_ // Zip drives not supported on 64bit systems
|
||
|
||
//
|
||
// Handle Legacy Zip device
|
||
//
|
||
|
||
if( fdx->LegacyZipPdo ) {
|
||
//
|
||
// we had a Legacy Zip device - verify that it's still there
|
||
//
|
||
if( !P5IsDeviceStillThere( Fdo, fdx->LegacyZipPdo ) ) {
|
||
// Legacy Zip device is gone - do some cleanup and mark the PDO for removal/deletion
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - LegacyZip device gone\n");
|
||
// note - P5MarkPdoAsHardwareGone sets fdx->LegacyZipPdo to NULL
|
||
P5MarkPdoAsHardwareGone( Fdo, PdoTypeLegacyZip, 0 );
|
||
}
|
||
}
|
||
|
||
if( NULL == fdx->LegacyZipPdo ) {
|
||
//
|
||
// We don't have a LegacyZip - check for arrival
|
||
//
|
||
if( !ParEnableLegacyZip ) {
|
||
|
||
//
|
||
// Enumeration of LegacyZip drives was disabled, check the
|
||
// registry to see if user has enabled LegacyZip detection
|
||
//
|
||
|
||
// Check under \HKLM\SYSTEM\CCS\Services\Parport\Parameters
|
||
PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
|
||
|
||
if( !ParEnableLegacyZip ) {
|
||
// Check under \HKLM\SYSTEM\CCS\Services\Parallel\Parameters (upgrade case - under Win2k flag was here)
|
||
PptRegGetDword( RTL_REGISTRY_SERVICES, L"Parallel\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
|
||
|
||
if( ParEnableLegacyZip ) {
|
||
// we found the setting in the old location, save
|
||
// setting in new Parport location so that we find the
|
||
// flag on the first check in the future
|
||
PptRegSetDword( RTL_REGISTRY_SERVICES, L"Parport\\Parameters", L"ParEnableLegacyZip", &ParEnableLegacyZip );
|
||
}
|
||
}
|
||
}
|
||
|
||
if( ParEnableLegacyZip ) {
|
||
|
||
//
|
||
// Enumeration of LegacyZip drives is enabled - check for a LegacyZip arrival
|
||
//
|
||
|
||
if( P5LegacyZipDetected( controller ) ) {
|
||
// detected drive - create LegacyZip PDO
|
||
PDEVICE_OBJECT legacyZipPdo = P4CreatePdo( Fdo, PdoTypeLegacyZip, 0, NULL );
|
||
DD((PCE)fdx,DDE,"legacy Zip arrival detected\n");
|
||
if( legacyZipPdo ) {
|
||
fdx->LegacyZipPdo = legacyZipPdo;
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - created LegacyZipPdo\n");
|
||
} else {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - FAILED to create LegacyZipPdo\n");
|
||
}
|
||
} else {
|
||
// no legacy Zip detected - nothing more to do here
|
||
DD((PCE)fdx,DDE,"no legacy Zip detected\n");
|
||
}
|
||
|
||
} // if( ParEnableLegacyZip ) -- Detection of LegacyZips is enabled
|
||
|
||
} // if( fdx->LegacyZipPdo )
|
||
|
||
|
||
//
|
||
// Handle enumeration of IEEE 1284.3 Daisy Chain Devices
|
||
//
|
||
|
||
// did the 1284.3 daisy chain change since the last rescan?
|
||
daisyChainDevCount = PptInitiate1284_3( fdx );
|
||
DD((PCE)fdx,DDW,"daisyChainDevCount = %d\n",daisyChainDevCount);
|
||
|
||
changeDetected = FALSE;
|
||
|
||
{
|
||
ULONG id;
|
||
const ULONG maxId = 1;
|
||
ULONG count = 0;
|
||
for( id = 0 ; id <= maxId ; ++id ) {
|
||
if( fdx->DaisyChainPdo[id] ) {
|
||
++count;
|
||
}
|
||
}
|
||
if( count != daisyChainDevCount ) {
|
||
// number of devices changed from previous scan
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - number of DC devices changed - count=%d, daisyChainDevCount=%d\n",
|
||
count, daisyChainDevCount);
|
||
changeDetected = TRUE;
|
||
}
|
||
}
|
||
|
||
if( !changeDetected ) {
|
||
// number of devices stayed the same - are any of the devices different?
|
||
//
|
||
// number of daisy chain devices didn't change
|
||
// check if any of the devices changed
|
||
ULONG id;
|
||
const ULONG maxId = 1;
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - number of DC devices stayed same - check the devices\n");
|
||
for( id = 0 ; id <= maxId ; ++id ) {
|
||
if( fdx->DaisyChainPdo[id] && !P5IsDeviceStillThere( Fdo, fdx->DaisyChainPdo[id] ) ) {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - a DC device changed\n");
|
||
changeDetected = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if( changeDetected ) {
|
||
// we detected a change in the 1284.3 daisy chain devices - nuke all existing devices
|
||
ULONG id;
|
||
const ULONG maxId = 1;
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - changeDetected - nuking existing daisy chain PDOs\n");
|
||
for( id = 0 ; id <= maxId ; ++id ) {
|
||
if( fdx->DaisyChainPdo[id] ) {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - nuking daisy chain %d\n",id);
|
||
P5MarkPdoAsHardwareGone( Fdo, PdoTypeDaisyChain, id );
|
||
PptAssert( NULL == fdx->DaisyChainPdo[id] );
|
||
}
|
||
}
|
||
fdx->PnpInfo.Ieee1284_3DeviceCount = 0;
|
||
} else {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - !changeDetected in daisy chain PDOs\n");
|
||
}
|
||
|
||
// reinit daisy chain and assign addresses
|
||
daisyChainDevCount = PptInitiate1284_3( fdx );
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - daisyChainDevCount = %d\n",daisyChainDevCount);
|
||
if( daisyChainDevCount > 2 ) {
|
||
// we only support 2 devices per port even though the spec supports up to 4 devices per port
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - DaisyChainDevCount > 2, set to 2\n");
|
||
daisyChainDevCount = 2;
|
||
}
|
||
|
||
if( changeDetected ) {
|
||
// we detected a change in the 1284.3 daisy chain devices - we
|
||
// previously nuked all old devices - now create a new PDO for
|
||
// each device detected
|
||
UCHAR id;
|
||
PptAssert( 0 == fdx->PnpInfo.Ieee1284_3DeviceCount );
|
||
for( id = 0 ; id < daisyChainDevCount ; ++id ) {
|
||
|
||
BOOLEAN bBuildStlDeviceId = FALSE;
|
||
PPDO_EXTENSION pdx = NULL;
|
||
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - changeDetected - trying to create new daisy chain PDOs\n");
|
||
|
||
if( P5SelectDaisyChainDevice( controller, id ) ) {
|
||
|
||
PCHAR devId = NULL;
|
||
|
||
// do a check to see if this is an SCM Micro device
|
||
pdx = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, sizeof(PDO_EXTENSION) );
|
||
if( pdx != NULL ) {
|
||
RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
|
||
pdx->Controller = fdx->PortInfo.Controller;
|
||
bBuildStlDeviceId = ParStlCheckIfStl( pdx, id );
|
||
ExFreePool( pdx );
|
||
}
|
||
|
||
|
||
if( bBuildStlDeviceId ) {
|
||
|
||
// SCM Micro device
|
||
pdx = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, sizeof(PDO_EXTENSION) );
|
||
if( pdx != NULL ) {
|
||
ULONG DeviceIdSize;
|
||
RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
|
||
pdx->Controller = fdx->PortInfo.Controller;
|
||
devId = ParStlQueryStlDeviceId(pdx, NULL, 0,&DeviceIdSize, TRUE);
|
||
ExFreePool (pdx);
|
||
}
|
||
|
||
} else {
|
||
|
||
// non-SCM Micro device
|
||
devId = P4ReadRawIeee1284DeviceId( controller );
|
||
|
||
}
|
||
|
||
if( devId ) {
|
||
|
||
// try to create a PDO for the daisy chain device
|
||
fdx->DaisyChainPdo[id] = P4CreatePdo( Fdo, PdoTypeDaisyChain, id, (devId+2) );
|
||
|
||
if( fdx->DaisyChainPdo[id] ) {
|
||
// have new PDO
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - new DaisyChainPdo[%d]\n",id);
|
||
++(fdx->PnpInfo.Ieee1284_3DeviceCount);
|
||
|
||
if( bBuildStlDeviceId ) {
|
||
// SCM Micro device - requires additional initialization
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - new SCM Micro DaisyChainPdo[%d]\n",id);
|
||
pdx = fdx->DaisyChainPdo[id]->DeviceExtension;
|
||
pdx->Controller = fdx->PortInfo.Controller;
|
||
ParStlCheckIfStl( pdx, 0 ); // update IEEE 1284 flags in the new pdx
|
||
}
|
||
|
||
} else {
|
||
// create PDO failed
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - create DaisyChainPdo[%d] failed\n",id);
|
||
}
|
||
ExFreePool( devId );
|
||
} else {
|
||
// devId failed
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - devId for DC %d failed\n",id);
|
||
}
|
||
P5DeselectAllDaisyChainDevices( controller );
|
||
} else {
|
||
// select failed
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - select for DC %d failed\n",id);
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG count = 0;
|
||
i = 0;
|
||
for( i = 0 ; i < 2 ; ++i ) {
|
||
if( fdx->DaisyChainPdo[i] ) {
|
||
++count;
|
||
}
|
||
}
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Ieee1284_3DeviceCount=%d count1 = %d\n",
|
||
fdx->PnpInfo.Ieee1284_3DeviceCount,count);
|
||
PptAssert( fdx->PnpInfo.Ieee1284_3DeviceCount == count );
|
||
}
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - daisyChainDevCount = %d, fdx->PnpInfo.Ieee1284_3DeviceCount = %d\n",
|
||
daisyChainDevCount, fdx->PnpInfo.Ieee1284_3DeviceCount);
|
||
|
||
// PptAssert( daisyChainDevCount == fdx->PnpInfo.Ieee1284_3DeviceCount );
|
||
|
||
#endif // _X86_
|
||
|
||
target_failed_to_acquire_port: // jump here if we couldn't get the port - result is that we report that nothing has changed
|
||
|
||
//
|
||
// Count the number of devices that we are going to report to PnP
|
||
// so that we can allocate a DEVICE_RELATIONS structure of the
|
||
// appropriate size.
|
||
//
|
||
|
||
if( fdx->RawPortPdo ) {
|
||
++deviceCount;
|
||
}
|
||
|
||
if( fdx->EndOfChainPdo ) {
|
||
++deviceCount;
|
||
}
|
||
|
||
if( fdx->LegacyZipPdo ) {
|
||
++deviceCount;
|
||
}
|
||
|
||
{
|
||
const ULONG maxDaisyChainId = 1;
|
||
ULONG i;
|
||
for( i=0 ; i <= maxDaisyChainId; ++i ) {
|
||
if( fdx->DaisyChainPdo[i] ) {
|
||
++deviceCount;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if( deviceCount > 0 && fdx->RawPortPdo ) {
|
||
|
||
//
|
||
// Allocate and populate DEVICE_RELATIONS structure that we return to PnP
|
||
//
|
||
|
||
devRelSize = sizeof(DEVICE_RELATIONS) + (deviceCount-1)*sizeof(PDEVICE_OBJECT);
|
||
devRel = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, devRelSize );
|
||
|
||
if( !devRel ) {
|
||
// release port and fail IRP
|
||
P4ReleaseBus( Fdo );
|
||
return P4CompleteRequestReleaseRemLock( Irp, STATUS_INSUFFICIENT_RESOURCES, Irp->IoStatus.Information, &fdx->RemoveLock );
|
||
}
|
||
|
||
{ // local block - begin
|
||
ULONG idx = 0;
|
||
|
||
RtlZeroMemory( devRel, devRelSize );
|
||
|
||
++(devRel->Count);
|
||
ObReferenceObject( fdx->RawPortPdo );
|
||
devRel->Objects[ idx++ ] = fdx->RawPortPdo;
|
||
|
||
if( fdx->EndOfChainPdo ) {
|
||
++(devRel->Count);
|
||
ObReferenceObject( fdx->EndOfChainPdo );
|
||
devRel->Objects[ idx++ ] = fdx->EndOfChainPdo;
|
||
}
|
||
|
||
if( fdx->LegacyZipPdo ) {
|
||
++(devRel->Count);
|
||
ObReferenceObject( fdx->LegacyZipPdo );
|
||
devRel->Objects[ idx++ ] = fdx->LegacyZipPdo;
|
||
}
|
||
|
||
{
|
||
const ULONG maxDaisyChainId = 3;
|
||
ULONG i;
|
||
|
||
for( i=0 ; i <= maxDaisyChainId; ++i ) {
|
||
if( fdx->DaisyChainPdo[i] ) {
|
||
++(devRel->Count);
|
||
ObReferenceObject( fdx->DaisyChainPdo[i] );
|
||
devRel->Objects[ idx++ ] = fdx->DaisyChainPdo[i];
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
} // local block - end
|
||
|
||
PptAssert( deviceCount == devRel->Count ); // verify that our two counts match
|
||
|
||
DD((PCE)fdx,DDE,"PptFdoHandleBusRelations - reporting %d devices\n",devRel->Count);
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = (ULONG_PTR)devRel;
|
||
} else {
|
||
// deviceCount <= 0 - error somewhere - likely two ports
|
||
// have the same LPTx name in the FDO stack's devnode
|
||
|
||
// RMT - this assert needs to be changed to ErrorLog msg
|
||
PptAssert(!"no RawPort device - likely multiple ports have same LPTx name - email: DFritz");
|
||
}
|
||
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - passing IRP down stack\n");
|
||
|
||
status = PptPnpPassThroughPnpIrpAndReleaseRemoveLock( fdx, Irp );
|
||
|
||
//
|
||
// Release our lock on the bus and pass Irp down the stack
|
||
//
|
||
if( acquiredPort ) {
|
||
PIO_WORKITEM workItem = IoAllocateWorkItem( Fdo );
|
||
if( workItem ) {
|
||
|
||
PIO_WORKITEM oldWorkItem = InterlockedCompareExchangePointer( &fdx->FreePortWorkItem, workItem, NULL );
|
||
if( NULL == oldWorkItem ) {
|
||
|
||
// no workitem currently in use, queue this one
|
||
IoQueueWorkItem( workItem, P5WorkItemFreePort, DelayedWorkQueue, fdx );
|
||
|
||
} else {
|
||
|
||
// there is already a workitem in use, bail out and recover as best we can
|
||
|
||
// We really shouldn't be able to get here - how in blazes did we
|
||
// acquire the port at the top of this function if the workitem
|
||
// that we queued to free the port during the previous invocation
|
||
// of this function has not yet freed the port?
|
||
|
||
PptAssertMsg( "workitem collision - port arbitration state may be hosed", (oldWorkItem != NULL) );
|
||
IoFreeWorkItem( workItem );
|
||
PptFreePort( fdx );
|
||
|
||
}
|
||
|
||
} else {
|
||
PptFreePort( fdx );
|
||
}
|
||
// DbgPrint("xxx work item to free port has been queued\n");
|
||
//DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Releasing Port\n");
|
||
//PptFreePort( fdx );
|
||
//DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Released\n");
|
||
} else {
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - Port Not acquired so no need to release\n");
|
||
}
|
||
|
||
DD((PCE)fdx,DDW,"PptFdoHandleBusRelations - exit\n");
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PptPnpStartScanPciCardCmResourceList(
|
||
IN PFDO_EXTENSION Fdx,
|
||
IN PIRP Irp,
|
||
OUT PBOOLEAN FoundPort,
|
||
OUT PBOOLEAN FoundIrq,
|
||
OUT PBOOLEAN FoundDma
|
||
)
|
||
/*++dvdf3
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to parse the resource list for what we
|
||
believe are PCI parallel port cards.
|
||
|
||
This function scans the CM_RESOURCE_LIST supplied with the Pnp
|
||
IRP_MN_START_DEVICE IRP, extracts the resources from the list,
|
||
and saves them in the device extension.
|
||
|
||
Arguments:
|
||
|
||
Fdx - The device extension of the target of the START IRP
|
||
Irp - The IRP
|
||
FoundPort - Did we find a Port resource?
|
||
FoundIrq - Did we find an IRQ resource?
|
||
FoundDma - Did we find a DMA resource?
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if we were given a resource list,
|
||
STATUS_INSUFFICIENT_RESOURCES - otherwise
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
||
PCM_RESOURCE_LIST ResourceList;
|
||
PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
|
||
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
|
||
ULONG i;
|
||
ULONG length;
|
||
|
||
*FoundPort = FALSE;
|
||
*FoundIrq = FALSE;
|
||
*FoundDma = FALSE;
|
||
|
||
ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
|
||
|
||
FullResourceDescriptor = &ResourceList->List[0];
|
||
|
||
if( FullResourceDescriptor ) {
|
||
|
||
Fdx->InterfaceType = FullResourceDescriptor->InterfaceType;
|
||
|
||
PartialResourceList = &FullResourceDescriptor->PartialResourceList;
|
||
|
||
for (i = 0; i < PartialResourceList->Count; i++) {
|
||
|
||
PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
|
||
|
||
switch (PartialResourceDescriptor->Type) {
|
||
|
||
case CmResourceTypePort:
|
||
|
||
length = PartialResourceDescriptor->u.Port.Length;
|
||
|
||
//
|
||
// Use a heuristic based on length to guess which register set is
|
||
// SPP+EPP, which is ECP, and which is PCI Config or other.
|
||
//
|
||
switch( length ) {
|
||
|
||
case 8: // SPP + EPP base address
|
||
|
||
Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
|
||
Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
|
||
Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
|
||
Fdx->AddressSpace = PartialResourceDescriptor->Flags;
|
||
*FoundPort = TRUE;
|
||
break;
|
||
|
||
case 4: // ECP base address
|
||
|
||
Fdx->PnpInfo.OriginalEcpController = PartialResourceDescriptor->u.Port.Start;
|
||
Fdx->PnpInfo.SpanOfEcpController = PartialResourceDescriptor->u.Port.Length;
|
||
Fdx->PnpInfo.EcpController = (PUCHAR)(ULONG_PTR)Fdx->PnpInfo.OriginalEcpController.QuadPart;
|
||
Fdx->EcpAddressSpace = PartialResourceDescriptor->Flags;
|
||
break;
|
||
|
||
default:
|
||
// don't know what this is - ignore it
|
||
;
|
||
}
|
||
break;
|
||
|
||
case CmResourceTypeBusNumber:
|
||
|
||
Fdx->BusNumber = PartialResourceDescriptor->u.BusNumber.Start;
|
||
break;
|
||
|
||
case CmResourceTypeInterrupt:
|
||
|
||
*FoundIrq = TRUE;
|
||
Fdx->FoundInterrupt = TRUE;
|
||
Fdx->InterruptLevel = (KIRQL)PartialResourceDescriptor->u.Interrupt.Level;
|
||
Fdx->InterruptVector = PartialResourceDescriptor->u.Interrupt.Vector;
|
||
Fdx->InterruptAffinity = PartialResourceDescriptor->u.Interrupt.Affinity;
|
||
|
||
if (PartialResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
|
||
|
||
Fdx->InterruptMode = Latched;
|
||
|
||
} else {
|
||
|
||
Fdx->InterruptMode = LevelSensitive;
|
||
}
|
||
break;
|
||
|
||
case CmResourceTypeDma:
|
||
|
||
// we don't do anything with DMA - fall through to default case
|
||
|
||
default:
|
||
|
||
break;
|
||
|
||
} // end switch( PartialResourceDescriptor->Type )
|
||
} // end for(... ; i < PartialResourceList->Count ; ...)
|
||
} // end if( FullResourceDescriptor )
|
||
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN PptIsPci(
|
||
PFDO_EXTENSION Fdx,
|
||
PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Does this look like a PCI card? Return TRUE if yes, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
||
PCM_RESOURCE_LIST ResourceList;
|
||
PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
|
||
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
|
||
ULONG i;
|
||
ULONG portResourceDescriptorCount = 0;
|
||
BOOLEAN largePortRangeFound = FALSE;
|
||
ULONG rangeLength;
|
||
|
||
//
|
||
// If there are more than 2 IO resource descriptors, or if any IO resource
|
||
// descriptor has a range > 8 bytes, then assume that this is a PCI device
|
||
// and requires non-traditional handling.
|
||
//
|
||
|
||
ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
|
||
|
||
if (ResourceList == NULL) {
|
||
// we weren't given any resources
|
||
return FALSE;
|
||
}
|
||
|
||
FullResourceDescriptor = &ResourceList->List[0];
|
||
|
||
if (FullResourceDescriptor) {
|
||
|
||
PartialResourceList = &FullResourceDescriptor->PartialResourceList;
|
||
|
||
for (i = 0; i < PartialResourceList->Count; i++) {
|
||
|
||
PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
|
||
|
||
switch (PartialResourceDescriptor->Type) {
|
||
|
||
case CmResourceTypePort:
|
||
|
||
rangeLength = PartialResourceDescriptor->u.Port.Length;
|
||
DD((PCE)Fdx,DDT,"pnp::PptIsPCI - CmResourceTypePort - Start= %I64x, Length= %x , \n",
|
||
PartialResourceDescriptor->u.Port.Start.QuadPart, rangeLength);
|
||
|
||
++portResourceDescriptorCount;
|
||
|
||
if( rangeLength > 8 ) {
|
||
largePortRangeFound = TRUE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
;
|
||
} // end switch( PartialResourceDescriptor->Type )
|
||
} // end for(... ; i < PartialResourceList->Count ; ...)
|
||
} // end if( FullResourceDescriptor )
|
||
|
||
if( (portResourceDescriptorCount > 2) || (TRUE == largePortRangeFound) ) {
|
||
// looks like PCI
|
||
return TRUE;
|
||
} else {
|
||
// does not look like PCI
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
PptPnpStartScanCmResourceList(
|
||
IN PFDO_EXTENSION Fdx,
|
||
IN PIRP Irp,
|
||
OUT PBOOLEAN FoundPort,
|
||
OUT PBOOLEAN FoundIrq,
|
||
OUT PBOOLEAN FoundDma
|
||
)
|
||
/*++dvdf3
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by PptPnpStartDevice().
|
||
|
||
This function scans the CM_RESOURCE_LIST supplied with the Pnp
|
||
IRP_MN_START_DEVICE IRP, extracts the resources from the list,
|
||
and saves them in the device Fdx.
|
||
|
||
Arguments:
|
||
|
||
Fdx - The device extension of the target of the START IRP
|
||
Irp - The IRP
|
||
FoundPort - Did we find a Port resource?
|
||
FoundIrq - Did we find an IRQ resource?
|
||
FoundDma - Did we find a DMA resource?
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if we were given a resource list,
|
||
STATUS_INSUFFICIENT_RESOURCES - otherwise
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation( Irp );
|
||
PCM_RESOURCE_LIST ResourceList;
|
||
PCM_FULL_RESOURCE_DESCRIPTOR FullResourceDescriptor;
|
||
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor;
|
||
ULONG i;
|
||
PHYSICAL_ADDRESS start;
|
||
ULONG length;
|
||
BOOLEAN isPci = FALSE;
|
||
|
||
*FoundPort = FALSE;
|
||
*FoundIrq = FALSE;
|
||
*FoundDma = FALSE;
|
||
|
||
ResourceList = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated;
|
||
|
||
if (ResourceList == NULL) {
|
||
// we weren't given any resources, bail out
|
||
DD((PCE)Fdx,DDT,"START - FAIL - No Resources - AllocatedResourcesTranslated == NULL\n");
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto targetExit;
|
||
}
|
||
|
||
if( TRUE == PptIsPci( Fdx, Irp ) ) {
|
||
// This appears to be a PCI card
|
||
status = PptPnpStartScanPciCardCmResourceList(Fdx, Irp, FoundPort, FoundIrq, FoundDma);
|
||
isPci=TRUE;
|
||
goto targetExit;
|
||
}
|
||
|
||
//
|
||
// Device appears to be traditional / non-PCI card parallel port
|
||
//
|
||
|
||
FullResourceDescriptor = &ResourceList->List[0];
|
||
|
||
if (FullResourceDescriptor) {
|
||
|
||
Fdx->InterfaceType = FullResourceDescriptor->InterfaceType;
|
||
|
||
PartialResourceList = &FullResourceDescriptor->PartialResourceList;
|
||
|
||
for (i = 0; i < PartialResourceList->Count; i++) {
|
||
|
||
PartialResourceDescriptor = &PartialResourceList->PartialDescriptors[i];
|
||
|
||
switch (PartialResourceDescriptor->Type) {
|
||
|
||
case CmResourceTypePort:
|
||
|
||
start = PartialResourceDescriptor->u.Port.Start;
|
||
length = PartialResourceDescriptor->u.Port.Length;
|
||
DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - start= %I64x , length=%x\n",start, length);
|
||
|
||
*FoundPort = TRUE;
|
||
if ((Fdx->PortInfo.OriginalController.LowPart == 0) &&
|
||
(Fdx->PortInfo.OriginalController.HighPart == 0)) {
|
||
|
||
DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming Controller\n");
|
||
|
||
Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
|
||
Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
|
||
Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
|
||
Fdx->AddressSpace = PartialResourceDescriptor->Flags;
|
||
|
||
} else if ((Fdx->PnpInfo.OriginalEcpController.LowPart == 0) &&
|
||
(Fdx->PnpInfo.OriginalEcpController.HighPart == 0) &&
|
||
(IsNotNEC_98)) {
|
||
|
||
if ((PartialResourceDescriptor->u.Port.Start.LowPart < Fdx->PortInfo.OriginalController.LowPart) &&
|
||
(PartialResourceDescriptor->u.Port.Start.HighPart < Fdx->PortInfo.OriginalController.HighPart)) {
|
||
|
||
//
|
||
// Swapping address spaces
|
||
//
|
||
|
||
DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming Controller - Swapping Controller/EcpController\n");
|
||
|
||
Fdx->PnpInfo.OriginalEcpController = Fdx->PortInfo.OriginalController;
|
||
Fdx->PnpInfo.SpanOfEcpController = Fdx->PortInfo.SpanOfController;
|
||
Fdx->PnpInfo.EcpController = Fdx->PortInfo.Controller;
|
||
Fdx->EcpAddressSpace = Fdx->AddressSpace;
|
||
|
||
Fdx->PortInfo.OriginalController = PartialResourceDescriptor->u.Port.Start;
|
||
Fdx->PortInfo.SpanOfController = PartialResourceDescriptor->u.Port.Length;
|
||
Fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)Fdx->PortInfo.OriginalController.QuadPart;
|
||
Fdx->AddressSpace = PartialResourceDescriptor->Flags;
|
||
|
||
} else {
|
||
DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - assuming EcpController\n");
|
||
|
||
Fdx->PnpInfo.OriginalEcpController = PartialResourceDescriptor->u.Port.Start;
|
||
Fdx->PnpInfo.SpanOfEcpController = PartialResourceDescriptor->u.Port.Length;
|
||
Fdx->PnpInfo.EcpController = (PUCHAR)(ULONG_PTR)Fdx->PnpInfo.OriginalEcpController.QuadPart;
|
||
Fdx->EcpAddressSpace = PartialResourceDescriptor->Flags;
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
case CmResourceTypeBusNumber:
|
||
|
||
Fdx->BusNumber = PartialResourceDescriptor->u.BusNumber.Start;
|
||
break;
|
||
|
||
case CmResourceTypeInterrupt:
|
||
|
||
*FoundIrq = TRUE;
|
||
Fdx->FoundInterrupt = TRUE;
|
||
Fdx->InterruptLevel = (KIRQL)PartialResourceDescriptor->u.Interrupt.Level;
|
||
Fdx->InterruptVector = PartialResourceDescriptor->u.Interrupt.Vector;
|
||
Fdx->InterruptAffinity = PartialResourceDescriptor->u.Interrupt.Affinity;
|
||
|
||
if (PartialResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
|
||
|
||
Fdx->InterruptMode = Latched;
|
||
|
||
} else {
|
||
|
||
Fdx->InterruptMode = LevelSensitive;
|
||
}
|
||
break;
|
||
|
||
case CmResourceTypeDma:
|
||
|
||
// we don't do anything with DMA - fall through to default case
|
||
|
||
default:
|
||
|
||
break;
|
||
|
||
} // end switch( PartialResourceDescriptor->Type )
|
||
} // end for(... ; i < PartialResourceList->Count ; ...)
|
||
} // end if( FullResourceDescriptor )
|
||
|
||
targetExit:
|
||
|
||
if( FALSE == isPci ) {
|
||
// we scanned the resources - dump what we found
|
||
DD((PCE)Fdx,DDT,"pnp::PptPnpStartScanCmResourceList - done, found:\n");
|
||
DD((PCE)Fdx,DDT," OriginalEcpController= %I64x\n", Fdx->PnpInfo.OriginalEcpController);
|
||
DD((PCE)Fdx,DDT," EcpController = %p\n", Fdx->PnpInfo.EcpController);
|
||
DD((PCE)Fdx,DDT," SpanOfEcpController = %x\n", Fdx->PnpInfo.SpanOfEcpController);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PptPnpStartValidateResources(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN BOOLEAN FoundPort,
|
||
IN BOOLEAN FoundIrq,
|
||
IN BOOLEAN FoundDma
|
||
)
|
||
/*++dvdf3
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by PptPnpStartDevice().
|
||
|
||
This function does a sanity check of the resources saved in our
|
||
extension by PptPnpStartScanCmResourceList() to determine
|
||
if those resources appear to be valid. Checks for for Irq
|
||
and Dma resource validity are anticipated in a future version.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The target of the START IRP
|
||
FoundPort - Did we find a Port resource?
|
||
FoundIrq - Did we find an IRQ resource?
|
||
FoundDma - Did we find a DMA resource?
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - on success,
|
||
STATUS_NO_SUCH_DEVICE - if we weren't given a port resource,
|
||
STATUS_NONE_MAPPED - if we were given a port resource but our
|
||
port address is NULL
|
||
|
||
--*/
|
||
{
|
||
PFDO_EXTENSION fdx = DeviceObject->DeviceExtension;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
UNREFERENCED_PARAMETER( FoundIrq ); // future use
|
||
UNREFERENCED_PARAMETER( FoundDma ); // future use
|
||
|
||
if( !FoundPort ) {
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
} else {
|
||
// fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)fdx->PortInfo.OriginalController.LowPart;
|
||
fdx->PortInfo.Controller = (PUCHAR)(ULONG_PTR)fdx->PortInfo.OriginalController.QuadPart;
|
||
|
||
if(!fdx->PortInfo.Controller) {
|
||
// ( Controller == NULL ) is invalid
|
||
PptLogError(DeviceObject->DriverObject, DeviceObject,
|
||
fdx->PortInfo.OriginalController, PhysicalZero, 0, 0, 0, 10,
|
||
STATUS_SUCCESS, PAR_REGISTERS_NOT_MAPPED);
|
||
status = STATUS_NONE_MAPPED;
|
||
}
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
PptPnpFilterExistsNonIrqResourceList(
|
||
IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
|
||
)
|
||
/*++dvdf8
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by
|
||
PptPnpFilterResourceRequirements().
|
||
|
||
This function scans the IO_RESOURCE_REQUIREMENTS_LIST to determine
|
||
whether there exists any resource alternatives that do NOT contain
|
||
an IRQ resource descriptor. The method used to filter out IRQ
|
||
resources may differ based on whether or not there exists a
|
||
resource alternative that does not contain an IRQ resource
|
||
descriptor.
|
||
|
||
Arguments:
|
||
|
||
ResourceRequirementsList - The list to scan.
|
||
|
||
Return Value:
|
||
|
||
TRUE - There exists at least one resource alternative in the list that
|
||
does not contain an IRQ resource descriptor.
|
||
FALSE - Otherwise.
|
||
|
||
--*/
|
||
{
|
||
ULONG listCount = ResourceRequirementsList->AlternativeLists;
|
||
PIO_RESOURCE_LIST curList;
|
||
ULONG i;
|
||
|
||
i=0;
|
||
curList = ResourceRequirementsList->List;
|
||
while( i < listCount ) {
|
||
DD(NULL,DDT,"Searching List i=%d for an IRQ, curList= %x\n", i,curList);
|
||
{
|
||
ULONG remain = curList->Count;
|
||
PIO_RESOURCE_DESCRIPTOR curDesc = curList->Descriptors;
|
||
BOOLEAN foundIrq = FALSE;
|
||
while( remain ) {
|
||
DD(NULL,DDT," curDesc= %x , remain=%d\n", curDesc, remain);
|
||
if(curDesc->Type == CmResourceTypeInterrupt) {
|
||
DD(NULL,DDT," Found IRQ - skip to next list\n");
|
||
foundIrq = TRUE;
|
||
break;
|
||
}
|
||
++curDesc;
|
||
--remain;
|
||
}
|
||
if( foundIrq == FALSE ) {
|
||
//
|
||
// We found a resource list that does not contain an IRQ resource.
|
||
// Our search is over.
|
||
//
|
||
DD(NULL,DDT," Found a list with NO IRQ - return TRUE from PptPnpFilterExistsNonIrqResourceList\n");
|
||
return TRUE;
|
||
}
|
||
}
|
||
//
|
||
// The next list starts immediately after the last descriptor of the current list.
|
||
//
|
||
curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
|
||
++i;
|
||
}
|
||
|
||
//
|
||
// All resource alternatives contain at least one IRQ resource descriptor.
|
||
//
|
||
DD(NULL,DDT,"all lists contain IRQs - return FALSE from PptPnpFilterExistsNonIrqResourceList\n");
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
PptPnpFilterRemoveIrqResourceLists(
|
||
PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
|
||
)
|
||
/*++dvdf8
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by
|
||
PptPnpFilterResourceRequirements().
|
||
|
||
This function removes all resource alternatives (IO_RESOURCE_LISTs)
|
||
that contain IRQ resources from the IO_RESOURCE_REQUIREMENTS_LIST
|
||
|
||
Arguments:
|
||
|
||
ResourceRequirementsList - The list to process.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
ULONG listCount = ResourceRequirementsList->AlternativeLists;
|
||
PIO_RESOURCE_LIST curList;
|
||
PIO_RESOURCE_LIST nextList;
|
||
ULONG i;
|
||
PCHAR currentEndOfResourceRequirementsList;
|
||
LONG bytesToMove;
|
||
|
||
DD(NULL,DDT,"Enter PptPnpFilterRemoveIrqResourceLists() - AlternativeLists= %d\n", listCount);
|
||
|
||
//
|
||
// We use the end of the list to compute the size of the memory
|
||
// block to move when we remove a resource alternative from the
|
||
// list of lists.
|
||
//
|
||
currentEndOfResourceRequirementsList = PptPnpFilterGetEndOfResourceRequirementsList(ResourceRequirementsList);
|
||
|
||
i=0;
|
||
curList = ResourceRequirementsList->List;
|
||
|
||
//
|
||
// Walk through the IO_RESOURCE_LISTs.
|
||
//
|
||
while( i < listCount ) {
|
||
|
||
if( PptPnpListContainsIrqResourceDescriptor(curList) ) {
|
||
//
|
||
// The current list contains IRQ, remove it by shifting the
|
||
// remaining lists into its place and decrementing the list count.
|
||
//
|
||
|
||
DD(NULL,DDT,"list contains an IRQ - Removing List\n");
|
||
|
||
//
|
||
// Get a pointer to the start of the next list.
|
||
//
|
||
nextList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
|
||
|
||
//
|
||
// compute the number of bytes to move
|
||
//
|
||
bytesToMove = (LONG)(currentEndOfResourceRequirementsList - (PCHAR)nextList);
|
||
|
||
//
|
||
// if (currentEndOfResourceRequirementsList == next list),
|
||
// then this is the last list so there is nothing to move.
|
||
//
|
||
if( bytesToMove > 0 ) {
|
||
//
|
||
// More lists remain - shift them into the hole.
|
||
//
|
||
RtlMoveMemory(curList, nextList, bytesToMove);
|
||
|
||
//
|
||
// Adjust the pointer to the end of of the
|
||
// IO_RESOURCE_REQUIREMENTS_LIST (list of lists) due to the shift.
|
||
//
|
||
currentEndOfResourceRequirementsList -= ( (PCHAR)nextList - (PCHAR)curList );
|
||
}
|
||
|
||
//
|
||
// Note that we removed an IO_RESOURCE_LIST from the IO_RESOURCE_REQUIREMENTS_LIST.
|
||
//
|
||
--listCount;
|
||
|
||
} else {
|
||
//
|
||
// The current list does not contain an IRQ resource, advance to next list.
|
||
//
|
||
DD(NULL,DDT,"list does not contain an IRQ - i=%d listCount=%d curList= %#x\n", i,listCount,curList);
|
||
curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
|
||
++i;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Note the post filtered list count in the ResourceRequirementsList.
|
||
//
|
||
ResourceRequirementsList->AlternativeLists = listCount;
|
||
|
||
DD(NULL,DDT,"Leave PptPnpFilterRemoveIrqResourceLists() - AlternativeLists= %d\n", listCount);
|
||
|
||
return;
|
||
}
|
||
|
||
PVOID
|
||
PptPnpFilterGetEndOfResourceRequirementsList(
|
||
IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
|
||
)
|
||
/*++dvdf8
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by PptPnpFilterRemoveIrqResourceLists()
|
||
|
||
This function finds the end of an IO_RESOURCE_REQUIREMENTS_LIST
|
||
(list of IO_RESOURCE_LISTs).
|
||
|
||
Arguments:
|
||
|
||
ResourceRequirementsList - The list to scan.
|
||
|
||
Return Value:
|
||
|
||
Pointer to the next address past the end of the IO_RESOURCE_REQUIREMENTS_LIST.
|
||
|
||
--*/
|
||
{
|
||
ULONG listCount = ResourceRequirementsList->AlternativeLists;
|
||
PIO_RESOURCE_LIST curList;
|
||
ULONG i;
|
||
|
||
i=0;
|
||
curList = ResourceRequirementsList->List;
|
||
while( i < listCount ) {
|
||
//
|
||
// Pointer arithmetic based on the size of an IO_RESOURCE_DESCRIPTOR.
|
||
//
|
||
curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
|
||
++i;
|
||
}
|
||
return (PVOID)curList;
|
||
}
|
||
|
||
VOID
|
||
PptPnpFilterNukeIrqResourceDescriptorsFromAllLists(
|
||
PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirementsList
|
||
)
|
||
/*++dvdf8
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by
|
||
PptPnpFilterResourceRequirements().
|
||
|
||
This function "nukes" all IRQ resources descriptors
|
||
in the IO_RESOURCE_REQUIREMENTS_LIST by changing the descriptor
|
||
types from CmResourceTypeInterrupt to CmResourceTypeNull.
|
||
|
||
Arguments:
|
||
|
||
ResourceRequirementsList - The list to process.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
ULONG listCount = ResourceRequirementsList->AlternativeLists;
|
||
ULONG i = 0;
|
||
PIO_RESOURCE_LIST curList = ResourceRequirementsList->List;
|
||
|
||
DD(NULL,DDT,"Enter PptPnpFilterNukeIrqResourceDescriptorsFromAllLists() - AlternativeLists= %d\n", listCount);
|
||
|
||
//
|
||
// Walk through the list of IO_RESOURCE_LISTs in the IO_RESOURCE_REQUIREMENTS list.
|
||
//
|
||
while( i < listCount ) {
|
||
DD(NULL,DDT,"Nuking IRQs from List i=%d, curList= %x\n", i,curList);
|
||
//
|
||
// Nuke all IRQ resources from the current IO_RESOURCE_LIST.
|
||
//
|
||
PptPnpFilterNukeIrqResourceDescriptors( curList );
|
||
curList = (PIO_RESOURCE_LIST)(curList->Descriptors + curList->Count);
|
||
++i;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
PptPnpFilterNukeIrqResourceDescriptors(
|
||
PIO_RESOURCE_LIST IoResourceList
|
||
)
|
||
/*++dvdf8
|
||
|
||
Routine Description:
|
||
|
||
This function is a helper function called by
|
||
PptPnpFilterNukeIrqResourceDescriptorsFromAllLists().
|
||
|
||
This function "nukes" all IRQ resources descriptors
|
||
in the IO_RESOURCE_LIST by changing the descriptor
|
||
types from CmResourceTypeInterrupt to CmResourceTypeNull.
|
||
|
||
Arguments:
|
||
|
||
IoResourceList - The list to process.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
{
|
||
PIO_RESOURCE_DESCRIPTOR pIoResourceDescriptorIn = IoResourceList->Descriptors;
|
||
ULONG i;
|
||
|
||
//
|
||
// Scan the descriptor list for Interrupt descriptors.
|
||
//
|
||
for (i = 0; i < IoResourceList->Count; ++i) {
|
||
|
||
if (pIoResourceDescriptorIn->Type == CmResourceTypeInterrupt) {
|
||
//
|
||
// Found one - change resource type from Interrupt to Null.
|
||
//
|
||
pIoResourceDescriptorIn->Type = CmResourceTypeNull;
|
||
DD(NULL,DDT," - giving up IRQ resource - MinimumVector: %d MaximumVector: %d\n",
|
||
pIoResourceDescriptorIn->u.Interrupt.MinimumVector,
|
||
pIoResourceDescriptorIn->u.Interrupt.MaximumVector);
|
||
}
|
||
++pIoResourceDescriptorIn;
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
PptPnpListContainsIrqResourceDescriptor(
|
||
IN PIO_RESOURCE_LIST List
|
||
)
|
||
{
|
||
ULONG i;
|
||
PIO_RESOURCE_DESCRIPTOR curDesc = List->Descriptors;
|
||
|
||
for(i=0; i<List->Count; ++i) {
|
||
if(curDesc->Type == CmResourceTypeInterrupt) {
|
||
return TRUE;
|
||
} else {
|
||
++curDesc;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
PptPnpBounceAndCatchPnpIrp(
|
||
PFDO_EXTENSION Fdx,
|
||
PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Pass a PnP IRP down the stack to our parent and catch it on the way back
|
||
up after it has been handled by the drivers below us in the driver stack.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
KEVENT event;
|
||
PDEVICE_OBJECT parentDevObj = Fdx->ParentDeviceObject;
|
||
|
||
DD((PCE)Fdx,DDT,"PptBounceAndCatchPnpIrp()\n");
|
||
|
||
// setup
|
||
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
IoCopyCurrentIrpStackLocationToNext(Irp);
|
||
IoSetCompletionRoutine(Irp, PptSynchCompletionRoutine, &event, TRUE, TRUE, TRUE);
|
||
|
||
// send
|
||
status = IoCallDriver(parentDevObj, Irp);
|
||
|
||
// wait for completion routine to signal that it has caught the IRP on
|
||
// its way back out
|
||
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
||
|
||
if (status == STATUS_PENDING) {
|
||
// If IoCallDriver returned STATUS_PENDING, then we must
|
||
// extract the "real" status from the IRP
|
||
status = Irp->IoStatus.Status;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
PptPnpPassThroughPnpIrpAndReleaseRemoveLock(
|
||
IN PFDO_EXTENSION Fdx,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Pass a PnP IRP down the stack to our parent,
|
||
release RemoveLock, and return status from IoCallDriver.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
|
||
IoSkipCurrentIrpStackLocation(Irp);
|
||
status = IoCallDriver(Fdx->ParentDeviceObject, Irp);
|
||
PptReleaseRemoveLock(&Fdx->RemoveLock, Irp);
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
P4DestroyPdo(
|
||
IN PDEVICE_OBJECT Pdo
|
||
)
|
||
{
|
||
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
|
||
PDEVICE_OBJECT fdo = pdx->Fdo;
|
||
PFDO_EXTENSION fdx = fdo->DeviceExtension;
|
||
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo\n");
|
||
|
||
//
|
||
// Remove registry entry under HKLM\HARDWARE\DEVICEMAP\PARALLEL PORTS
|
||
//
|
||
if( pdx->PdoName ) {
|
||
NTSTATUS status = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", pdx->PdoName );
|
||
if( status != STATUS_SUCCESS ) {
|
||
DD((PCE)pdx,DDW,"P4DestroyPdo - Failed to Delete DEVICEMAP registry entry - status=%x\n",status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// remove self from FDO's DevDeletionListHead list
|
||
//
|
||
if( !IsListEmpty( &fdx->DevDeletionListHead ) ) {
|
||
|
||
BOOLEAN done = FALSE;
|
||
PLIST_ENTRY first = NULL;
|
||
|
||
while( !done ) {
|
||
|
||
// look for self on list - remove if found
|
||
|
||
PLIST_ENTRY current = RemoveHeadList( &fdx->DevDeletionListHead );
|
||
|
||
if( CONTAINING_RECORD( current, PDO_EXTENSION, DevDeletionList ) != pdx ) {
|
||
|
||
// this is not the entry that we are looking for
|
||
|
||
if( !first ) {
|
||
|
||
// note the first entry so we can stop if we search the entire list and don't find self
|
||
|
||
first = current;
|
||
InsertTailList( &fdx->DevDeletionListHead, current );
|
||
|
||
} else {
|
||
|
||
// have we searched the entire list?
|
||
|
||
if( first == current ) {
|
||
|
||
// we searched the entire list and didn't find self - we must not be on the list
|
||
// put entry back on front of list, then we're done with search
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - searched entire list - we're not on it - done with search\n");
|
||
InsertHeadList( &fdx->DevDeletionListHead, current );
|
||
done = TRUE;
|
||
|
||
} else {
|
||
|
||
// not the entry that we're looking for - place at end of list - continue search
|
||
InsertTailList( &fdx->DevDeletionListHead, current );
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
// found self - self removed from list - done with search
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - found self on FDO's DevDeletionListHead and removed self - done with search\n");
|
||
done = TRUE;
|
||
}
|
||
|
||
} // end while( !done )
|
||
|
||
} // endif( !IsListEmpty... )
|
||
|
||
|
||
//
|
||
// clean up any ShadowBuffer queue used by hardware ECP modes
|
||
//
|
||
if( pdx->bShadowBuffer ) {
|
||
BOOLEAN queueDeleted = Queue_Delete( &(pdx->ShadowBuffer) );
|
||
if( !queueDeleted ) {
|
||
PptAssertMsg( "Failed to delete queue?!?", FALSE );
|
||
}
|
||
pdx->bShadowBuffer = FALSE;
|
||
}
|
||
PptAssert( NULL == pdx->ShadowBuffer.theArray );
|
||
|
||
|
||
//
|
||
// clean up symbolic link - unless it has been previously cleaned up elsewhere
|
||
//
|
||
if( pdx->SymLinkName ) {
|
||
P5DeletePdoSymLink( Pdo );
|
||
}
|
||
|
||
//
|
||
// clean up other device extension pool allocations
|
||
//
|
||
if( pdx->Mfg ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Mfg <%s>\n", pdx->Mfg);
|
||
ExFreePool( pdx->Mfg );
|
||
pdx->Mfg = NULL;
|
||
}
|
||
if( pdx->Mdl ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Mdl <%s>\n", pdx->Mdl);
|
||
ExFreePool( pdx->Mdl );
|
||
pdx->Mdl = NULL;
|
||
}
|
||
if( pdx->Cid ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Cid <%s>\n", pdx->Cid);
|
||
ExFreePool( pdx->Cid );
|
||
pdx->Cid = NULL;
|
||
}
|
||
if( pdx->DeviceInterface.Buffer ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up DeviceInterface <%S>\n", pdx->PdoName);
|
||
RtlFreeUnicodeString( &pdx->DeviceInterface );
|
||
pdx->DeviceInterfaceState = FALSE;
|
||
}
|
||
if( pdx->PdoName ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up PdoName <%S>\n", pdx->PdoName);
|
||
ExFreePool( pdx->PdoName );
|
||
pdx->PdoName = NULL;
|
||
}
|
||
if( pdx->Location ) {
|
||
DD((PCE)pdx,DDT,"P4DestroyPdo - clean up Location <%s>\n", pdx->Location);
|
||
ExFreePool( pdx->Location );
|
||
pdx->Location = NULL;
|
||
}
|
||
|
||
//
|
||
// delete device object
|
||
//
|
||
IoDeleteDevice( Pdo );
|
||
}
|
||
|
||
|
||
VOID
|
||
P4SanitizeId(
|
||
IN OUT PWSTR DeviceId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine parses the UNICODE_NULL terminated string and replaces any invalid
|
||
characters with an underscore character.
|
||
|
||
Invalid characters are:
|
||
c <= 0x20 (L' ')
|
||
c > 0x7F
|
||
c == 0x2C (L',')
|
||
|
||
Arguments:
|
||
|
||
DeviceId - specifies a device id string (or part of one), must be
|
||
UNICODE_NULL terminated.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PWCHAR p;
|
||
for( p = DeviceId; *p; ++p ) {
|
||
if( (*p <= L' ') || (*p > (WCHAR)0x7F) || (*p == L',') ) {
|
||
*p = L'_';
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
P4InitializePdo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PDEVICE_OBJECT Pdo,
|
||
IN enum _PdoType PdoType,
|
||
IN UCHAR DaisyChainId, // Ignored unless PdoTypeDaisyChain == PdoType
|
||
IN PCHAR Ieee1284Id, // NULL if none
|
||
IN PWSTR PdoName,
|
||
IN PWSTR SymLinkName
|
||
)
|
||
{
|
||
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
||
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
|
||
|
||
// we do buffered IO rather than direct IO
|
||
Pdo->Flags |= DO_BUFFERED_IO;
|
||
|
||
// DO_POWER_PAGABLE should be set same as parent FDO
|
||
Pdo->Flags |= ( Fdo->Flags & DO_POWER_PAGABLE );
|
||
|
||
// need to be able to forward Irps to parent
|
||
Pdo->StackSize = Fdo->StackSize + 1;
|
||
|
||
RtlZeroMemory( pdx, sizeof(PDO_EXTENSION) );
|
||
|
||
// used by debugger extension
|
||
pdx->Signature1 = PARPORT_TAG;
|
||
pdx->Signature2 = PARPORT_TAG;
|
||
|
||
// frequently need to know what type of PDO we have in order to do special case handling
|
||
pdx->PdoType = PdoType;
|
||
|
||
// Save name used in call to IoCreateDevice (for debugging use)
|
||
pdx->PdoName = PdoName;
|
||
|
||
// Save name used in call to IoCreateUnprotectedSymbolicLink for later call to IoDeleteSymbolicLink
|
||
pdx->SymLinkName = SymLinkName;
|
||
|
||
// initialize Mfg, Mdl, and Cid
|
||
if( Ieee1284Id ) {
|
||
//
|
||
// Extract Mfg, Mdl, and Cid from Ieee1284Id and save in extension
|
||
//
|
||
|
||
// ParPnpFindDeviceIdKeys modifies deviceID passed in so make
|
||
// a copy of the 1284 ID and pass in a pointer to the copy
|
||
PCHAR tmpBuffer;
|
||
ULONG tmpBufLen = strlen(Ieee1284Id) + sizeof(CHAR);
|
||
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - have Ieee1284Id\n");
|
||
|
||
tmpBuffer = ExAllocatePool( PagedPool, tmpBufLen );
|
||
if( tmpBuffer ) {
|
||
PCHAR mfg, mdl, cls, des, aid, cid;
|
||
RtlZeroMemory( tmpBuffer, tmpBufLen );
|
||
strcpy( tmpBuffer, Ieee1284Id );
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - calling ParPnpFindDeviceIdKeys\n");
|
||
ParPnpFindDeviceIdKeys( &mfg, &mdl, &cls, &des, &aid, &cid, tmpBuffer );
|
||
if( mfg ) {
|
||
PCHAR buffer;
|
||
ULONG bufLen = strlen(mfg) + sizeof(CHAR);
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - found mfg - <%s>\n",mfg);
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
strcpy( buffer, mfg );
|
||
pdx->Mfg = buffer;
|
||
}
|
||
}
|
||
if( mdl ) {
|
||
PCHAR buffer;
|
||
ULONG bufLen = strlen(mdl) + sizeof(CHAR);
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - found mdl - <%s>\n",mdl);
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
strcpy( buffer, mdl );
|
||
pdx->Mdl = buffer;
|
||
}
|
||
}
|
||
if( cid ) {
|
||
PCHAR buffer;
|
||
ULONG bufLen = strlen(cid) + sizeof(CHAR);
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - found cid - <%s>\n",cid);
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
strcpy( buffer, cid );
|
||
pdx->Cid = buffer;
|
||
}
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - no cid found\n");
|
||
}
|
||
ExFreePool( tmpBuffer );
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - out of pool\n");
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// PdoType doesn't have a Mfg, Mdl, or Cid, make up Mfg and Mdl, no Cid
|
||
//
|
||
const CHAR rawPortMfg[] = "Microsoft";
|
||
const CHAR rawPortMdl[] = "RawPort";
|
||
const CHAR legacyZipMfg[] = "IMG";
|
||
const CHAR legacyZipMdl[] = "VP0";
|
||
PCHAR mfgStr;
|
||
ULONG mfgLen;
|
||
PCHAR mdlStr;
|
||
ULONG mdlLen;
|
||
PCHAR buffer;
|
||
|
||
if( PdoTypeRawPort == PdoType ) {
|
||
mfgStr = (PCHAR)rawPortMfg;
|
||
mfgLen = sizeof(rawPortMfg);
|
||
mdlStr = (PCHAR)rawPortMdl;
|
||
mdlLen = sizeof(rawPortMdl);
|
||
} else {
|
||
// PdoTypeLegacyZip
|
||
PptAssert( PdoTypeLegacyZip == PdoType );
|
||
mfgStr = (PCHAR)legacyZipMfg;
|
||
mfgLen = sizeof(legacyZipMfg);
|
||
mdlStr = (PCHAR)legacyZipMdl;
|
||
mdlLen = sizeof(legacyZipMdl);
|
||
}
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, mfgLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, mfgLen );
|
||
strcpy( buffer, mfgStr );
|
||
pdx->Mfg = buffer;
|
||
}
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, mdlLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, mdlLen );
|
||
strcpy( buffer, mdlStr );
|
||
pdx->Mdl = buffer;
|
||
}
|
||
pdx->Cid = NULL;
|
||
}
|
||
|
||
// initialize Location information - LPTx or LPTx.y
|
||
PptAssert( fdx->PnpInfo.PortName &&
|
||
( (0 == wcscmp(fdx->PnpInfo.PortName, L"LPT1") ) ||
|
||
(0 == wcscmp(fdx->PnpInfo.PortName, L"LPT2") ) ||
|
||
(0 == wcscmp(fdx->PnpInfo.PortName, L"LPT3") ) ) );
|
||
|
||
switch( PdoType ) {
|
||
PCHAR buffer;
|
||
ULONG bufLen;
|
||
|
||
case PdoTypeRawPort :
|
||
bufLen = sizeof("LPTx");
|
||
buffer = ExAllocatePool( NonPagedPool, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
_snprintf( buffer, bufLen, "%S", fdx->PnpInfo.PortName );
|
||
pdx->Location = buffer;
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
|
||
}
|
||
break;
|
||
|
||
case PdoTypeDaisyChain :
|
||
bufLen = sizeof("LPTx.y");
|
||
buffer = ExAllocatePool( NonPagedPool, bufLen );
|
||
if( buffer ) {
|
||
PptAssert( DaisyChainId >= 0 && DaisyChainId < 4 );
|
||
RtlZeroMemory( buffer, bufLen );
|
||
_snprintf( buffer, bufLen, "%S.%1d", fdx->PnpInfo.PortName, DaisyChainId );
|
||
pdx->Location = buffer;
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
|
||
}
|
||
break;
|
||
|
||
case PdoTypeEndOfChain :
|
||
bufLen = sizeof("LPTx.y");
|
||
buffer = ExAllocatePool( NonPagedPool, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
_snprintf( buffer, bufLen, "%S.4", fdx->PnpInfo.PortName );
|
||
pdx->Location = buffer;
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
|
||
}
|
||
break;
|
||
|
||
case PdoTypeLegacyZip :
|
||
bufLen = sizeof("LPTx.y");
|
||
buffer = ExAllocatePool( NonPagedPool, bufLen );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, bufLen );
|
||
_snprintf( buffer, bufLen, "%S.5", fdx->PnpInfo.PortName );
|
||
pdx->Location = buffer;
|
||
} else {
|
||
DD((PCE)fdx,DDT,"P4InitializePdo - out of pool");
|
||
}
|
||
break;
|
||
|
||
default :
|
||
PptAssert(!"Invalid PdoType");
|
||
}
|
||
|
||
|
||
// initialize synchronization and list mechanisms
|
||
ExInitializeFastMutex( &pdx->OpenCloseMutex );
|
||
InitializeListHead( &pdx->WorkQueue );
|
||
KeInitializeSemaphore( &pdx->RequestSemaphore, 0, MAXLONG );
|
||
KeInitializeEvent( &pdx->PauseEvent, NotificationEvent, TRUE );
|
||
|
||
|
||
// general info
|
||
pdx->DeviceObject = Pdo;
|
||
pdx->DevType = DevTypePdo;
|
||
|
||
pdx->EndOfChain = (PdoTypeEndOfChain == PdoType) ? TRUE : FALSE; // override later if this is a
|
||
pdx->Ieee1284_3DeviceId = (PdoTypeDaisyChain == PdoType) ? DaisyChainId : 0; // 1284.3 Daisy Chain device
|
||
|
||
pdx->IsPdo = TRUE; // really means !FDO
|
||
pdx->Fdo = Fdo;
|
||
pdx->ParClassFdo = Fdo; // depricated - use Fdo field on prev line
|
||
pdx->PortDeviceObject = Fdo; // depricated - use Fdo field 2 lines up - modify functions to use it
|
||
pdx->BusyDelay = 0;
|
||
pdx->BusyDelayDetermined = FALSE;
|
||
|
||
// timing constants
|
||
pdx->TimerStart = PAR_WRITE_TIMEOUT_VALUE;
|
||
pdx->AbsoluteOneSecond.QuadPart = 10*1000*1000;
|
||
pdx->IdleTimeout.QuadPart = - 250*10*1000; // 250 ms
|
||
pdx->OneSecond.QuadPart = - pdx->AbsoluteOneSecond.QuadPart;
|
||
|
||
// init IEEE 1284 protocol settings
|
||
ParInitializeExtension1284Info( pdx );
|
||
|
||
pdx->DeviceType = PAR_DEVTYPE_PDO; // deprecated - use DevType in common extension
|
||
|
||
if( Ieee1284Id ) {
|
||
ULONG length = strlen(Ieee1284Id) + 1;
|
||
PCHAR copyOfIeee1284Id = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, length );
|
||
if( copyOfIeee1284Id ) {
|
||
RtlZeroMemory( copyOfIeee1284Id, length );
|
||
strcpy( copyOfIeee1284Id, Ieee1284Id );
|
||
ParDetectDot3DataLink( pdx, Ieee1284Id );
|
||
ExFreePool( copyOfIeee1284Id );
|
||
}
|
||
}
|
||
|
||
// RMT - doug - need to put this back in - ParCheckParameters(DevObj->DeviceExtension); // Check the registry for parameter overrides
|
||
|
||
// Write symbolic link map info to the registry.
|
||
{
|
||
NTSTATUS status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
|
||
(PWSTR)L"PARALLEL PORTS",
|
||
pdx->PdoName,
|
||
REG_SZ,
|
||
pdx->SymLinkName,
|
||
wcslen(pdx->SymLinkName)*sizeof(WCHAR) + sizeof(WCHAR) );
|
||
if( NT_SUCCESS( status ) ) {
|
||
DD((PCE)fdx,DDT,"Created DEVICEMAP registry entry - %S -> %S\n",pdx->PdoName,pdx->SymLinkName);
|
||
} else {
|
||
DD((PCE)fdx,DDT,"Failed to create DEVICEMAP registry entry - status = %x\n", status);
|
||
}
|
||
}
|
||
|
||
Pdo->Flags &= ~DO_DEVICE_INITIALIZING; // Tell the IO system that we are ready to receive IRPs
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
PWSTR
|
||
P4MakePdoSymLinkName(
|
||
IN PWSTR LptName,
|
||
IN enum _PdoType PdoType,
|
||
IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
|
||
IN UCHAR RetryNumber
|
||
)
|
||
/*
|
||
|
||
Generate \DosDevices\LPTx or \DosDevices\LPTx.y PdoSymbolicLinkName from LPTx Name
|
||
|
||
In: LPTx
|
||
Out: \DosDevices\LPTx or \DosDevices\LPTx.y depending on PdoType
|
||
|
||
examples:
|
||
|
||
LPT1 PdoTypeEndOfChain -> \DosDevices\LPT1.4
|
||
LPT2 PdoTypeDaisyChain DaisyChainId==3 -> \DosDevices\LPT2.3
|
||
LPT3 PdoTypeRawPort -> \DosDevices\LPT3
|
||
|
||
returns - pointer to pool allocation containing PdoSymbolicLinkName on success (caller frees), or
|
||
- NULL on error
|
||
|
||
*/
|
||
{
|
||
const UCHAR maxDaisyChainSuffix = 3;
|
||
const UCHAR endOfChainSuffix = 4;
|
||
const UCHAR legacyZipSuffix = 5;
|
||
const ULONG maxSymLinkNameLength = sizeof(L"\\DosDevices\\LPTx.y-z");
|
||
|
||
UCHAR suffix = 0;
|
||
PWSTR buffer;
|
||
|
||
if( !LptName ) {
|
||
PptAssert( !"NULL LptName" );
|
||
return NULL;
|
||
}
|
||
|
||
DD(NULL,DDT,"P4MakePdoSymLinkName - LptName = %S\n",LptName);
|
||
|
||
switch( PdoType ) {
|
||
case PdoTypeDaisyChain :
|
||
if( DaisyChainId > maxDaisyChainSuffix ) {
|
||
PptAssert( !"DaisyChainId > maxDaisyChainSuffix" );
|
||
return NULL;
|
||
}
|
||
suffix = DaisyChainId;
|
||
break;
|
||
case PdoTypeEndOfChain :
|
||
suffix = endOfChainSuffix;
|
||
break;
|
||
case PdoTypeLegacyZip :
|
||
suffix = legacyZipSuffix;
|
||
break;
|
||
case PdoTypeRawPort :
|
||
break; // no suffix
|
||
default :
|
||
PptAssert( !"Unrecognised PdoType" );
|
||
return NULL;
|
||
}
|
||
|
||
if( 0 == RetryNumber ) {
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxSymLinkNameLength );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, maxSymLinkNameLength );
|
||
if( PdoTypeRawPort == PdoType ) {
|
||
swprintf( buffer, L"\\DosDevices\\%s\0", LptName );
|
||
} else {
|
||
swprintf( buffer, L"\\DosDevices\\%s.%d\0", LptName, suffix );
|
||
}
|
||
}
|
||
} else if( RetryNumber <= 9 ) {
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxSymLinkNameLength );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, maxSymLinkNameLength );
|
||
if( PdoTypeRawPort == PdoType ) {
|
||
swprintf( buffer, L"\\DosDevices\\%s-%1d\0", LptName, RetryNumber );
|
||
} else {
|
||
swprintf( buffer, L"\\DosDevices\\%s.%d-%1d\0", LptName, suffix, RetryNumber );
|
||
}
|
||
}
|
||
} else {
|
||
buffer = NULL;
|
||
}
|
||
|
||
return buffer;
|
||
}
|
||
|
||
|
||
PWSTR
|
||
P4MakePdoDeviceName(
|
||
IN PWSTR LptName,
|
||
IN enum _PdoType PdoType,
|
||
IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
|
||
IN UCHAR RetryNumber // used if we had a name collision on IoCreateDevice
|
||
)
|
||
/*
|
||
|
||
Generate \Device\Parallely or \Device\Parallely.z PDO DeviceName from LPTx Name
|
||
|
||
In: LPTx
|
||
Out: \Device\Parallely or \Device\Parallely.z depending on PdoType
|
||
|
||
y == (x-1), optional .z suffix is based on type of Pdo
|
||
|
||
examples:
|
||
|
||
LPT1 PdoTypeEndOfChain -> \Device\Parallel0.4
|
||
LPT2 PdoTypeDaisyChain DaisyChainId==3 -> \Device\Parallel1.3
|
||
LPT3 PdoTypeRawPort -> \Device\Parallel2
|
||
|
||
returns - pointer to pool allocation containing PdoDeviceName on success (caller frees), or
|
||
- NULL on error
|
||
|
||
*/
|
||
{
|
||
const UCHAR maxDaisyChainSuffix = 3;
|
||
const UCHAR endOfChainSuffix = 4;
|
||
const UCHAR legacyZipSuffix = 5;
|
||
ULONG maxDeviceNameLength;
|
||
|
||
UCHAR lptNumber;
|
||
UCHAR suffix = 0;
|
||
PWSTR buffer = NULL;
|
||
|
||
DD(NULL,DDT,"P4MakePdoDeviceName - LptName=<%S>, PdoType=%d, DaisyChainId=%d\n",LptName,PdoType,DaisyChainId);
|
||
|
||
|
||
if( !LptName ) {
|
||
PptAssert( !"NULL LptName" );
|
||
return NULL;
|
||
}
|
||
|
||
switch( PdoType ) {
|
||
case PdoTypeDaisyChain :
|
||
if( DaisyChainId > maxDaisyChainSuffix ) {
|
||
PptAssert( !"DaisyChainId > maxDaisyChainSuffix" );
|
||
return NULL;
|
||
}
|
||
suffix = DaisyChainId;
|
||
break;
|
||
case PdoTypeEndOfChain :
|
||
suffix = endOfChainSuffix;
|
||
break;
|
||
case PdoTypeLegacyZip :
|
||
suffix = legacyZipSuffix;
|
||
break;
|
||
case PdoTypeRawPort :
|
||
break; // no suffix
|
||
default :
|
||
PptAssert( !"Unrecognised PdoType" );
|
||
return NULL;
|
||
}
|
||
|
||
if ( 0 == wcscmp( (PCWSTR)L"LPT1", LptName ) ) { lptNumber = 1; }
|
||
else if( 0 == wcscmp( (PCWSTR)L"LPT2", LptName ) ) { lptNumber = 2; }
|
||
else if( 0 == wcscmp( (PCWSTR)L"LPT3", LptName ) ) { lptNumber = 3; }
|
||
else {
|
||
PptAssert( !"LptName not of the form LPTx where 1 <= x <= 3" );
|
||
return NULL;
|
||
}
|
||
|
||
DD(NULL,DDT,"P4MakePdoDeviceName - suffix=%d\n",suffix);
|
||
|
||
if( 0 == RetryNumber ) {
|
||
maxDeviceNameLength = sizeof(L"\\Device\\Parallelx.y");
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxDeviceNameLength );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, maxDeviceNameLength );
|
||
if( PdoTypeRawPort == PdoType ) {
|
||
swprintf( buffer, L"\\Device\\Parallel%d\0", lptNumber-1 );
|
||
} else {
|
||
swprintf( buffer, L"\\Device\\Parallel%d.%d\0", lptNumber-1, suffix );
|
||
}
|
||
}
|
||
} else {
|
||
if( RetryNumber <= 9 ) {
|
||
maxDeviceNameLength = sizeof(L"\\Device\\Parallelx.y-z");
|
||
buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, maxDeviceNameLength );
|
||
if( buffer ) {
|
||
RtlZeroMemory( buffer, maxDeviceNameLength );
|
||
if( PdoTypeRawPort == PdoType ) {
|
||
swprintf( buffer, L"\\Device\\Parallel%d-%1d\0", lptNumber-1, RetryNumber );
|
||
} else {
|
||
swprintf( buffer, L"\\Device\\Parallel%d.%d-%1d\0", lptNumber-1, suffix, RetryNumber );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if( buffer ) {
|
||
DD(NULL,DDT,"P4MakePdoDeviceName <%S>\n",buffer);
|
||
}
|
||
|
||
return buffer;
|
||
}
|
||
|
||
|
||
PDEVICE_OBJECT
|
||
P4CreatePdo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN enum _PdoType PdoType,
|
||
IN UCHAR DaisyChainId, // ignored unless PdoType == PdoTypeDaisyChain
|
||
IN PCHAR Ieee1284Id // NULL if device does not report IEEE 1284 Device ID
|
||
)
|
||
{
|
||
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
|
||
PWSTR lptName = fdx->PnpInfo.PortName;
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
PDEVICE_OBJECT pdo = NULL;
|
||
PWSTR wstrDeviceName = NULL;
|
||
PWSTR wstrSymLinkName = NULL;
|
||
BOOLEAN createdSymLink = FALSE;
|
||
UCHAR retryNumber = 0;
|
||
|
||
UNICODE_STRING deviceName;
|
||
UNICODE_STRING symLinkName;
|
||
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - enter - PdoType= %d, DaisyChainId=%d, Ieee1284Id=<%s>\n", PdoType, DaisyChainId, Ieee1284Id);
|
||
|
||
__try {
|
||
|
||
if( !lptName ) {
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - no lptName\n");
|
||
__leave;
|
||
}
|
||
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - lptName = %S\n",lptName);
|
||
|
||
targetRetryDeviceName:
|
||
|
||
wstrDeviceName = P4MakePdoDeviceName( lptName, PdoType, DaisyChainId, retryNumber );
|
||
if( !wstrDeviceName ) {
|
||
DD((PCE)fdx,DDT,"P4MakePdoDeviceName FAILED\n");
|
||
__leave;
|
||
}
|
||
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - wstrDeviceName = %S\n",wstrDeviceName);
|
||
RtlInitUnicodeString( &deviceName, wstrDeviceName );
|
||
|
||
status = IoCreateDevice( fdx->DriverObject,
|
||
sizeof(PDO_EXTENSION),
|
||
&deviceName,
|
||
FILE_DEVICE_PARALLEL_PORT,
|
||
FILE_DEVICE_SECURE_OPEN,
|
||
TRUE,
|
||
&pdo );
|
||
|
||
if( STATUS_SUCCESS != status ) {
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - FAILED\n");
|
||
pdo = NULL; // just to make sure that we don't try to use this later
|
||
if( STATUS_OBJECT_NAME_COLLISION == status ) {
|
||
// try again with another name
|
||
DD(NULL,DDE,"P4CreatePdo - STATUS_OBJECT_NAME_COLLISION on %S\n",wstrDeviceName);
|
||
ExFreePool( wstrDeviceName );
|
||
++retryNumber;
|
||
goto targetRetryDeviceName;
|
||
}
|
||
__leave;
|
||
}
|
||
|
||
retryNumber = 0;
|
||
|
||
targetRetrySymLink:
|
||
|
||
wstrSymLinkName = P4MakePdoSymLinkName( lptName, PdoType, DaisyChainId, retryNumber );
|
||
if( !wstrSymLinkName ) {
|
||
DD((PCE)fdx,DDT,"P4MakePdoSymLinkName FAILED\n");
|
||
__leave;
|
||
}
|
||
RtlInitUnicodeString( &symLinkName, wstrSymLinkName );
|
||
|
||
status = IoCreateUnprotectedSymbolicLink( &symLinkName , &deviceName );
|
||
if( STATUS_SUCCESS != status ) {
|
||
if( STATUS_OBJECT_NAME_COLLISION == status ) {
|
||
DD(NULL,DDE,"P4CreatePdo - STATUS_OBJECT_NAME_COLLISION on %S\n", wstrSymLinkName);
|
||
ExFreePool( wstrSymLinkName );
|
||
++retryNumber;
|
||
goto targetRetrySymLink;
|
||
}
|
||
DD((PCE)fdx,DDT,"P4CreatePdo - create SymLink FAILED\n");
|
||
__leave;
|
||
} else {
|
||
createdSymLink = TRUE;
|
||
}
|
||
|
||
if( (NULL == Ieee1284Id) && (PdoTypeDaisyChain == PdoType) ) {
|
||
// SCM Micro device?
|
||
PPDO_EXTENSION pdx = pdo->DeviceExtension;
|
||
PPARALLEL_PORT_INFORMATION PortInfo = &fdx->PortInfo;
|
||
BOOLEAN bBuildStlDeviceId;
|
||
ULONG DeviceIdSize;
|
||
|
||
pdx->Controller = PortInfo->Controller;
|
||
|
||
bBuildStlDeviceId = ParStlCheckIfStl( pdx, DaisyChainId ) ;
|
||
|
||
if( TRUE == bBuildStlDeviceId ) {
|
||
Ieee1284Id = ParStlQueryStlDeviceId( pdx, NULL, 0, &DeviceIdSize, FALSE );
|
||
}
|
||
|
||
pdx->OriginalController = PortInfo->OriginalController;
|
||
|
||
P4InitializePdo( Fdo, pdo, PdoType, DaisyChainId, Ieee1284Id, wstrDeviceName, wstrSymLinkName );
|
||
|
||
if (Ieee1284Id) {
|
||
ExFreePool (Ieee1284Id);
|
||
Ieee1284Id = NULL;
|
||
}
|
||
|
||
} else {
|
||
P4InitializePdo( Fdo, pdo, PdoType, DaisyChainId, Ieee1284Id, wstrDeviceName, wstrSymLinkName );
|
||
}
|
||
|
||
} // __try
|
||
|
||
__finally {
|
||
if( STATUS_SUCCESS != status ) {
|
||
// failure - do cleanup
|
||
if( createdSymLink ) {
|
||
IoDeleteSymbolicLink( &symLinkName );
|
||
}
|
||
if( pdo ) {
|
||
IoDeleteDevice( pdo );
|
||
pdo = NULL;
|
||
}
|
||
if( wstrDeviceName ) {
|
||
ExFreePool( wstrDeviceName );
|
||
}
|
||
if( wstrSymLinkName ) {
|
||
ExFreePool( wstrSymLinkName );
|
||
}
|
||
}
|
||
} // __finally
|
||
|
||
return pdo;
|
||
}
|
||
|
||
|
||
VOID
|
||
P4SanitizeMultiSzId(
|
||
IN OUT PWSTR WCharBuffer,
|
||
IN ULONG BufWCharCount
|
||
)
|
||
// BufWCharCount == number of WCHARs (not bytes) in the string
|
||
//
|
||
// Sanitize the MULTI_SZ (HardwareID or CompatibleID) for PnP:
|
||
// 1) Leave UNICODE_NULLs (L'\0') alone, otherwise
|
||
// 2) Convert illegal characters to underscores (L'_')
|
||
// illegal characters are ( == L',' ) || ( <= L' ' ) || ( > (WCHAR)0x7F )
|
||
{
|
||
PWCHAR p = WCharBuffer;
|
||
ULONG i;
|
||
for( i = 0; i < BufWCharCount ; ++i, ++p ) {
|
||
if( L'\0'== *p ) {
|
||
continue;
|
||
} else if( (*p <= L' ') || (*p > (WCHAR)0x7F) || (L',' == *p) ) {
|
||
*p = L'_';
|
||
}
|
||
}
|
||
}
|