//------------------------------------------------------------------- // 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