windows-nt/Source/XPSP1/NT/drivers/smartcrd/gempc430/iopack.cpp
2020-09-26 16:20:57 +08:00

453 lines
11 KiB
C++

#include "iopack.h"
#include "kernel.h"
#pragma LOCKEDCODE
NTSTATUS onRequestComplete(PDEVICE_OBJECT pDO,IN PIRP Irp, IN PVOID context)
{
//DBG_PRINT(" ======= Request completion Irp %8.8lX, Packet %8.8lX\n",Irp,context);
CIoPacket* packet = (CIoPacket*) context;
if(packet)
{
return packet->onRequestComplete();
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
#pragma PAGEDCODE
CIoPacket::CIoPacket(UCHAR StackSize)
{
m_Status = STATUS_INSUFFICIENT_RESOURCES;
systemIrp = FALSE;
m_DoNotFreeIrp = FALSE;
CompletionEvent = NULL;
IoStatus.Status = STATUS_SUCCESS;
IoStatus.Information = 0;
SystemBuffer = NULL;
m_Irp = NULL;
m_TimeOut = 60000;// Default timeout 60 seconds for any kind of IORequest
__try
{
debug = kernel->createDebug();
memory = kernel->createMemory();
event = kernel->createEvent();
irp = kernel->createIrp();
if( !ALLOCATED_OK(memory) || !ALLOCATED_OK(event) ||
!ALLOCATED_OK(irp)) __leave;
SystemBuffer = memory->allocate(NonPagedPool,PAGE_SIZE);
if(!SystemBuffer) __leave;
m_Irp = irp->allocate(StackSize+1, FALSE);
if (!m_Irp) __leave;
irp->initialize(m_Irp,irp->sizeOfIrp(StackSize+1),StackSize+1);
Stack = *(irp->getNextStackLocation(m_Irp));
irp->setCompletionRoutine(m_Irp,CALLBACK_FUNCTION(onRequestComplete),NULL,TRUE,TRUE,TRUE);
m_Status = STATUS_SUCCESS;
}
__finally
{
if(!NT_SUCCESS(m_Status))
{
// Remove all allocated objects...
// In this constructor we know that it is not system Irp...
TRACE("FAILED TO CREATE IoPacket object %x\n",m_Status);
TRACE("SystemBuffer - %x\n",SystemBuffer);
TRACE("debug - %x, memory - %x\n",debug,memory);
TRACE("event - %x, irp - %x\n",event,irp);
if(ALLOCATED_OK(memory))
{
if(SystemBuffer) memory->free(SystemBuffer);
SystemBuffer = NULL;
}
if(ALLOCATED_OK(irp))
{
if(m_Irp) irp->free(m_Irp);
m_Irp = NULL;
}
DISPOSE_OBJECT(irp);
DISPOSE_OBJECT(event);
DISPOSE_OBJECT(memory);
DISPOSE_OBJECT(debug);
}
}
};
CIoPacket::CIoPacket(PIRP Irp)
{
m_Status = STATUS_INSUFFICIENT_RESOURCES;
systemIrp = TRUE;
m_DoNotFreeIrp = FALSE;
CompletionEvent = NULL;
IoStatus.Status = STATUS_SUCCESS;
IoStatus.Information = 0;
SystemBuffer = NULL;
m_TimeOut = 60000;// Default timeout 60 seconds for any kind of IORequest
m_Irp = NULL;
__try
{
if(!Irp) __leave;
debug = kernel->createDebug();
memory = kernel->createMemory();
event = kernel->createEvent();
irp = kernel->createIrp();
if( !ALLOCATED_OK(memory) || !ALLOCATED_OK(event) ||
!ALLOCATED_OK(irp)) __leave;
m_Irp = Irp;
Stack = *(irp->getNextStackLocation(m_Irp));
SystemBuffer = m_Irp->AssociatedIrp.SystemBuffer;
// We do not care here if system buffers is NULL
// but we will not copy data if it will be not initialized (NULL)
m_Status = STATUS_SUCCESS;
}
__finally
{
if(!NT_SUCCESS(m_Status))
{
TRACE("FAILED TO CREATE IoPacket object %x\n",m_Status);
TRACE("SystemBuffer - %x, Irp - %x\n",SystemBuffer,Irp);
TRACE("debug - %x, memory - %x\n",debug,memory);
TRACE("event - %x, irp - %x\n",event,irp);
// Remove all allocated objects...
DISPOSE_OBJECT(irp);
DISPOSE_OBJECT(event);
DISPOSE_OBJECT(memory);
DISPOSE_OBJECT(debug);
}
}
};
CIoPacket::~CIoPacket()
{
if(!systemIrp)
{
if(SystemBuffer) memory->free(SystemBuffer);
SystemBuffer = NULL;
}
DISPOSE_OBJECT(irp);
DISPOSE_OBJECT(event);
DISPOSE_OBJECT(memory);
DISPOSE_OBJECT(debug);
};
VOID CIoPacket::setMajorIOCtl(UCHAR controlCode)
{
Stack.MajorFunction = controlCode;
};
UCHAR CIoPacket::getMajorIOCtl()
{
return Stack.MajorFunction;
};
VOID CIoPacket::setMinorIOCtl(UCHAR controlCode)
{
Stack.MinorFunction = controlCode;
};
NTSTATUS CIoPacket::buildStack(PDEVICE_OBJECT DeviceObject, ULONG Major, UCHAR Minor, ULONG IoCtl, PVOID Context)
{
// Create copy of the next stack
if(!m_Irp) return STATUS_INVALID_DEVICE_STATE;
Stack = *(irp->getNextStackLocation(m_Irp));
Stack.DeviceObject = DeviceObject;
switch(Major)
{
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
// Set stack parameters...
Stack.MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
Stack.Parameters.Others.Argument1 = Context;
Stack.Parameters.DeviceIoControl.IoControlCode = IoCtl;
}
break;
case IRP_MJ_PNP:
{
// Set stack parameters...
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = Minor;
if(Minor==IRP_MN_QUERY_CAPABILITIES)
{
Stack.Parameters.DeviceCapabilities.Capabilities = (PDEVICE_CAPABILITIES) Context;
}
}
break;
default:
// Copy current stack location to next...
if(systemIrp) Stack = *(irp->getCurrentStackLocation(m_Irp));
else
{
Stack.DeviceObject = DeviceObject;
Stack.MajorFunction = (UCHAR)Major;
Stack.MinorFunction = Minor;
}
}
return STATUS_SUCCESS;
};
VOID CIoPacket::copyStackToNext()
{
PIO_STACK_LOCATION nextStack;
if(!m_Irp) return;
nextStack = irp->getNextStackLocation(m_Irp);
if(nextStack) *nextStack = Stack;
};
VOID CIoPacket::copyCurrentStackToNext()
{
if(!m_Irp) return;
irp->copyCurrentStackLocationToNext(m_Irp);
}
// Function will set completion routine for the Irp.
VOID CIoPacket::setCompletion(PIO_COMPLETION_ROUTINE CompletionFunction)
{
PIO_COMPLETION_ROUTINE Completion;
if(!m_Irp) return;
Completion = CompletionFunction==NULL ? CALLBACK_FUNCTION(onRequestComplete) : CompletionFunction;
if(m_Irp) irp->setCompletionRoutine(m_Irp,Completion,this,TRUE,TRUE,TRUE);
};
VOID CIoPacket::setDefaultCompletionFunction()
{
if(m_Irp) irp->setCompletionRoutine(m_Irp,CALLBACK_FUNCTION(onRequestComplete),this,TRUE,TRUE,TRUE);
};
NTSTATUS CIoPacket::copyBuffer(PUCHAR pBuffer, ULONG BufferLength)
{
if(!pBuffer || !BufferLength || BufferLength>PAGE_SIZE) return STATUS_INVALID_PARAMETER;
if(m_Irp)
{
if(!systemIrp)
{
if(!m_Irp->AssociatedIrp.SystemBuffer)
{
if(!SystemBuffer)
{
SystemBuffer = memory->allocate(NonPagedPool,PAGE_SIZE);
if(!SystemBuffer) return STATUS_INSUFFICIENT_RESOURCES;
}
m_Irp->AssociatedIrp.SystemBuffer = SystemBuffer;
}
}
if(m_Irp->AssociatedIrp.SystemBuffer)
memory->copy(m_Irp->AssociatedIrp.SystemBuffer,pBuffer,BufferLength);
else
{
TRACE(" ***** AssociatedIrp SYSTEM BUFFER IS NULL!\nFailed to copy bus driver reply with len %x!\n",BufferLength);
}
return STATUS_SUCCESS;
}
else return STATUS_INSUFFICIENT_RESOURCES;
};
PIO_STACK_LOCATION CIoPacket::getStack()
{
return &Stack;
};
PVOID CIoPacket::getBuffer()
{
return SystemBuffer;
};
ULONG CIoPacket::getReadLength()
{
return Stack.Parameters.Read.Length;
};
VOID CIoPacket::setWriteLength(ULONG length)
{
Stack.Parameters.Write.Length = length;
};
VOID CIoPacket::setReadLength(ULONG length)
{
Stack.Parameters.Read.Length = length;
};
ULONG CIoPacket::getWriteLength()
{
return Stack.Parameters.Write.Length;
};
VOID CIoPacket::setInformation(ULONG_PTR information)
{
if(m_Irp) m_Irp->IoStatus.Information = information;
IoStatus.Information = information;
};
ULONG_PTR CIoPacket::getInformation()
{
return IoStatus.Information;
};
VOID CIoPacket::updateInformation()
{
if(m_Irp) IoStatus.Information = m_Irp->IoStatus.Information;
};
NTSTATUS CIoPacket::getSystemReply(PUCHAR pReply,ULONG Length)
{
if(!pReply || !Length || Length> PAGE_SIZE) return STATUS_INVALID_PARAMETER;
if(SystemBuffer)
{
memory->copy(pReply,SystemBuffer,Length);
return STATUS_SUCCESS;
}
else return STATUS_INSUFFICIENT_RESOURCES;
};
#pragma LOCKEDCODE
NTSTATUS CIoPacket::onRequestComplete()
{ // Callback to finish previously sended request
TRACE(" =======> IoPacket processes Completion()\n");
if(systemIrp)
{
if (m_Irp->PendingReturned)
{
TRACE(" Irp marked as pending...\n");
irp->markPending(m_Irp);
}
}
IoStatus.Status = m_Irp->IoStatus.Status;
IoStatus.Information = m_Irp->IoStatus.Information;
TRACE(" Irp completes with status %8.8lX , info %8.8lX\n",IoStatus.Status,IoStatus.Information);
if(!systemIrp)
{
if(!m_DoNotFreeIrp)
{
PIRP Irp = m_Irp;
m_Irp = NULL;
if(Irp) irp->free(Irp);
}
}
if(CompletionEvent) event->set(CompletionEvent,IO_NO_INCREMENT,FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
};
#pragma PAGEDCODE
VOID CIoPacket::setCompletionEvent(PKEVENT CompletionEvent)
{
if(CompletionEvent)
{
this->CompletionEvent = CompletionEvent;
}
}
VOID CIoPacket::setStatus(NTSTATUS status)
{
IoStatus.Status = status;
}
NTSTATUS CIoPacket::getStatus()
{
return IoStatus.Status;
}
VOID CIoPacket::setDefaultCompletionEvent()
{
event->initialize(&DefaultCompletionEvent,NotificationEvent, FALSE);
setCompletionEvent(&DefaultCompletionEvent);
}
NTSTATUS CIoPacket::waitForCompletion()
{ // Set current timeout
return waitForCompletion(getTimeout());
}
NTSTATUS CIoPacket::waitForCompletion(LONG TimeOut)
{
// Because we set Alertable parameter to FALSE,
// there are only two possible statuses from the function STATUS_SUCCESS and
// STATUS_TIMEOUT...
// We should not try to cancel system Irps!
if(systemIrp)
{
NTSTATUS status;
status = event->waitForSingleObject(CompletionEvent, Executive,KernelMode, FALSE, NULL);
if(!NT_SUCCESS(status))
{
TRACE("waitForCompletion() reports error %x\n", status);
setStatus(STATUS_IO_TIMEOUT);
setInformation(0);
}
status = getStatus();
return status;
}
else
{
LARGE_INTEGER timeout;
timeout.QuadPart = -TimeOut * 10000;
if (event->waitForSingleObject(CompletionEvent, Executive, KernelMode, FALSE, &timeout) == STATUS_TIMEOUT)
{
KIRQL oldIrql;
// Ok! We've got timeout..
// Completion function still can be called.
// First tell completion not to free our Irp
IoAcquireCancelSpinLock(&oldIrql);
if(m_Irp) m_DoNotFreeIrp = TRUE;
IoReleaseCancelSpinLock(oldIrql);
DEBUG_START();
TRACE("######## waitForCompletion() reports TIMEOUT after %d msec ############\n",getTimeout());
if(m_Irp)
{
irp->cancel(m_Irp); // okay in this context
// Wait for the cancel callback to be called
event->waitForSingleObject(CompletionEvent, Executive, KernelMode, FALSE, NULL);
TRACE("######## Current Irp cancelled!!! ############\n");
// Now we can safely free our Irp
if(m_DoNotFreeIrp)
{
if(m_Irp) irp->free(m_Irp);
m_Irp = NULL;
m_DoNotFreeIrp = FALSE;
}
// Report Irp timeout
setStatus(STATUS_IO_TIMEOUT);
setInformation(0);
}
}
return getStatus();
}
}
VOID CIoPacket::setStackDefaults()
{
setDefaultCompletionEvent();
copyStackToNext();
setDefaultCompletionFunction();
}
// Normally IoPacket will be created on the next stack location.
// The function allows to take current stack location.
// It is useful if we want to forward system IRP down the stack.
VOID CIoPacket::setCurrentStack()
{
if(m_Irp) Stack = *(irp->getCurrentStackLocation(m_Irp));
}
VOID CIoPacket::setTimeout(LONG TimeOut)
{
m_TimeOut = TimeOut;
};
ULONG CIoPacket::getTimeout()
{
return m_TimeOut;
};