Windows2003-3790/drivers/smartcrd/gempc430/usbreader.cpp
2020-09-30 16:53:55 +02:00

729 lines
20 KiB
C++

//-------------------------------------------------------------------
// This is abstract class for generic device
// Specific devices should use it as a parent device
// Author: Sergey Ivanov
// Log:
// 01.11.99 - implemented
//-------------------------------------------------------------------
#ifdef USBREADER_PROJECT
#pragma message("COMPILING USB READER...")
#ifndef __USB_READER__
#define __USB_READER__
#include "generic.h"
#include "usbreader.h"
#include "smartcard.h"
#include "usbdev.h"
#include "reader.h"
#include "gemcore.h"
#pragma PAGEDCODE
CUSBReader::CUSBReader()
{
ULONG DevID;
m_Status = STATUS_INSUFFICIENT_RESOURCES;
m_Type = USBREADER_DEVICE;
interface = NULL;
DevID = incrementDeviceNumber();
TRACE("########### Creating USBReader with index %d\n",DevID);
// Each reader creates own smartcard object...
scard_Initialized = FALSE;
smartCard = new (NonPagedPool) CSmartCard;
TRACE("**** Creating pooling thread... ****\n");
// We can not use default device function because it was already used by
// our Io thread (unless we extend it?)
// Lets define new thread function and xfer control to it...
PoolingThread = new (NonPagedPool) CThread((PCLIENT_THREAD_ROUTINE)PoolingThreadFunction,this,
getDevicePoolingInterval());
if(!ALLOCATED_OK(PoolingThread))
{
DISPOSE_OBJECT(PoolingThread);
TRACE("****** FAILED TO CREATE POOLING THREAD!\n");
}
else
{
// Thread which controls asynchronous driver communications
IoThread = new (NonPagedPool) CThread((PCLIENT_THREAD_ROUTINE)ThreadFunction,this,0);
if(!ALLOCATED_OK(IoThread))
{
DISPOSE_OBJECT(IoThread);
TRACE("****** FAILED TO CREATE IO THREAD!\n");
}
else
{
IoThread->start();
setDeviceState(WORKING);
m_Status = STATUS_SUCCESS;
}
}
TRACE("********* USB Reader %8.8lX was created with status %8.8lX...\n",this,m_Status);
}
#pragma PAGEDCODE
CUSBReader::~CUSBReader()
{
TRACE("Destroing USB reader pooling thread...\n");
if(PoolingThread) PoolingThread->dispose();
if(smartCard)
{
TRACE("Disconnecting from smartcard system...\n");
smartCard->smartCardDisconnect();
smartCard->dispose();
}
if(interface) interface->dispose();
if(IoThread) IoThread->stop();
cancelAllPendingRequests();
if(IoThread) IoThread->dispose();
remove();
TRACE("********* USB Reader %8.8lX was destroied...\n",this);
}
//Handle IRP_MJ_DEVICE_READ request
#pragma PAGEDCODE
NTSTATUS CUSBReader::open(IN PIRP Irp)
{
NTSTATUS status;
TRACE("\n------- USB READER OPEN DEVICE --------\n");
if(getDeviceState()!=WORKING)
{
TRACE(" READER IS NOT AT WORKING STATE... State %x\n",getDeviceState());
status = STATUS_DEVICE_NOT_CONNECTED;
return completeDeviceRequest(Irp,status,0);
}
if(IoThread)
{
status = makeRequestPending(Irp,m_DeviceObject,OPEN_REQUEST);
// Tell thread to start processing
if(NT_SUCCESS(status))
{
TRACE("CALL THREAD FUNCTION...\n");
IoThread->callThreadFunction();
}
else return completeDeviceRequest(Irp,status,0);
}
else
{
// IoThread is not ready... Process synchronously!
status = thread_open(Irp);
}
return status;
}
#pragma PAGEDCODE
NTSTATUS CUSBReader::thread_open(PIRP Irp)
{
TRACE("\n------- PROCESSING USB READER OPEN DEVICE --------\n");
TRACE("DEVICE NUMBER %x\n", this);
if (!NT_SUCCESS(acquireRemoveLock()))
{
TRACE("------- FAILED TO LOCK USB READER --------\n");
return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
}
// Check if device is already active and reports
// device busy...
if(isOpenned())
{
TRACE("------- USB READER ALREADY OPENNED --------\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_DEVICE_BUSY, 0);
}
if(!NT_SUCCESS(synchronizeDevicePowerState()))
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("******* FAILED TO SYNCHRONIZE DEVICE POWER...\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_INVALID_DEVICE_STATE, 0);
}
if(PoolingThread) PoolingThread->start();
markAsOpenned();
TRACE("\n------- USB READER OPENNED! --------\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_SUCCESS, 0);
};//Create
#pragma PAGEDCODE
VOID CUSBReader::onDeviceStart()
{
TRACE("============= PNP START INITIALIZATION ===============\n");
if(interface)
{
if(!interface->isInitialized())
{
interface->initialize();
}
}
reader_UpdateCardState();
setNotificationState(SCARD_SWALLOWED);
TRACE("============= PNP START INITIALIZATION FINISHED ===============\n");
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::close(PIRP Irp)
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("\n------- USB READER CLOSE DEVICE -------\n");
if(!isOpenned())
{
return completeDeviceRequest(Irp, STATUS_SUCCESS, 0);
}
// Check lock count to know if some pending calls exist...
// Finish all pending calls...
// Stop Card pooling...
if(PoolingThread) PoolingThread->stop();
// Power down card if inserted...
if(getCardState()== SCARD_SWALLOWED)
{
ULONG ResponseBufferLength = 0;
reader_WaitForIdleAndBlock();
reader_Power(SCARD_POWER_DOWN,NULL,&ResponseBufferLength, FALSE);
reader_set_Idle();
}
setNotificationState(getCardState());
completeCardTracking();
markAsClosed();
return completeDeviceRequest(Irp, STATUS_SUCCESS, 0);
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::deviceControl(IN PIRP Irp)
{
NTSTATUS status;
TRACE("\n----- IRP_MJ_DEVICE_CONTROL ------\n");
if(getDeviceState()!=WORKING)
{
TRACE(" READER IS NOT AT WORKING STATE... State %x\n",getDeviceState());
status = STATUS_DEVICE_NOT_CONNECTED;
return completeDeviceRequest(Irp,status,0);
}
status = thread_deviceControl(Irp);
return status;
}
// Redefine base class system interface function...
//Handle IRP_MJ_DEVICE_CONTROL request
#pragma PAGEDCODE
NTSTATUS CUSBReader::thread_deviceControl(IN PIRP Irp)
{ // RequestControl
NTSTATUS status = STATUS_SUCCESS;
ULONG info = 0;
if (!NT_SUCCESS(acquireRemoveLock()))
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("******* DIOC: FAILED TO AQUIRE REMOVE LOCK...\n");
return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
}
TRACE("----- thread_deviceControl() ------\n");
if(isSurprizeRemoved())
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("******* DIOC: FAILED! DEVICE WAS SURPRIZE REMOVED...\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
}
// This was fix for "device SET_POWER without system SET_POWER"
// It was seen first on ia64 machine
// If device was powered off tell system to restore power on this device,
// wait till device will be at proper state...
/*if(!NT_SUCCESS(synchronizeDevicePowerState()))
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("******* FAILED TO SYNCHRONIZE DEVICE POWER...\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_INVALID_DEVICE_STATE, 0);
}
*/
// If we've got request but device was not enable yet -> wait for the device!
// (One of the reasons to disable device - power state change)
if(!synchronizeDeviceExecution())
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("******* DIOC: FAILED TO SYNCHRONIZE EXECUTION ...\n");
releaseRemoveLock();
return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
}
// SmartCard system will complete the request,
// So... We do not need to do it here.
status = SmartcardDeviceControl(getCardExtention(),Irp);
TRACE("===== USB reader: SmartcardDeviceControl() returns %8.8lX\n", status);
releaseRemoveLock();
if(!NT_SUCCESS(status))
{// In case of errors force to update card status...
if(PoolingThread) PoolingThread->callThreadFunction();
}
return status;
}
#pragma PAGEDCODE
NTSTATUS CUSBReader::cleanup(PIRP Irp)
{
DEBUG_START();//Force to debug even if thread disable it...
TRACE("\n----- IRP_MJ_CLEANUP ------\n");
if(PoolingThread) PoolingThread->stop();
cancelAllPendingRequests();
setNotificationState(getCardState());
completeCardTracking();
reader_set_Idle();
TRACE("----- IRP_MJ_CLEANUP FINISHED... ------\n");
return completeDeviceRequest(Irp, STATUS_SUCCESS, 0);
}
#pragma LOCKEDCODE
// This is callback function for the attached threads
VOID CUSBReader::PoolingThreadFunction(CUSBReader* device)
{
if(device) device->PoolingThreadRoutine();
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::PoolingThreadRoutine()
{
NTSTATUS status;
ULONG State;
LONG TimeOut;
if(!NT_SUCCESS(status = reader_WaitForIdle())) return status;
reader_set_busy();
TimeOut = getCommandTimeout();
setCommandTimeout(10000);//Change get status command timeout!
DEBUG_STOP();
State = reader_UpdateCardState();
TRACE("======>> Card state %x\n",CardState);
DEBUG_START();
setCommandTimeout(TimeOut);
reader_set_Idle();
return STATUS_SUCCESS;
};
#pragma LOCKEDCODE
VOID CUSBReader::reader_set_busy()
{
setBusy();
};
#pragma LOCKEDCODE
VOID CUSBReader::reader_set_Idle()
{
setIdle();
};
#pragma LOCKEDCODE
NTSTATUS CUSBReader::reader_WaitForIdle()
{
return waitForIdle();
};
#pragma LOCKEDCODE
NTSTATUS CUSBReader::reader_WaitForIdleAndBlock()
{
return waitForIdleAndBlock();
};
#ifdef DEBUG
/*
// Overwrite device functions...
NTSTATUS CUSBReader::read(IN PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG info = 0;
TRACE("USB reader: IRP_MJ_DEVICE_READ\n");
if (!NT_SUCCESS(acquireRemoveLock())) return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
status = reader_Read(Irp);
releaseRemoveLock();
status = completeDeviceRequest(Irp, status, info);
return status;
}
NTSTATUS CUSBReader::write(IN PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG info = 0;
TRACE("USB reader: IRP_MJ_DEVICE_WRITE\n");
if (!NT_SUCCESS(acquireRemoveLock())) return completeDeviceRequest(Irp, STATUS_DELETE_PENDING, 0);
status = reader_Write(Irp);
releaseRemoveLock();
status = completeDeviceRequest(Irp, status, info);
return status;
}
*/
#endif
#pragma PAGEDCODE
BOOL CUSBReader::createInterface(LONG interfaceType, LONG protocolType,CUSBReader* device)
{
interface = kernel->createReaderInterface(interfaceType,protocolType,device);
if(interface) return TRUE;
else return FALSE;
};
#pragma PAGEDCODE
VOID CUSBReader::initializeSmartCardSystem()
{
if(smartCard)
{
CardState = SCARD_UNKNOWN;
StateToNotify = SCARD_UNKNOWN;
smartCard->smartCardConnect(this);
}
};
#pragma PAGEDCODE
VOID CUSBReader::onSystemPowerDown()
{
// Stop pooling thread
TRACE("Stop polling thread going to PowerDeviceD3 (OFF)\n");
disableDevice();
if(PoolingThread) {if(PoolingThread->isThreadActive()) setThreadRestart();};
if(PoolingThread) PoolingThread->stop();
return;
}
#pragma PAGEDCODE
VOID CUSBReader::onSystemPowerUp()
{
// Stop pooling thread
TRACE("Restore reader state going to PowerDeviceD0 (ON)\n");
if(interface)
{
if(interface->isInitialized())
{
// Restore reader mode after power down
NTSTATUS status = interface->setReaderMode(READER_MODE_NATIVE);
if(!NT_SUCCESS(status))
{
TRACE("Failed to set Gemcore reader mode %x\n",READER_MODE_NATIVE);
}
}
}
if(getCardState() >= SCARD_SWALLOWED) setCardState(SCARD_ABSENT);
completeCardTracking();
if(isRequiredThreadRestart())
{
TRACE("Starting pooling thread going to PowerDeviceD0 (ON)\n");
if(PoolingThread) PoolingThread->start();
}
enableDevice();
return;
}
#pragma PAGEDCODE
BOOLEAN CUSBReader::setDevicePowerState(IN DEVICE_POWER_STATE DeviceState)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
BOOLEAN fRes = FALSE;
DEBUG_START();
switch (DeviceState)
{
case PowerDeviceD3:
// Device will be going OFF,
// TODO: add any needed device-dependent code to save state here.
// ( We have nothing to do in this sample )
TRACE("Set Device Power State to PowerDeviceD3 (OFF)\n");
setCurrentDevicePowerState(DeviceState);
break;
case PowerDeviceD1:
case PowerDeviceD2:
// power states D1,D2 translate to USB suspend
#ifdef DEBUG
TRACE("Set Device Power State to %s\n",Powerdevstate[DeviceState]);
#endif
setCurrentDevicePowerState(DeviceState);
break;
case PowerDeviceD0:
TRACE("Set Device Power State to PowerDeviceD0(ON)\n");
// We'll need to finish the rest in the completion routine;
// signal caller we're going to D0 and will need to set a completion routine
fRes = TRUE;
// Caller will pass on to PDO ( Physical Device object )
break;
default:
TRACE(" Bogus DeviceState = %x\n", DeviceState);
}
return fRes;
}
#pragma PAGEDCODE
ULONG CUSBReader::reader_UpdateCardState()
{
if(interface)
{
CardState = interface->getReaderState();
completeCardTracking();
}
else CardState = 0;
return CardState;
};
#pragma LOCKEDCODE
VOID CUSBReader::completeCardTracking()
{
if(smartCard)
{
smartCard->completeCardTracking();
}
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_getVersion(PUCHAR pVersion, PULONG pLength)
{
if(interface) return interface->getReaderVersion(pVersion,pLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_setMode(ULONG mode)
{
if(interface) return interface->setReaderMode(mode);
else return STATUS_INVALID_DEVICE_STATE;
};
#ifdef DEBUG
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Read(IN PIRP Irp)
{
CIoPacket* request = new (NonPagedPool) CIoPacket(Irp);
if(!ALLOCATED_OK(request) || !ALLOCATED_OK(interface))
{
DISPOSE_OBJECT(request);
return completeDeviceRequest(Irp,STATUS_INSUFFICIENT_RESOURCES,0);
}
NTSTATUS status = interface->read(request);
DISPOSE_OBJECT(request);
return status;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Write(IN PIRP Irp)
{
CIoPacket* request = new (NonPagedPool) CIoPacket(Irp);
if(!ALLOCATED_OK(request) || !ALLOCATED_OK(interface))
{
DISPOSE_OBJECT(request);
return completeDeviceRequest(Irp,STATUS_INSUFFICIENT_RESOURCES,0);
}
NTSTATUS status = interface->write(request);
DISPOSE_OBJECT(request);
return status;
};
#endif
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Read(BYTE * pRequest,ULONG RequestLength,BYTE * pReply,ULONG* pReplyLength)
{
if(interface) return interface->readAndWait(pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Write(BYTE* pRequest,ULONG RequestLength,BYTE * pReply,ULONG* pReplyLength)
{
if(interface) return interface->writeAndWait(pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Ioctl(ULONG ControlCode,BYTE* pRequest,ULONG RequestLength,BYTE* pReply,ULONG* pReplyLength)
{
if(interface) return interface->ioctl(ControlCode,pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_SwitchSpeed(ULONG ControlCode,BYTE* pRequest,ULONG RequestLength,BYTE* pReply,ULONG* pReplyLength)
{
if(interface) return interface->SwitchSpeed(ControlCode,pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_VendorAttribute(ULONG ControlCode,BYTE* pRequest,ULONG RequestLength,BYTE* pReply,ULONG* pReplyLength)
{
if(interface) return interface->VendorAttribute(ControlCode,pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_Power(ULONG ControlCode,BYTE* pReply,ULONG* pReplyLength, BOOLEAN Specific)
{
if(interface) return interface->power(ControlCode,pReply,pReplyLength, Specific);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_SetProtocol(ULONG ProtocolRequested, UCHAR ProtocolNegociation)
{
NTSTATUS status;
if(interface)
{
ReaderConfig config = interface->getConfiguration();
// Update all required configuration fields to set specific protocol
switch(ProtocolNegociation)
{
case PROTOCOL_MODE_DEFAULT:
config.PTSMode = PTS_MODE_DISABLED;
break;
case PROTOCOL_MODE_MANUALLY:
default:
config.PTSMode = PTS_MODE_MANUALLY;
break;
}
config.PTS1 = smartCardExtention.CardCapabilities.PtsData.Fl << 4 |
smartCardExtention.CardCapabilities.PtsData.Dl;
interface->setConfiguration(config);
status = interface->setProtocol(ProtocolRequested);
return status;
}
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::setTransparentConfig(PSCARD_CARD_CAPABILITIES cardCapabilities, BYTE NewWtx)
{
if(interface) return interface->setTransparentConfig(cardCapabilities,NewWtx);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_translate_request(BYTE * pRequest,ULONG RequestLength,BYTE * pReply,ULONG* pReplyLength, PSCARD_CARD_CAPABILITIES cardCapabilities, BYTE NewWtx)
{
if(interface) return interface->translate_request(pRequest,RequestLength,pReply,pReplyLength, cardCapabilities, NewWtx);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::reader_translate_response(BYTE * pRequest,ULONG RequestLength,BYTE * pReply,ULONG* pReplyLength)
{
if(interface) return interface->translate_response(pRequest,RequestLength,pReply,pReplyLength);
else return STATUS_INVALID_DEVICE_STATE;
};
#pragma PAGEDCODE
NTSTATUS CUSBReader::PnP_HandleSurprizeRemoval(IN PIRP Irp)
{ // It is PnP internal function.
// So, device will be locked at PnP entry and
// we do not need to do it here.
TRACE("******** USB READER SURPRIZE REMOVAL ********\n");
// Just stop thread and remove all pending IOs
if(PoolingThread) PoolingThread->stop();
setSurprizeRemoved();
cancelAllPendingRequests();
return PnP_Default(Irp);
};
VOID CUSBReader::onDeviceStop()
{
TRACE("******** ON USB READER STOP ********\n");
// Just stop thread and remove all pending IOs
if(PoolingThread) PoolingThread->stop();
//if(IoThread) IoThread->stop();
return;
};
// Reader startIoRequest function
// It will dispatch all pending Io requests
NTSTATUS CUSBReader::startIoRequest(CPendingIRP* IrpReq)
{
NTSTATUS status;
TRACE(" CUSBReader::::startIoRequest() was called...\n");
// Our child's functions run under protection of child BUSY/IDLE breaks.
// So, we do not need to check idle state here...
if(getDeviceState()!=WORKING)
{
TRACE(" READER IS NOT AT WORKING STATE... State %x\n",getDeviceState());
TRACE(" <<<<<< READER IO REQUEST FINISHED WITH STATUS %8.8lX>>>>>>\n",STATUS_DEVICE_NOT_CONNECTED);
NTSTATUS status = completeDeviceRequest(IrpReq->Irp, STATUS_DEVICE_NOT_CONNECTED, 0);
IrpReq->dispose();
return status;
}
// Our reader will support asynchronous communications only for these functions...
switch(IrpReq->Type)
{
case OPEN_REQUEST:
TRACE("OPEN_REQUEST RECIEVED FROM THREAD...\n");
status = thread_open(IrpReq->Irp);
break;
case IOCTL_REQUEST:
TRACE("IOCTL_REQUEST RECIEVED FROM THREAD...\n");
status = thread_deviceControl(IrpReq->Irp);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
}
IrpReq->dispose();
TRACE(" <<<<<< READER IO REQUEST FINISHED WITH STATUS %8.8lX>>>>>>\n",status);
return status;
};
NTSTATUS CUSBReader::ThreadRoutine()
{
// 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();
return STATUS_SUCCESS;
};
#endif
#endif //USBREADER_PROJECT