952 lines
28 KiB
C++
952 lines
28 KiB
C++
//-------------------------------------------------------------------
|
|
// This is implementation of WDM device
|
|
// Author: Sergey Ivanov
|
|
// Log:
|
|
// 10/01/99 - implemented
|
|
//-------------------------------------------------------------------
|
|
|
|
#ifndef __WDM_ADAPTER__
|
|
#define __WDM_ADAPTER__
|
|
#include "kernel.h"
|
|
|
|
#pragma LOCKEDCODE
|
|
|
|
class CPendingIRP;
|
|
class CLinkedList;
|
|
|
|
|
|
#pragma PAGEDCODE
|
|
// This is adapter class
|
|
// It defines default device methods specific for any WDM.
|
|
class CWDMDevice : public CDevice
|
|
{
|
|
public:
|
|
NTSTATUS m_Status;
|
|
SAFE_DESTRUCTORS();
|
|
protected:
|
|
NTSTATUS device_Default(PIRP Irp)
|
|
{
|
|
// Default functions to handle requests...
|
|
// By default we do not handle any requests if
|
|
// they are not reimplimented.
|
|
Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
|
Irp->IoStatus.Information = 0;
|
|
irp->completeRequest(Irp,IO_NO_INCREMENT);
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
};
|
|
|
|
NTSTATUS PnP_Default(IN PIRP Irp)
|
|
{
|
|
// Default device does not do anything.
|
|
// So let's just transfer request to low level driver...
|
|
irp->skipCurrentStackLocation(Irp);
|
|
return system->callDriver(m_pLowerDeviceObject, Irp);
|
|
};
|
|
|
|
NTSTATUS power_Default(IN PIRP Irp)
|
|
{
|
|
// Default device does not do anything.
|
|
// So let's just transfer request to low level driver...
|
|
power->startNextPowerIrp(Irp); // must be done while we own the IRP
|
|
irp->skipCurrentStackLocation(Irp);
|
|
return power->callPowerDriver(m_pLowerDeviceObject, Irp);
|
|
}
|
|
|
|
NTSTATUS completeDeviceRequest(PIRP Irp, NTSTATUS status, ULONG_PTR info)
|
|
{
|
|
// Complete current request with given information
|
|
|
|
if (Irp->PendingReturned)
|
|
{
|
|
irp->getCurrentStackLocation(Irp)->Control &= ~SL_PENDING_RETURNED;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = info;
|
|
irp->completeRequest(Irp,IO_NO_INCREMENT);
|
|
return status;
|
|
}
|
|
public:
|
|
// Redefine base class methods..
|
|
CWDMDevice()
|
|
{
|
|
m_Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Signature[0]=L'I';
|
|
Signature[1]=L'S';
|
|
Signature[2]=L'V';
|
|
|
|
initialized = FALSE;
|
|
if(createDeviceObjects())
|
|
{
|
|
//Default our interface
|
|
memory->copy(&InterfaceClassGuid,&GUID_CLASS_GRCLASS,sizeof(GUID));
|
|
|
|
// It is reqired to initialize our object directly
|
|
// through createDeviceObjects() function.
|
|
event->initialize(&IdleState,SynchronizationEvent, TRUE);
|
|
// This event will signal if device is ready to process requests...
|
|
event->initialize(&m_evEnabled,NotificationEvent,TRUE);
|
|
initializeRemoveLock();
|
|
m_Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
Idle_conservation = 0;
|
|
Idle_performance = 0;
|
|
m_VendorNameLength = 0;
|
|
m_DeviceTypeLength = 0;
|
|
|
|
TRACE("WDM device created...\n");
|
|
};
|
|
|
|
~CWDMDevice()
|
|
{
|
|
TRACE(" Destroing WDM device %8.8lX ...\n", this);
|
|
if(!m_RemoveLock.removing)
|
|
{
|
|
TRACE("######## ERROR: surprize destroing...\n");
|
|
remove();
|
|
}
|
|
|
|
unregisterDeviceInterface(getDeviceInterfaceName());
|
|
|
|
Signature[0]++;
|
|
Signature[1]++;
|
|
|
|
removeDeviceObjects();
|
|
};
|
|
|
|
|
|
BOOL checkValid(VOID)
|
|
{
|
|
if(!initialized) return FALSE;
|
|
|
|
return (Signature[0]==L'I' && Signature[1]==L'S'
|
|
&& Signature[2]==L'V');
|
|
};
|
|
|
|
// It is alright to create device directly or
|
|
// to call the function
|
|
virtual CDevice* create(VOID)
|
|
{
|
|
CDevice* obj = new (NonPagedPool) CWDMDevice;
|
|
RETURN_VERIFIED_OBJECT(obj);
|
|
};
|
|
|
|
|
|
virtual VOID dispose()
|
|
{
|
|
TRACE("Destroing WDM device...\n");
|
|
if(!m_RemoveLock.removing)
|
|
{
|
|
TRACE("######## ERROR: surprize destroing...\n");
|
|
remove();
|
|
}
|
|
Signature[0]++;
|
|
Signature[1]++;
|
|
removeDeviceObjects();
|
|
|
|
// The device is link to the system.
|
|
// So let system to remove device first and
|
|
// after this we will remove device object...
|
|
//self_delete();
|
|
};
|
|
|
|
BOOL createDeviceObjects()
|
|
{
|
|
debug = kernel->createDebug();
|
|
system = kernel->createSystem();
|
|
lock = kernel->createLock();
|
|
irp = kernel->createIrp();
|
|
event = kernel->createEvent();
|
|
power = kernel->createPower();
|
|
memory = kernel->createMemory();
|
|
|
|
m_IoRequests = new (NonPagedPool) CLinkedList<CPendingIRP>;
|
|
if(!system || !irp || !event || !power || !lock
|
|
|| !memory || !m_IoRequests)
|
|
{
|
|
removeDeviceObjects();
|
|
return FALSE;
|
|
}
|
|
TRACE("WDM device objects created...\n");
|
|
initialized = TRUE;
|
|
return TRUE;
|
|
};
|
|
|
|
VOID removeDeviceObjects()
|
|
{
|
|
TRACE("Destroing WDM device objects...\n");
|
|
|
|
if(m_IoRequests) delete m_IoRequests;
|
|
|
|
if(lock) lock->dispose();
|
|
if(irp) irp->dispose();
|
|
if(event) event->dispose();
|
|
|
|
if(power) power->dispose();
|
|
if(memory) memory->dispose();
|
|
|
|
if(system) system->dispose();
|
|
if(debug) debug->dispose();
|
|
initialized = FALSE;
|
|
};
|
|
|
|
// This part contains device synchronization functions.
|
|
// They should be used to synchronize device removal.
|
|
// So basically any access to device should be started with acquireRemoveLock()
|
|
// and finished with releaseRemoveLock()...
|
|
#pragma PAGEDCODE
|
|
VOID initializeRemoveLock()
|
|
{ // InitializeRemoveLock
|
|
PAGED_CODE();
|
|
event->initialize(&m_RemoveLock.evRemove, NotificationEvent, FALSE);
|
|
m_RemoveLock.usage = 1;
|
|
m_RemoveLock.removing = FALSE;
|
|
} // InitializeRemoveLock
|
|
|
|
#pragma LOCKEDCODE
|
|
NTSTATUS acquireRemoveLock()
|
|
{
|
|
LONG usage = lock->interlockedIncrement(&m_RemoveLock.usage);
|
|
|
|
if (m_RemoveLock.removing)
|
|
{ // removal in progress
|
|
if (lock->interlockedDecrement(&m_RemoveLock.usage) == 0)
|
|
event->set(&m_RemoveLock.evRemove,IO_NO_INCREMENT,FALSE);
|
|
|
|
TRACE("LOCK: m_RemoveLock.usage %d\n",m_RemoveLock.usage);
|
|
TRACE("****** FAILED TO LOCK WDM DEVICE! REMOVE REQUEST IS ACTIVE! *******\n");
|
|
return STATUS_DELETE_PENDING;
|
|
}
|
|
//TRACE("LOCK: m_RemoveLock.usage %d\n",m_RemoveLock.usage);
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
#pragma PAGEDCODE
|
|
VOID releaseRemoveLock()
|
|
{
|
|
ULONG usage;
|
|
if(m_Type==BUS_DEVICE)
|
|
{ //???????????????????????- BIG BIG BUG!!!
|
|
// It is connected only to BUS device!
|
|
// At some conditions not all remove locks was released properly.
|
|
// For other devices it is not appeared at all.
|
|
if(m_RemoveLock.usage<0) m_RemoveLock.usage = 0;
|
|
if (!m_RemoveLock.removing)
|
|
{
|
|
if(m_RemoveLock.usage<2) m_RemoveLock.usage = 2;
|
|
}
|
|
|
|
}
|
|
|
|
if (usage = lock->interlockedDecrement(&m_RemoveLock.usage) == 0)
|
|
event->set(&m_RemoveLock.evRemove,IO_NO_INCREMENT,FALSE);
|
|
//TRACE("UNLOCK: m_RemoveLock.usage %d\n",m_RemoveLock.usage);
|
|
};
|
|
|
|
#pragma PAGEDCODE
|
|
VOID releaseRemoveLockAndWait()
|
|
{
|
|
PAGED_CODE();
|
|
TRACE("REMOVING DEVICE...\n");
|
|
m_RemoveLock.removing = TRUE;
|
|
// We are going to remove device.
|
|
// So if somebody is waiting for the active device,
|
|
// first allow them to fail request and complete Irp
|
|
event->set(&m_evEnabled,IO_NO_INCREMENT,FALSE);
|
|
|
|
releaseRemoveLock();
|
|
releaseRemoveLock();
|
|
// Child device at bus could be removed by the Bus itself
|
|
// In this case it will not have second AquireRemoveLock from PnP system!
|
|
if(m_Type == CHILD_DEVICE)
|
|
if(m_RemoveLock.usage<0) m_RemoveLock.usage = 0;
|
|
TRACE("LOCK COUNT ON REMOVING %x\n",m_RemoveLock.usage);
|
|
//ASSERT(m_RemoveLock.usage==0);
|
|
event->waitForSingleObject(&m_RemoveLock.evRemove, Executive, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
BOOL isDeviceLocked()
|
|
{
|
|
lock->interlockedIncrement(&m_RemoveLock.usage);
|
|
// Add device will increment Usage!
|
|
// Current request will add more...
|
|
if(lock->interlockedDecrement(&m_RemoveLock.usage)<=2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
TRACE("Current lock count %d\n",m_RemoveLock.usage);
|
|
return TRUE;
|
|
};
|
|
|
|
// Contrary to RemoveLock disableDevice() stops and blocks any active request
|
|
// INSIDE driver. It will not fail the request but will synchronize its
|
|
// execution.
|
|
VOID disableDevice()
|
|
{
|
|
TRACE("********** DISABLING DEVICE...***********\n");
|
|
event->clear(&m_evEnabled);
|
|
}
|
|
|
|
VOID enableDevice()
|
|
{
|
|
TRACE("********** ENABLING DEVICE...***********\n");
|
|
event->set(&m_evEnabled,IO_NO_INCREMENT,FALSE);
|
|
}
|
|
|
|
|
|
BOOL synchronizeDeviceExecution()
|
|
{ // If device is not ready to process requests, block waiting for the device
|
|
ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL);
|
|
NTSTATUS status = event->waitForSingleObject(&m_evEnabled, Executive,KernelMode, FALSE, NULL);
|
|
if(!NT_SUCCESS(status) || m_RemoveLock.removing) return FALSE;
|
|
return TRUE;
|
|
}
|
|
// Functions to synchronize device execution
|
|
VOID setBusy()
|
|
{
|
|
event->clear(&IdleState);
|
|
//TRACE("\n DEVICE BUSY\n");
|
|
};
|
|
|
|
VOID setIdle()
|
|
{
|
|
event->set(&IdleState,IO_NO_INCREMENT,FALSE);
|
|
//TRACE("\n DEVICE IDLE\n");
|
|
};
|
|
|
|
NTSTATUS waitForIdle()
|
|
{
|
|
ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL);
|
|
NTSTATUS status = event->waitForSingleObject(&IdleState, Executive,KernelMode, FALSE, NULL);
|
|
if(!NT_SUCCESS(status)) return STATUS_IO_TIMEOUT;
|
|
return STATUS_SUCCESS;
|
|
};
|
|
NTSTATUS waitForIdleAndBlock()
|
|
{
|
|
if(NT_SUCCESS(waitForIdle()))
|
|
{
|
|
setBusy();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else return STATUS_IO_TIMEOUT;
|
|
};
|
|
|
|
BOOL registerDeviceInterface(const GUID* Guid)
|
|
{
|
|
if(isDeviceInterfaceRegistered())
|
|
{
|
|
TRACE("Device interface already active...\n");
|
|
return TRUE;
|
|
}
|
|
|
|
if(memory) memory->copy(&InterfaceClassGuid, Guid,sizeof(GUID));
|
|
|
|
TRACE("Registering device interface at system...\n");
|
|
NTSTATUS Status = system->registerDeviceInterface(getPhysicalObject(),
|
|
&InterfaceClassGuid, NULL, getDeviceInterfaceName());
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("#### Failed to register device interface...\n");
|
|
return FALSE;
|
|
}
|
|
system->setDeviceInterfaceState(getDeviceInterfaceName(),TRUE);
|
|
m_DeviceInterfaceRegistered = TRUE;
|
|
return TRUE;
|
|
};
|
|
|
|
VOID unregisterDeviceInterface(UNICODE_STRING* InterfaceName)
|
|
{
|
|
if(isDeviceInterfaceRegistered())
|
|
{
|
|
TRACE("Unregistering device interface...\n");
|
|
system->setDeviceInterfaceState(InterfaceName,FALSE);
|
|
}
|
|
m_DeviceInterfaceRegistered = FALSE;
|
|
};
|
|
|
|
virtual NTSTATUS setVendorName(const PCHAR Name,USHORT Length)
|
|
{
|
|
m_VendorNameLength = Length<MAXIMUM_ATTR_STRING_LENGTH? Length:MAXIMUM_ATTR_STRING_LENGTH;
|
|
if(!m_VendorNameLength) return STATUS_INVALID_PARAMETER;
|
|
memory->copy(m_VendorName, Name, m_VendorNameLength);
|
|
return STATUS_SUCCESS;
|
|
|
|
};
|
|
virtual NTSTATUS getVendorName(PUCHAR Name,PUSHORT pLength)
|
|
{
|
|
USHORT Len = m_VendorNameLength<*pLength? m_VendorNameLength:*pLength;
|
|
*pLength = Len;
|
|
if(!Len) return STATUS_INVALID_PARAMETER;
|
|
memory->copy(Name, m_VendorName, Len);
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
virtual NTSTATUS setDeviceType(const PCHAR Type,USHORT Length)
|
|
{
|
|
m_DeviceTypeLength = Length<MAXIMUM_ATTR_STRING_LENGTH? Length:MAXIMUM_ATTR_STRING_LENGTH;
|
|
if(!m_DeviceTypeLength) return STATUS_INVALID_PARAMETER;
|
|
memory->copy(m_DeviceType, Type, m_DeviceTypeLength);
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
virtual NTSTATUS getDeviceType(PUCHAR Type,PUSHORT pLength)
|
|
{
|
|
USHORT Len = m_DeviceTypeLength<*pLength? m_DeviceTypeLength:*pLength;
|
|
*pLength = Len;
|
|
if(!Len) return STATUS_INVALID_PARAMETER;
|
|
memory->copy(Type, m_DeviceType, Len);
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
// This is basic PnP part of driver.
|
|
// It allows to add and remove device.
|
|
// Specific PnP request should be reimplemented by clients...
|
|
virtual NTSTATUS createDeviceObjectByName(PDEVICE_OBJECT* ppFdo)
|
|
{
|
|
if(!ALLOCATED_OK(system)) return STATUS_INSUFFICIENT_RESOURCES;
|
|
// By default we will create autogenerated name...
|
|
// Specific implementations can overwrite the function to
|
|
// change the functionality.
|
|
return system->createDevice(m_DriverObject,sizeof(CWDMDevice*),NULL,
|
|
FILE_DEVICE_UNKNOWN,FILE_AUTOGENERATED_DEVICE_NAME,FALSE,ppFdo);
|
|
};
|
|
|
|
virtual NTSTATUS registerDevicePowerPolicy()
|
|
{ // By default all devices at startup are ON
|
|
if(!ALLOCATED_OK(power)) return STATUS_INSUFFICIENT_RESOURCES;
|
|
POWER_STATE state;
|
|
state.DeviceState = PowerDeviceD0;
|
|
power->declarePowerState(m_DeviceObject, DevicePowerState, state);
|
|
|
|
if(m_PhysicalDeviceObject)
|
|
{
|
|
m_CurrentDevicePowerState = PowerDeviceD0;
|
|
m_Idle = power->registerDeviceForIdleDetection(m_PhysicalDeviceObject,Idle_conservation,Idle_performance, PowerDeviceD3);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
virtual NTSTATUS initializeInterruptSupport()
|
|
{
|
|
// Here is where we can initialize our DPC (Deferred Procedure Call) object
|
|
// that allows our interrupt service routine to request a DPC to finish handling
|
|
// a device interrupt.
|
|
// At default WDM device we do not do this.
|
|
//interrupt->initializeDpcRequest(m_DeviceObject,&CALLBACK_FUNCTION(DpcForIsr));
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
NTSTATUS add(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pPdo)
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT pFdo;
|
|
if(!ALLOCATED_OK(system)) return STATUS_INSUFFICIENT_RESOURCES;
|
|
TRACE("Add with Driver %8.8lX, pPDO %8.8lX\n",DriverObject, pPdo);
|
|
|
|
// Init first our objects...
|
|
m_DriverObject = DriverObject;
|
|
m_PhysicalDeviceObject = pPdo;
|
|
// create Fdo for the registered objects.
|
|
// Clients can overwrite device object name and it's visibility.
|
|
status = createDeviceObjectByName(&pFdo);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TRACE("#### Failed to create physical device! Status %x\n",status);
|
|
DISPOSE_OBJECT(m_DeviceObjectName);
|
|
return status;
|
|
}
|
|
|
|
TRACE(" Device object was created %8.8lX\n",pFdo);
|
|
m_DeviceObject = pFdo;
|
|
m_Added = TRUE;
|
|
|
|
CLogger* logger = kernel->getLogger();
|
|
|
|
if(pPdo)
|
|
{
|
|
m_pLowerDeviceObject = system->attachDevice(pFdo, pPdo);
|
|
if(!m_pLowerDeviceObject)
|
|
{
|
|
TRACE("#### Failed to get lower device object...\n");
|
|
if(ALLOCATED_OK(logger))
|
|
logger->logEvent(GRCLASS_FAILED_TO_ADD_DEVICE,getSystemObject());
|
|
system->deleteDevice(pFdo);
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
}
|
|
else m_pLowerDeviceObject = NULL;
|
|
|
|
|
|
initializeInterruptSupport();
|
|
|
|
pFdo->Flags |= DO_BUFFERED_IO;
|
|
pFdo->Flags |= DO_POWER_PAGABLE;
|
|
pFdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
registerDevicePowerPolicy();
|
|
TRACE("WDM device added...\n");
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
VOID remove()
|
|
{
|
|
if(!m_Added) return;
|
|
TRACE("Removing WDM device...\n");
|
|
// Wait untill we finished all activity at device
|
|
releaseRemoveLockAndWait();
|
|
|
|
// Remove device from our system
|
|
TRACE("Unregistering device from kernel...\n");
|
|
kernel->unregisterObject(getSystemObject());
|
|
|
|
TRACE("Removing device object name...\n");
|
|
if(m_DeviceObjectName) delete m_DeviceObjectName;
|
|
m_DeviceObjectName = NULL;
|
|
|
|
if(m_pLowerDeviceObject)
|
|
{
|
|
TRACE("Detaching device from system...\n");
|
|
system->detachDevice(m_pLowerDeviceObject);
|
|
}
|
|
TRACE("WDM device removed...\n");
|
|
|
|
// Tell our system - device removed...
|
|
m_Added = FALSE;
|
|
|
|
// Removing device from system could result in
|
|
// requesting Unload() from system if the device was last registered device.
|
|
// So, this call should be last call AFTER disposing the device.
|
|
};
|
|
|
|
virtual VOID onDeviceStop()
|
|
{
|
|
return;
|
|
};
|
|
|
|
|
|
NTSTATUS forward(PIRP Irp, PIO_COMPLETION_ROUTINE Routine)
|
|
{
|
|
CIoPacket* IoPacket;
|
|
// This function sends the current request
|
|
// If completion routine is not set it will complete
|
|
// the request by default(it means without doing anything special).
|
|
TRACE("WDM forward()...\n");
|
|
IoPacket = new (NonPagedPool) CIoPacket(Irp);
|
|
if(!ALLOCATED_OK(IoPacket))
|
|
{
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IoPacket->copyCurrentStackToNext();
|
|
if(Routine) IoPacket->setCompletion(Routine);
|
|
else IoPacket->setDefaultCompletionFunction();
|
|
NTSTATUS status = system->callDriver(getLowerDriver(),IoPacket->getIrpHandle());
|
|
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
};
|
|
// Send the current request to low level driver and wait for reply
|
|
// Current IRP will not be completed, so we can process it and
|
|
// complete later.
|
|
// See also description of send() function.
|
|
NTSTATUS forwardAndWait(PIRP Irp)
|
|
{ // Send request to low level and wait for a reply
|
|
CIoPacket* IoPacket;
|
|
|
|
TRACE("WDM forwardAndWait()...\n");
|
|
IoPacket = new (NonPagedPool) CIoPacket(Irp);
|
|
if(!ALLOCATED_OK(IoPacket))
|
|
{
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
IoPacket->setCurrentStack();
|
|
IoPacket->setStackDefaults();
|
|
NTSTATUS status = system->callDriver(getLowerDriver(),IoPacket->getIrpHandle());
|
|
if(status == STATUS_PENDING)
|
|
{
|
|
TRACE("Waiting for the bus driver to complete...\n");
|
|
ASSERT(system->getCurrentIrql()<=DISPATCH_LEVEL);
|
|
status = IoPacket->waitForCompletion();
|
|
TRACE("Request completed with status %x\n",status);
|
|
}
|
|
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
};
|
|
|
|
// WDM by default just forwards requests...
|
|
virtual NTSTATUS send(CIoPacket* Irp)
|
|
{
|
|
TRACE("WDM sendRequestToDevice()\n");
|
|
if(Irp) return forward(Irp->getIrpHandle(),NULL);
|
|
else return STATUS_INVALID_PARAMETER;
|
|
};
|
|
|
|
virtual NTSTATUS sendAndWait(CIoPacket* Irp)
|
|
{
|
|
TRACE("WDM sendRequestToDeviceAndWait()\n");
|
|
if(Irp) return forwardAndWait(Irp->getIrpHandle());
|
|
else return STATUS_INVALID_PARAMETER;
|
|
};
|
|
|
|
// Define device interface functions
|
|
virtual NTSTATUS write(PUCHAR pRequest,ULONG RequestLength)
|
|
{
|
|
CIoPacket* IoPacket;
|
|
if(!pRequest || !RequestLength) return STATUS_INVALID_PARAMETER;
|
|
IoPacket = new (NonPagedPool) CIoPacket(getLowerDriver()->StackSize);
|
|
if(!ALLOCATED_OK(IoPacket))
|
|
{
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
TRACE("IoPacket with device %x\n",getSystemObject());
|
|
IoPacket->setTimeout(getCommandTimeout());
|
|
IoPacket->buildStack(getSystemObject(),IRP_MJ_WRITE);
|
|
IoPacket->setWriteLength(RequestLength);
|
|
IoPacket->copyBuffer(pRequest,RequestLength);
|
|
|
|
TRACE("WDM write()...\n");
|
|
NTSTATUS status = send(IoPacket);
|
|
TRACE("WDM write finished: %x\n", status);
|
|
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
};
|
|
|
|
virtual NTSTATUS writeAndWait(PUCHAR pRequest,ULONG RequestLength,PUCHAR pReply,ULONG* pReplyLength)
|
|
{
|
|
CIoPacket* IoPacket;
|
|
if(!pRequest || !RequestLength || !pReply || !pReplyLength) return STATUS_INVALID_PARAMETER;
|
|
IoPacket = new (NonPagedPool) CIoPacket(getLowerDriver()->StackSize);
|
|
if(!ALLOCATED_OK(IoPacket))
|
|
{
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
TRACE("IoPacket with device %x\n",getSystemObject());
|
|
IoPacket->setTimeout(getCommandTimeout());
|
|
IoPacket->buildStack(getSystemObject(),IRP_MJ_WRITE);
|
|
IoPacket->setWriteLength(RequestLength);
|
|
IoPacket->copyBuffer(pRequest,RequestLength);
|
|
|
|
TRACE("WDM sendAndWait()...\n");
|
|
NTSTATUS status = sendAndWait(IoPacket);
|
|
TRACE("WDM writeAndWait finished: %x\n",status);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
*pReplyLength = 0;
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
}
|
|
|
|
*pReplyLength = (ULONG)IoPacket->getInformation();
|
|
IoPacket->getSystemReply(pReply,*pReplyLength);
|
|
//TRACE_BUFFER(pReply,*pReplyLength);
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
};
|
|
|
|
virtual NTSTATUS readAndWait(PUCHAR pRequest,ULONG RequestLength,PUCHAR pReply,ULONG* pReplyLength)
|
|
{
|
|
CIoPacket* IoPacket;
|
|
if(!pRequest || !RequestLength || !pReply || !pReplyLength) return STATUS_INVALID_PARAMETER;
|
|
IoPacket = new (NonPagedPool) CIoPacket(getLowerDriver()->StackSize);
|
|
if(!ALLOCATED_OK(IoPacket))
|
|
{
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
IoPacket->setTimeout(getCommandTimeout());
|
|
IoPacket->buildStack(getSystemObject(),IRP_MJ_READ);
|
|
IoPacket->setReadLength(RequestLength);
|
|
IoPacket->copyBuffer(pRequest,RequestLength);
|
|
|
|
TRACE("WDM sendAndWait()...\n");
|
|
NTSTATUS status = sendAndWait(IoPacket);
|
|
TRACE("WDM sendAndWait finished: %x\n",status);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
*pReplyLength = 0;
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
}
|
|
|
|
*pReplyLength = (ULONG)IoPacket->getInformation();
|
|
IoPacket->getSystemReply(pReply,*pReplyLength);
|
|
|
|
TRACE_BUFFER(pReply,*pReplyLength);
|
|
DISPOSE_OBJECT(IoPacket);
|
|
return status;
|
|
};
|
|
|
|
NTSTATUS synchronizeDevicePowerState()
|
|
{
|
|
if (m_CurrentDevicePowerState!=PowerDeviceD0)
|
|
{
|
|
NTSTATUS status;
|
|
TRACE("RESTORING DEVICE POWER ON from state %d!\n",m_CurrentDevicePowerState);
|
|
status = sendDeviceSetPower(PowerDeviceD0,TRUE);
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TRACE("FAILED TO SET POWER ON DEVICE STATE!\n");
|
|
return status;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS sendDeviceSetPower(DEVICE_POWER_STATE devicePower, BOOLEAN wait)
|
|
{// SendDeviceSetPower
|
|
POWER_STATE state;
|
|
NTSTATUS status;
|
|
|
|
state.DeviceState = devicePower;
|
|
if (wait)
|
|
{// synchronous operation
|
|
KEVENT Event;
|
|
event->initialize(&Event, NotificationEvent, FALSE);
|
|
POWER_CONTEXT context = {&Event};
|
|
|
|
status = power->requestPowerIrp(getPhysicalObject(), IRP_MN_SET_POWER, state,
|
|
(PREQUEST_POWER_COMPLETE) onSendDeviceSetPowerComplete, &context, NULL);
|
|
if (status == STATUS_PENDING)
|
|
{
|
|
event->waitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
status = context.status;
|
|
}
|
|
}// synchronous operation
|
|
else
|
|
status = power->requestPowerIrp(getPhysicalObject(), IRP_MN_SET_POWER,
|
|
state, NULL, NULL, NULL);
|
|
|
|
return status;
|
|
}// SendDeviceSetPower
|
|
// These functions define default interface with system.
|
|
// Clients should redefine them if they would like to have
|
|
// specific functionality.
|
|
virtual NTSTATUS pnpRequest(IN PIRP irp){return PnP_Default(irp);};
|
|
virtual NTSTATUS powerRequest(PIRP irp) {return power_Default(irp);};
|
|
|
|
// By default we allow user to get connection with device
|
|
virtual NTSTATUS open(PIRP irp) {return completeDeviceRequest(irp, STATUS_SUCCESS, 0); };//Create
|
|
virtual NTSTATUS close(PIRP irp){return completeDeviceRequest(irp, STATUS_SUCCESS, 0); };
|
|
|
|
virtual NTSTATUS read(PIRP irp) { return device_Default(irp); };
|
|
virtual NTSTATUS write(PIRP irp) { return device_Default(irp); };
|
|
|
|
virtual NTSTATUS deviceControl(PIRP irp) { return device_Default(irp);};
|
|
|
|
virtual NTSTATUS cleanup(PIRP irp) { return device_Default(irp); };
|
|
virtual NTSTATUS flush(PIRP irp) { return device_Default(irp); };
|
|
// Standard system startIo
|
|
// Actually we do not use it for now.
|
|
// Instead we have our own synchronization facilities.
|
|
virtual VOID startIo(PIRP irp){};
|
|
//---------------------------------------------------------------------------//
|
|
// SYNCHRONIZATION FACILITIES //
|
|
//---------------------------------------------------------------------------//
|
|
// To make synchronization at the driver we have to store and make pending
|
|
// all our requests.
|
|
// Specific devices should set specific thread which will start all pending Irp.
|
|
//---------------------------------------------------------------------------//
|
|
// CALLBACK FUNCTION:
|
|
// This function will not only complete current Irp but also will dispose
|
|
// corresponding IoRequest if any was pending at driver.
|
|
#pragma LOCKEDCODE
|
|
virtual VOID cancelPendingIrp(PIRP Irp)
|
|
{
|
|
KIRQL ioIrql;
|
|
// 1.
|
|
// We keep pending Irp inside list of IoRequests.
|
|
// So we do not need to warry about removing Irp from a queue...
|
|
// 2.
|
|
// As soon as IoRequest started, we do not allow to cancel it.
|
|
// So, in this case this function will not be called and it is responsibility
|
|
// of the driver to finish (or cancel) active IoRequest.
|
|
// It means this function should not warry about active (and removed from our queue)
|
|
// IoRequests. But it has to warry about not yet started requests...
|
|
|
|
|
|
TRACE(" CANCELLING IRP %8.8lX...\n", Irp);
|
|
// Release cancel spin lock if somebody own it...
|
|
lock->releaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
// Get our own spin lock in case somebody desided to cancel this Irp
|
|
lock->acquireCancelSpinLock(&ioIrql);
|
|
// Reset our cancel routine to prevent it being called...
|
|
irp->setCancelRoutine(Irp, NULL);
|
|
|
|
// If Irp was on the queue - remove IoRequest from queue...
|
|
if(m_IoRequests)
|
|
{
|
|
CPendingIRP* IrpReq = m_IoRequests->getFirst();
|
|
while (IrpReq)
|
|
{
|
|
if(IrpReq->Irp == Irp)
|
|
{ // We found our Irp.
|
|
m_IoRequests->remove(IrpReq);
|
|
TRACE(" IO REQUEST WAS DISPOSED...\n");
|
|
IrpReq->dispose();
|
|
break;
|
|
}
|
|
IrpReq = m_IoRequests->getNext(IrpReq);
|
|
}
|
|
}
|
|
|
|
|
|
if(m_OpenSessionIrp == Irp)
|
|
{
|
|
TRACE(" OPEN SESSION IRP WAS CANCELLED...\n");
|
|
m_OpenSessionIrp = NULL;
|
|
}
|
|
|
|
// Complete Irp as canceled...
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
// Release our spin lock...
|
|
lock->releaseCancelSpinLock(ioIrql);
|
|
TRACE(" IRP %8.8lX WAS CANCELLED...\n", Irp);
|
|
irp->completeRequest(Irp, IO_NO_INCREMENT);
|
|
};
|
|
#pragma PAGEDCODE
|
|
|
|
virtual CLinkedList<CPendingIRP>* getIoRequestsQueue()
|
|
{
|
|
return m_IoRequests;
|
|
};
|
|
|
|
|
|
virtual NTSTATUS makeRequestPending(PIRP Irp_request,PDEVICE_OBJECT toDeviceObject,PENDING_REQUEST_TYPE Type)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
lock->acquireCancelSpinLock(&OldIrql);
|
|
if (Irp_request->Cancel)
|
|
{
|
|
TRACE(" <<<<<< IO REQUEST CANCELLED... %8.8lX>>>>>>\n",Irp_request);
|
|
lock->releaseCancelSpinLock(OldIrql);
|
|
return STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
TRACE(" <<<<<< IO REQUEST PENDING %8.8lX>>>>>>\n",Irp_request);
|
|
|
|
CPendingIRP* IrpReq = new (NonPagedPool) CPendingIRP(Irp_request,Type,toDeviceObject);
|
|
if(!IrpReq)
|
|
{
|
|
lock->releaseCancelSpinLock(OldIrql);
|
|
TRACE("ERROR! FAILED TO ALLOCATE IoRequest. LOW ON MEMORY!\n");
|
|
return completeDeviceRequest(Irp_request,STATUS_INSUFFICIENT_RESOURCES,0);
|
|
}
|
|
|
|
Irp_request->IoStatus.Information=0;
|
|
Irp_request->IoStatus.Status=STATUS_PENDING;
|
|
irp->setCancelRoutine(Irp_request, CALLBACK_FUNCTION(cancelPendingIrp));
|
|
lock->releaseCancelSpinLock(OldIrql);
|
|
irp->markPending(Irp_request);
|
|
m_IoRequests->New(IrpReq);
|
|
return STATUS_PENDING;
|
|
}
|
|
};
|
|
|
|
// Cancel current pending IO request
|
|
virtual NTSTATUS cancelPendingRequest(CPendingIRP* IrpReq)
|
|
{
|
|
// Next function will remove and dispose our request...
|
|
cancelPendingIrp(IrpReq->Irp);
|
|
return STATUS_CANCELLED;
|
|
};
|
|
|
|
// Cancel all pending IO requests
|
|
virtual NTSTATUS cancelAllPendingRequests()
|
|
{
|
|
// Next function will remove and dispose our request...
|
|
if(m_IoRequests)
|
|
{
|
|
CPendingIRP* IrpReqNext;
|
|
CPendingIRP* IrpReq = m_IoRequests->getFirst();
|
|
while (IrpReq)
|
|
{
|
|
IrpReqNext = m_IoRequests->getNext(IrpReq);
|
|
cancelPendingRequest(IrpReq);// This call will dispose request...
|
|
IrpReq = IrpReqNext;
|
|
}
|
|
}
|
|
if(m_OpenSessionIrp) cancelPendingIrp(m_OpenSessionIrp);
|
|
return STATUS_CANCELLED;
|
|
};
|
|
|
|
|
|
// Checks if request queue is empty and if it is NOT - starts next request...
|
|
// This function will be called by the Irp processing thread.
|
|
virtual NTSTATUS startNextPendingRequest()
|
|
{
|
|
TRACE(" startNextPendingRequest() was called...\n");
|
|
if (!m_IoRequests->IsEmpty())
|
|
{
|
|
KIRQL OldIrql;
|
|
CDevice* device;
|
|
NTSTATUS status;
|
|
CPendingIRP* IrpReq = m_IoRequests->removeHead();
|
|
if(!IrpReq) return STATUS_INVALID_PARAMETER;
|
|
|
|
lock->acquireCancelSpinLock(&OldIrql);
|
|
// Now Irp can not be canceled!
|
|
irp->setCancelRoutine(IrpReq->Irp, NULL);
|
|
if (IrpReq->Irp->Cancel)
|
|
{
|
|
lock->releaseCancelSpinLock(OldIrql);
|
|
// Current Irp was already canceled,
|
|
// Cancel function will be called shortly.
|
|
// So just forget about current Irp.
|
|
return STATUS_SUCCESS;;
|
|
}
|
|
lock->releaseCancelSpinLock(OldIrql);
|
|
|
|
device = (CDevice*)IrpReq->DeviceObject->DeviceExtension;
|
|
// Call device specific startIo function...
|
|
TRACE(" Device startIoRequest() was called...\n");
|
|
if(device) status = device->startIoRequest(IrpReq);
|
|
else status = STATUS_INVALID_DEVICE_STATE;
|
|
return status;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
virtual NTSTATUS ThreadRoutine()
|
|
{
|
|
//NTSTATUS status;
|
|
//if(!NT_SUCCESS(status = waitForIdleAndBlock())) return status;
|
|
// If somebody inserted pending request - dispatch it...
|
|
// It will call specific child device startIoRequest().
|
|
// It is up to that device how to handle it.
|
|
// If child device is busy - it can insert this request into
|
|
// child device request queue again and process it later...
|
|
startNextPendingRequest();
|
|
//setIdle();
|
|
return STATUS_SUCCESS;
|
|
};
|
|
|
|
|
|
// Device specific function which processes pending requests...
|
|
// It will be redefined by specific devices.
|
|
// This function always should be virtual because
|
|
// we expect specific device behaviour...
|
|
virtual NTSTATUS startIoRequest(CPendingIRP* IoReq)
|
|
{
|
|
// Default startIo just cancel current request.
|
|
// IoReq will be disposed...
|
|
if(IoReq)
|
|
{
|
|
cancelPendingRequest(IoReq);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
};
|
|
};
|
|
|
|
#endif //If not defined
|