|
|
//-------------------------------------------------------------------
// 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); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 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
|