/*++ Copyright (c) 1998 Gemplus Development Name: gprelcmd.c Description: This module holds the functions used for the PC Card I/O. Environment: Kernel Mode Revision History: 06/04/99: (Y. Nadeau + M. Veillette) - Code Review 24/03/99: V1.00.004 (Y. Nadeau) - Fix to GprllWait to work in DPC 06/05/98: V1.00.003 (P. Plouidy) - Power management for NT5 10/02/98: V1.00.002 (P. Plouidy) - Plug and Play for NT5 03/07/97: V1.00.001 (P. Plouidy) - Start of development. --*/ // // Include section: // - stdio.h: standards definitons. // - ntddk.h: DDK Windows NT general definitons. // - ntdef.h: Windows NT general definitons. // #include #include #include #include "gprelcmd.h" // // Function definition section: // #if DBG void GPR_Debug_Buffer( PUCHAR pBuffer, DWORD Lenght) { USHORT index; SmartcardDebug( DEBUG_TRACE, (" LEN=%d CMD=", Lenght) ); for(index=0;indexBaseIoAddress)) + (const USHORT) GPRRegister), &value ); return(value); } /*++ Routine Description : Call the G_MaskRegister function in the lower level --*/ void GprllMaskHandshakeRegister( const PREADER_EXTENSION pReaderExt, const UCHAR Mask, const UCHAR BitState ) { G_MaskRegister((const USHORT) (((const USHORT) (pReaderExt->BaseIoAddress)) + (const USHORT) REGISTER_HANDSHAKE), (const UCHAR) Mask, (const UCHAR) BitState); // YN // get hardware time to update register GprllWait(1); } NTSTATUS GprllKeWaitAckEvent( const PREADER_EXTENSION pReaderExt, const UCHAR Tx ) /*++ Routine Description: This function Wait the acknowledge of the GPR after a send command to IOPort. We made a smart verification of the timer depending of the Tag command. Arguments In: pReaderExt holds the pointer to the READER_EXTENSION structure. Tx holds the command type Return Value: NTStatus --*/ { UCHAR T; // Tag return LARGE_INTEGER lTimeout; NTSTATUS NTStatus = STATUS_SUCCESS; ULONG NbLoop = 0; ULONG NbSecondTotal; ULONG ElapsedSecond = 0; ULONG TimeInLoop =1; BOOLEAN Continue = TRUE; ULONG i = 0; ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); // Make a smart timer depending on type of command if( (Tx & 0xf0) == APDU_EXCHANGE_CMD) { NbSecondTotal = pReaderExt->CmdTimeOut; } else { NbSecondTotal = GPR_CMD_TIME; } NbLoop = (NbSecondTotal / TimeInLoop); while( (Continue) && (ElapsedSecond < NbSecondTotal) ) { ElapsedSecond += TimeInLoop; lTimeout.QuadPart = -((LONGLONG)TimeInLoop * 10000000); //Wait the acknowledge of the GPR NTStatus = KeWaitForSingleObject( &(pReaderExt->GPRAckEvent), Executive, KernelMode, TRUE, &lTimeout ); if(NTStatus == STATUS_TIMEOUT) { // Verify if the reader was been // remove during exchange lTimeout.QuadPart = 0; NTStatus = KeWaitForSingleObject( &(pReaderExt->ReaderRemoved), Executive, KernelMode, FALSE, &lTimeout ); SmartcardDebug( DEBUG_PROTOCOL, ( "%s!GprllKeWaitAckEvent: TIMEOUT KeWaitForSingleObject=%X(hex)\n", SC_DRIVER_NAME, NTStatus) ); if (NTStatus == STATUS_SUCCESS) { NTStatus = STATUS_DEVICE_REMOVED; Continue = FALSE; } // Read the T register // <== Test if GPR hasn't been removed STATUS_DEVICE_NOT_CONNECTED // Reading T out T = GprllReadRegister(pReaderExt,REGISTER_T); if ( T == 0xFF ) { NTStatus = STATUS_DEVICE_REMOVED; Continue = FALSE; } // Else is a Timeout } else { Continue = FALSE; NTStatus = STATUS_SUCCESS; } } return NTStatus; } NTSTATUS GprllTLVExchange( const PREADER_EXTENSION pReaderExt, const UCHAR Ti, const USHORT Li, const UCHAR *Vi, UCHAR *To, USHORT *Lo, UCHAR *Vo ) /*++ Routine Description : Exchange data with GPR with a TLV command. Arguments In: pReaderExt holds the pointer to the READER_EXTENSION structure. Ti holds the command type Li holds the command length Vi holds the command data Out: To holds the command response type Lo holds the command response length Vo holds the command response data Return Value NTStatus STATUS_SUCCESS is Ok else if an error condition is raised: STATUS_DEVICE_PROTOCOL_ERROR STATUS_INVALID_DEVICE_STATE STATUS_DEVICE_NOT_CONNECTED STATUS_UNRECOGNIZED_MEDIA and others received IFDstatus corresponding to NTSTATUS --*/ { // Local variables // - T is type of TLV protocol // - new_Ti is the Ti modified // - L is length in TLV protocol // - V is data filed in TLV protocol NTSTATUS NTStatus = STATUS_SUCCESS; UCHAR T; UCHAR new_Ti; USHORT L; UCHAR V[GPR_BUFFER_SIZE]; //Verification of Li if ( (USHORT) Li >= GPR_BUFFER_SIZE ) { return (STATUS_DEVICE_PROTOCOL_ERROR); } new_Ti = Ti; // // Write the TLV // by Write TLV if Li <= 28 or By ChainIn if Li > 28 // if (Li<=MAX_V_LEN) { GprllSendCmd(pReaderExt,new_Ti,Li,Vi); NTStatus = GprllKeWaitAckEvent( pReaderExt, Ti ); if (STATUS_SUCCESS != NTStatus) { // YN GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); return NTStatus; } // GPR Command to read I/O Window: // In the handshake register, set to 0 bit 2(IREQ) , and set to 1 bit 1 (INTR) } else { NTStatus = GprllSendChainUp( pReaderExt, new_Ti, Li, Vi ); if (STATUS_SUCCESS != NTStatus) { // YN GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); return(NTStatus); } } // Read the T register, need to know if new data to exchange T = pReaderExt->To; // Read Answer by Read TLV or Chain Out method if To = Ti + 6 if ( T == (new_Ti + 6) ) { NTStatus = GprllReadChainUp(pReaderExt,&T,&L,V); if (STATUS_SUCCESS != NTStatus ) { // YN GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); return(NTStatus); } } else { L = pReaderExt->Lo; ASSERT(pReaderExt->Vo !=0); memcpy(V,pReaderExt->Vo,L); GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); } // Verify if Response buffer len is large enough // to contain data received from the reader // if( L > *Lo ) { *To=T; *Lo=1; Vo[0]=14; return(STATUS_UNRECOGNIZED_MEDIA); } // Translate answer *To=T; *Lo=L; memcpy(Vo,V,(SHORT)L); return (GDDK_Translate(Vo[0], Ti)); } void GprllSendCmd( const PREADER_EXTENSION pReaderExt, const UCHAR Ti, const USHORT Li, const UCHAR *Vi ) /*++ Routine Description : Write TLV into I/O Window and Send Command to GPR to read I/O Window Arguments: pReaderExt holds the pointer to the READER_EXTENSION structure. Ti holds the command type Li holds the command length Vi holds the command data --*/ { // Local variables // - TLV is an intermediate buffer. UCHAR TLV[2 + MAX_V_LEN]; USHORT Li_max; //Write Ti, Li and Vi[] TLV[0] = Ti; TLV[1] = (UCHAR) Li; ASSERT(Vi != 0); Li_max = Li; if (Li_max > MAX_V_LEN) { Li_max = MAX_V_LEN; } memcpy(TLV+2,Vi,Li_max); G_WriteBuf((const USHORT) (((const USHORT) (pReaderExt->BaseIoAddress)) + (const USHORT) REGISTER_T), (const USHORT) (Li_max + 2), (UCHAR *) TLV ); // GPR Command to read I/O Window: // In the handshake register, set to 0 bit 2(IREQ) , and set to 1 bit 1 (INTR) GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_INTR,1); } void GprllReadResp( const PREADER_EXTENSION pReaderExt ) /*++ Routine Description : Read no chainning TLV into I/O Window and Send Command to GPR to read I/O Window Arguments In: pReaderExt holds the pointer to the READER_EXTENSION structure. Out: To holds the command response type Lo holds the command response length Vo holds the command response data --*/ { //Local variables // - TLV is an intermediate buffer. UCHAR TLV[2 + MAX_V_LEN]; TLV[0] = 0x0; // Read To, Lo and Vo[] G_ReadBuf((const USHORT) (((const USHORT) (pReaderExt->BaseIoAddress)) + (const USHORT) REGISTER_T), MAX_V_LEN + 2, TLV); pReaderExt->To = TLV[0]; // maximum number of character is set by the TLV buffer pReaderExt->Lo = TLV[1]; if (pReaderExt->Lo > MAX_V_LEN) { pReaderExt->Lo = MAX_V_LEN; } memcpy(pReaderExt->Vo,TLV+2,pReaderExt->Lo); // Acquit the Hand shake: // In the handshake register, set to 0 bit 2 (BUSY/IREQ) GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); } NTSTATUS GprllSendChainUp( const PREADER_EXTENSION pReaderExt, const UCHAR Ti, const USHORT Li, const UCHAR *Vi ) /*++ Routine Description : Send chainning TLV to GPR Arguments: In: pReaderExt holds the pointer to the READER_EXTENSION structure. Ti holds the command type Li holds the command length Vi holds the command data Out: Nothing --*/ { // Local variables // - Tc is type of TLV protocol ( TLV chaining method ) // - Lc is Length of TLV protocol ( chaining method ) // - Vc is 28 bytes max of data to send // - Length is an temporary var to store Li UCHAR Tc; UCHAR Response; UCHAR Lo; USHORT Lc; USHORT Length; UCHAR Vc[MAX_V_LEN]; NTSTATUS NTStatus = STATUS_SUCCESS; Length=Li; //Prepare Tc (Add 4 to Ti for chaining method) Tc=Ti+4; Vc[0] = 0x0; while ( Length > 0 ) { //Prepare Lc //If length TLV > 28 Length = 28 else it's last command L = Length if ( Length > MAX_V_LEN ) { Lc=MAX_V_LEN; } else { Lc=Length; Tc=Ti; } //Prepare Vc memcpy(Vc,Vi+Li-Length,Lc); //Write to I/O window // Dont need the answer - handled by the interrupt function. GprllSendCmd(pReaderExt,Tc,Lc,Vc); NTStatus = GprllKeWaitAckEvent( pReaderExt, Ti ); if(STATUS_SUCCESS != NTStatus) { return NTStatus; } //If an error test Response Response = GprllReadRegister(pReaderExt,REGISTER_V); if(0x00 != Response) { Lo = GprllReadRegister(pReaderExt,REGISTER_L); if (Lo == 0x01) { return (GDDK_Translate(Response, Ti)); } else { // This is not a exchange is a cmd to reader // we don't care about the reader status. return (NTStatus); } } Length=Length-Lc; } return(NTStatus); } NTSTATUS GprllReadChainUp( const PREADER_EXTENSION pReaderExt, UCHAR *To, USHORT *Lo, UCHAR *Vo ) /*++ Routine Description : Receive chainning TLV response from GPR Arguments In: pReaderExt holds the pointer to the READER_EXTENSION structure. Out: To holds the command response type Lo holds the command response length Vo holds the command response data --*/ { // Local variables // - Tc is type of TLV protocol ( TLV chaining method ) // - Lc is Length of TLV protocol ( chaining method ) // - Length is an temporary var to store Lo UCHAR Tc; USHORT Lc; SHORT Lenght; NTSTATUS NTStatus = STATUS_SUCCESS; // Reading T out Tc = GprllReadRegister(pReaderExt,REGISTER_T); *To=Tc-4; Lenght = 0; do { // Read TLV Tc = pReaderExt->To; Lc = pReaderExt->Lo; ASSERT(pReaderExt->Vo != 0); // The Vo buffer is limited by the caller local variable. if ( Lenght + (SHORT) pReaderExt->Lo > GPR_BUFFER_SIZE) { return (STATUS_BUFFER_TOO_SMALL); } memcpy(Vo+Lenght,pReaderExt->Vo,pReaderExt->Lo); GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); // Prepare Lo *Lo=(USHORT)Lenght+Lc; Lenght=Lenght+Lc; // GPR send the next Chainning TLV // In the handshake register, set to 0 bit 2(IREQ) and set to 1 bit 1 (INTR) if ((*To) != Tc ) { GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_IREQ,0); GprllMaskHandshakeRegister(pReaderExt,HANDSHAKE_INTR,1); NTStatus = GprllKeWaitAckEvent( pReaderExt, *To ); if(STATUS_SUCCESS != NTStatus) { return NTStatus; } } // (End do) if To=Tc -> Last Chainning TLV } while( (*To) != Tc ); return(NTStatus); } void GprllWait( const LONG lWaitingTime ) /*++ Routine Description : This function puts the driver in a waiting state for a timeout. If IRQL < DISPATCH_LEVEL, use normal fonction to process this delay. use KeStallExecutionProcessor, just when GprllWait is called in the context of DPC routine. Arguments pReaderExt: Pointer to the current ReaderExtension structure. lWaitingTime: Timeout value in ms --*/ { LARGE_INTEGER Delay; if( KeGetCurrentIrql() >= DISPATCH_LEVEL ) { ULONG Cnt = 20 * lWaitingTime; while( Cnt-- ) { // KeStallExecutionProcessor: counted in us KeStallExecutionProcessor( 50 ); } } else { Delay.QuadPart = (LONGLONG)-10 * 1000 * lWaitingTime; // KeDelayExecutionThread: counted in 100 ns KeDelayExecutionThread( KernelMode, FALSE, &Delay ); } return; }