Windows2003-3790/drivers/smartcrd/gpr400/gprelcmd.c
2020-09-30 16:53:55 +02:00

875 lines
20 KiB
C

/*++
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 <stdio.h>
#include <ntddk.h>
#include <ntdef.h>
#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;index<Lenght;index++)
{
SmartcardDebug(
DEBUG_TRACE,
("%02X,",
pBuffer[index])
);
}
SmartcardDebug(
DEBUG_TRACE,
("\n")
);
}
#endif
NTSTATUS GDDK_Translate(
const BYTE IFDStatus,
const UCHAR Tag
)
/*++
Routine Description:
Translate IFD status in NT status codes.
Arguments:
IFDStatus - is the value to translate.
IoctlType - is the current smart card ioctl.
Return Value:
the translated code status.
--*/
{
switch (IFDStatus)
{
case 0x00 : return STATUS_SUCCESS;
case 0x01 : return STATUS_NO_SUCH_DEVICE;
case 0x02 : return STATUS_NO_SUCH_DEVICE;
case 0x03 : return STATUS_INVALID_PARAMETER;
case 0x04 : return STATUS_IO_TIMEOUT;
case 0x05 : return STATUS_INVALID_PARAMETER;
case 0x09 : return STATUS_INVALID_PARAMETER;
case 0x0C : return STATUS_DEVICE_PROTOCOL_ERROR;
case 0x0D : return STATUS_SUCCESS;
case 0x10 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x11 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x12 : return STATUS_INVALID_PARAMETER;
case 0x13 : return STATUS_CONNECTION_ABORTED;
case 0x14 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x15 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x16 : return STATUS_INVALID_PARAMETER;
case 0x17 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x18 : return STATUS_UNRECOGNIZED_MEDIA;
case 0x19 : return STATUS_INVALID_PARAMETER;
case 0x1A : return STATUS_INVALID_PARAMETER;
case 0x1B : return STATUS_INVALID_PARAMETER;
case 0x1C : return STATUS_INVALID_PARAMETER;
case 0x1D : return STATUS_UNRECOGNIZED_MEDIA;
case 0x1E : return STATUS_INVALID_PARAMETER;
case 0x1F : return STATUS_INVALID_PARAMETER;
case 0x20 : return STATUS_INVALID_PARAMETER;
case 0x30 : return STATUS_IO_TIMEOUT;
case 0xA0 : return STATUS_SUCCESS;
case 0xA1 : return STATUS_UNRECOGNIZED_MEDIA;
case 0xA2 :
if (Tag == OPEN_SESSION_CMD)
{ return STATUS_UNRECOGNIZED_MEDIA;}
else
{ return STATUS_IO_TIMEOUT; }
case 0xA3 : return STATUS_PARITY_ERROR;
case 0xA4 : return STATUS_REQUEST_ABORTED;
case 0xA5 : return STATUS_REQUEST_ABORTED;
case 0xA6 : return STATUS_REQUEST_ABORTED;
case 0xA7 : return STATUS_UNRECOGNIZED_MEDIA;
case 0xCF : return STATUS_INVALID_PARAMETER;
case 0xE4 : return STATUS_UNRECOGNIZED_MEDIA;
case 0xE5 : return STATUS_SUCCESS;
case 0xE7 : return STATUS_SUCCESS;
case 0xF7 : return STATUS_NO_MEDIA;
case 0xF8 : return STATUS_UNRECOGNIZED_MEDIA;
case 0xFB : return STATUS_NO_MEDIA;
default : return STATUS_INVALID_PARAMETER;
}
}
/*++
Routine Description :
Read a byte at IO address
--*/
BOOLEAN G_ReadByte(const USHORT BIOAddr,UCHAR *Value)
{
*Value = READ_PORT_UCHAR((PUCHAR) BIOAddr);
return(TRUE);
}
/*++
Routine Description :
Write a byte at IO address
--*/
BOOLEAN G_WriteByte(const USHORT BIOAddr,UCHAR *Value)
{
WRITE_PORT_UCHAR((PUCHAR) BIOAddr,*Value);
return(TRUE);
}
/*++
Routine Description :
Read a buffer of "Len" bytes at IO address
--*/
BOOLEAN G_ReadBuf(const USHORT BIOAddr,const USHORT Len,UCHAR *Buffer)
{
USHORT i;
for(i=0;i<Len;i++)
{
*(Buffer+i) = READ_PORT_UCHAR((UCHAR *) UlongToPtr(BIOAddr+i));
}
#if DBG
// Excluse reader status reply
if(! ((Buffer[0] == 0xA2) && (Buffer[1]==4)) )
{
SmartcardDebug(
DEBUG_TRACE,
("%s!G_ReadBuf:",
SC_DRIVER_NAME)
);
GPR_Debug_Buffer(Buffer, Len );
}
#endif
return(TRUE);
}
/*++
Routine Description :
Write a buffer of "Len" bytes at IO address
--*/
BOOLEAN G_WriteBuf(const USHORT BIOAddr,const USHORT Len,UCHAR *Buffer)
{
USHORT i;
for(i=0;i<Len;i++)
{
WRITE_PORT_UCHAR((UCHAR *) UlongToPtr(BIOAddr + i),*(Buffer+i));
}
#if DBG
// Excluse reader status cmd
if(! ((Buffer[0] == 0xA0) && (Buffer[2] == 0x02)) )
{
SmartcardDebug(
DEBUG_TRACE,
("%s!G_WriteBuf:",
SC_DRIVER_NAME)
);
GPR_Debug_Buffer(Buffer,Len);
}
#endif
return(TRUE);
}
/*++
Routine Description :
Mask a register located at a specified address with a specified
byte
--*/
BOOLEAN G_MaskRegister(
const USHORT BIOAddr,
const UCHAR Mask,
const UCHAR BitState)
{
if(BitState == 0)
{
WRITE_PORT_UCHAR((PUCHAR)BIOAddr,(UCHAR) (READ_PORT_UCHAR((PUCHAR)BIOAddr) & ~Mask));
}
else
{
WRITE_PORT_UCHAR((PUCHAR)BIOAddr,(UCHAR) (READ_PORT_UCHAR((PUCHAR)BIOAddr) | Mask));
}
return(TRUE);
}
/*++
Routine Description :
Read a byte to an input address port
--*/
UCHAR GprllReadRegister(
const PREADER_EXTENSION pReaderExt,
const SHORT GPRRegister
)
{
//
// Locals variables:
// value holds the result of the read operation.
//
UCHAR value;
value = 0x0;
G_ReadByte((const USHORT)
(((const USHORT) (pReaderExt->BaseIoAddress)) + (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;
}