#include "Slot.h"
#include "Communicator.h"
#include "TokenTypeList.h"
#include "Session.h"
#include "SessionList.h"
#include "Pkcs11Exceptions.h"
#include "Mech_RSA_PKCS.h"
#include "Mech_SHA1_RSA_PKCS.h"
#include "Mech_RSA_PKCS_KEY_PAIR_GEN.h"
#include "ExclusionMutua.h"
#include "SlotTransaction.h"

extern CExclusionMutua g_ExclMutua;
extern SCARDCONTEXT g_hContext;
extern CTokenTypeList & g_rTokenTypeList;
extern CSessionList & gSessionList;

bool transactionBegun = false;
int numTransaction = 0;
/********************************************************************************************
	Constructor.

	Parámetros:
		- string sName: Nombre del lector.
********************************************************************************************/
CSlot::CSlot(string sName, CK_SLOT_ID slotId )
{
	DBG_TRACE(( "Constructor CSlot(%s,%d) [0x%08x]\n", sName.c_str(), slotId, this ));
	m_sName				= sName;
	m_hCard				= NULL;
	m_numPublic			= 0;
	//m_pTokenInfoCard = NULL;
	m_pTokenType		= NULL;
	m_slotId			= slotId;
	m_bTokenP15Error	= false;
	m_DniType			= 0;
	#if defined WIN32
        m_hEvento			= 0;
    #endif

	memset(&m_readerState, 0, sizeof(m_readerState));
	m_readerState.dwCurrentState	= SCARD_STATE_UNAWARE;
	m_readerState.szReader			= m_sName.c_str();

	// Leemos el estado inicial del slot
	long rv = SCardGetStatusChange(g_hContext, 0, &m_readerState, 1);
	if(rv!= SCARD_S_SUCCESS)
	{
		DBG_TRACE(( "SCardGetStatusChange error (0x%08x) \n", rv ));
	}

	// Limpiamos el estado inicial de ese lector
	m_readerState.dwCurrentState = (m_readerState.dwEventState & ~(SCARD_STATE_CHANGED));
}

#ifdef _DEBUG
CSlot::CSlot(const CSlot & slot) :
		m_sName(slot.m_sName),
		m_tokenInfoCard(slot.m_tokenInfoCard),
		m_hCard(slot.m_hCard),
		m_pTokenType(slot.m_pTokenType)
{
	DBG_TRACE(( "Constructor copy CSlot(0x%08x) [0x%08x]\n", &slot, this ));
};
#endif


/********************************************************************************************
	Destructor
********************************************************************************************/
CSlot::~CSlot(void)
{
	DBG_TRACE(( "Destructor ~CSlot() [0x%08x]\n", this ));

	Disconnect(true);
}


/********************************************************************************************
	Método que obtiene el nombre del lector.

	Devuelve: string con el nombre del lector.
********************************************************************************************/
const string & CSlot::GetName() const
{
	return m_sName;
}


/********************************************************************************************
	Método que obtiene el identificador del lector.

	Devuelve: CK_SLOT_ID con el identificador del lector.
********************************************************************************************/
CK_SLOT_ID CSlot::GetSlotId() const
{
	return m_slotId;
}


/********************************************************************************************
	Método que obtiene el handle de la tarjeta del lector.

	Devuelve: SCARDHANDLE con el handle de la tarjeta del lector.
********************************************************************************************/
SCARDHANDLE CSlot::GetCardHandle() const
{
	if( !m_hCard )
		throw Pkcs11Exception( CKR_SLOT_ID_INVALID );

	return m_hCard;
}

/********************************************************************************************
	Método que obtiene el estado de un slot.

	Devuelve: SCARD_READERSTATE con el estado.
********************************************************************************************/
SCARD_READERSTATE CSlot::GetState()
{
	return m_readerState;
}

/********************************************************************************************
	Método que actualiza el estado de un slot.

	Parámetros: SCARD_READERSTATE con el nuevo estado.
********************************************************************************************/
void CSlot::SetState(SCARD_READERSTATE & state)
{
	// Copiamos la estructura al completo
	m_readerState = state;
}


/********************************************************************************************
	Método que obtiene el TokenInfo de la tarjeta del lector.

	Devuelve: TokenInfo con la información de la tarjeta del lector.
********************************************************************************************/
TokenInfo * CSlot::GetTokenInfo(CK_TOKEN_INFO_PTR pInfo)
{
	if( !m_tokenInfoCard.m_loaded && SupportedTokenPresent() )
	{
		CSlotTransaction	transaction(*this);

		//
		// Leemos el TokenInfo, utilizando el CComm_Plain
		//
		CComm_Plain     comm_Plain(*this);
		
		ReadTokenInfo(comm_Plain);
	}

	// Si ya hemos leído el TokenInfo de la tarjeta insertada en este Slot, devolvemos el parseo
	if( m_tokenInfoCard.m_loaded )
	{
		// Si ya hemos intentado parsear el P15 y nos dio error, devolveremos un CKR_TOKEN_NOT_RECOGNIZED
		// para que Firefox no entre en un bucle de lectura y apertura de sesión (sin éxito)
		if( m_bTokenP15Error )
			throw Pkcs11Exception(CKR_TOKEN_NOT_RECOGNIZED);

		// Si la decodificación del TokenInfo ha ido bien, rellenamos los campos de la estructura pInfo en caso necesario
		if(pInfo!=NULL)
		{
			// Rellenamos la estructura pInfo con los datos leídos del FE TokenInfo
			if(!m_tokenInfoCard.LoadTokenInfo(pInfo))
				throw Pkcs11Exception(CKR_DEVICE_ERROR);

			pInfo->hardwareVersion.major = m_readerState.rgbAtr[HWVERSION_MAJOR];
			pInfo->hardwareVersion.minor = m_readerState.rgbAtr[HWVERSION_MINOR];
			pInfo->firmwareVersion.major = m_readerState.rgbAtr[FMVERSION_MAJOR];
			pInfo->firmwareVersion.minor = m_readerState.rgbAtr[FMVERSION_MINOR];

		}

		UpdatePINFlags(pInfo);
		return m_tokenInfoCard.m_asn1TokenInfo;
	}
	else if(CheckToken().empty())
		throw Pkcs11Exception(CKR_TOKEN_NOT_PRESENT);
	else
		throw Pkcs11Exception(CKR_TOKEN_NOT_RECOGNIZED);
}

void CSlot::GetMechanismList( CK_MECHANISM_TYPE_PTR pMechanismList,
							  CK_ULONG_PTR          pulCount)
{
	// Nos aseguramos de que hay un token en el slot
	if(!m_pTokenType)
		throw Pkcs11Exception(CKR_TOKEN_NOT_PRESENT);

	if(pMechanismList)
	{
		if(*pulCount < m_pTokenType->m_mechanismList.size())
		{
			*pulCount = (CK_ULONG)m_pTokenType->m_mechanismList.size();
			throw Pkcs11Exception(CKR_BUFFER_TOO_SMALL);
		}

		for(ulong idx=0; idx<m_pTokenType->m_mechanismList.size(); idx++)
			pMechanismList[idx] = m_pTokenType->m_mechanismList[idx];
	}

	// Devolvemos el número de mecanismos soportados por el token
	*pulCount = (CK_ULONG)m_pTokenType->m_mechanismList.size();
}

bool CSlot::IsValidMechanism ( CK_MECHANISM_TYPE mechanism )
{
	// Nos aseguramos de que hay un token en el slot
	if(!m_pTokenType)
		return false;

	for(ulong idx=0; idx<m_pTokenType->m_mechanismList.size(); idx++)
		if(mechanism == m_pTokenType->m_mechanismList[idx])
			return true;

	return false;
}

CMech* CSlot::newMechanism( CK_MECHANISM_TYPE mechanism )
{
	CMech* pMech = NULL;

	if( !IsValidMechanism(mechanism))
		return NULL;

	switch( mechanism )
	{
		case CKM_RSA_PKCS:
			pMech = new(CMech_RSA_PKCS);
			break;
		case CKM_SHA1_RSA_PKCS:
			pMech = new(CMech_SHA1_RSA_PKCS);
			break;
		case CKM_RSA_PKCS_KEY_PAIR_GEN:
			pMech = new(CMech_RSA_PKCS_KEY_PAIR_GEN);
			break;
		default:
			pMech = NULL;
	}

	return pMech;
}

void CSlot::ReadTokenInfo( CCommunicator & comm )
{
	m_bTokenP15Error = false;

	m_tokenInfoCard.LoadP15File( comm );
}


void CSlot::UpdatePINFlags(CK_TOKEN_INFO_PTR pInfo)
{
	CSession *	pP11Session		= NULL;
	ushort		ushPINCounter	= 0;

	try
	{
		// Sólo actualizamos el estado del PIN si tenemos sesión
		pP11Session = gSessionList.FindSessionInSlot(GetSlotId());

		try
		{
			// Obtenemos el número de intentos de PIN restantes
			ushPINCounter = pP11Session->m_pComm->GetPINCounter();
		}
		catch (Pkcs11Exception & e)
		{
			if( e.getErrId() == CKR_SESSION_HANDLE_INVALID )
				gSessionList.DeleteAllSessionsAndHandles(GetSlotId());
			else throw e;
		}

		// Reseteamos los flags de estado del PIN y los actualizamos
		pInfo->flags &= ~(CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED);

		switch(ushPINCounter)
		{
		case 0:	// PIN bloqueado
			pInfo->flags |= CKF_USER_PIN_LOCKED | CKF_USER_PIN_COUNT_LOW;
			break;
		case 1: // Último intento
			pInfo->flags |= CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_COUNT_LOW;
			break;
		case 2: // Se ha introducido mal una vez
			pInfo->flags |= CKF_USER_PIN_COUNT_LOW;
		}

	}
	catch (Pkcs11Exception & e)
	{
		// El slot no tiene sesión
		if ( e.getErrId() != CKR_SLOT_ID_INVALID )
			throw e;
	}

}


/********************************************************************************************
	Método que establece una conexión con la tarjeta del lector.
	Guarda el handle de la tarjeta en m_hCard.
********************************************************************************************/
void CSlot::Connect( DWORD dwShareMode /* = SCARD_SHARE_SHARED */ )
{
	LONG	lReturn;
	DWORD	protocol;

	// Comprobamos que haya un contexto establecido
	if (NULL == g_hContext)
	{
		throw Pkcs11Exception(CKR_CRYPTOKI_NOT_INITIALIZED);
	}

	if( m_hCard )
	{
        
        
        #if defined _YOSEMITE_
		
		SCardDisconnect( m_hCard, SCARD_RESET_CARD );

		lReturn = SCardConnect
					(
						g_hContext,
						m_sName.c_str(),
						dwShareMode,
						SCARD_PROTOCOL_T0,
						&m_hCard,
						&protocol
					);
		#else
		lReturn = SCardReconnect
                (
                    m_hCard,
                    dwShareMode,
                    SCARD_PROTOCOL_T0,
                    SCARD_RESET_CARD,
                    &protocol
                );
        #endif  
	}
	else
	{
		// Establecemos la conexión, almacenando en m_hCard el handle de la tarjeta
		lReturn = SCardConnect
					(
						g_hContext,
						m_sName.c_str(),
						dwShareMode,
						SCARD_PROTOCOL_T0,
						&m_hCard,
						&protocol
					);
	}

	if( lReturn != SCARD_S_SUCCESS)
	{
		m_hCard = NULL;
		throw Pkcs11Exception(CSlot::PCSCErr2P11Err(lReturn));
	}

	if( g_ExclMutua.m_pNombre_Lector.size() == 0 )
	{
		g_ExclMutua.m_pNombre_Lector.resize(m_sName.size());
		memcpy(g_ExclMutua.m_pNombre_Lector, &m_sName[0], m_sName.size());
	}
}

void CSlot::Disconnect( bool bCritical /*=NULL*/ )
{
	if( m_hCard )
		SCardDisconnect( m_hCard, SCARD_LEAVE_CARD );

	m_hCard	= NULL;

	if( bCritical )
	{
		m_pTokenType = NULL;
		m_tokenInfoCard.Erase();

        #if defined WIN32
            if(m_hEvento)
            {
                CloseHandle(m_hEvento);
                m_hEvento = NULL;
            }
        #endif

		// Destruiremos el mutex del lector, ya que no tiene tarjeta dentro.
		if(g_ExclMutua.m_pNombre_Lector.size() != 0)
		{
			g_ExclMutua.m_pNombre_Lector.resize(0);
			g_ExclMutua.DestroyMutex(true);
		}
	}
}

/********************************************************************************************
	Método que hace una comprobación de la tarjeta del lector.

	Devuelve:
		- string vacío si el lector no tiene tarjeta.
		- string con el nombre del tipo de tarjeta si el lector tiene una tarjeta de las
		  soportadas por el módulo.
		- UNSUPPORTED_TOKEN si el lector tiene una tarjeta pero no es una de las soportadas
		  por el módulo
********************************************************************************************/
string CSlot::CheckToken()
{
	BYTE cbAtr[36];
	DWORD dwAtrLen=sizeof(cbAtr);
	LONG lReturn;
	string sToken = "";

	DWORD pdwState;
	DWORD pdwProtocol;

	try
	{
		bool	connected = m_hCard ? true : false;

		// Nos aseguramos de que haya establecida una conexión con la tarjeta del lector
		if( !connected )
			Connect();

		// Obtenemos el ATR de la tarjeta
#if defined _MAVERICKS_
		lReturn = SCardGetAttrib(
			m_hCard,
			SCARD_ATTR_ATR_STRING,
			cbAtr,
			&dwAtrLen);
#else
		lReturn = SCardStatus(
			m_hCard,
			NULL,
			NULL,
			&pdwState,
			&pdwProtocol,
			cbAtr,
			&dwAtrLen);
#endif

		//
		// Liberamos la conexión
		//
		if( !connected )
			Disconnect();

		//
		// Analizamos el resultado
		//
		if( (m_hCard) && (lReturn == SCARD_W_REMOVED_CARD) )
		{
			Disconnect(true);
			return CheckToken();
		}
		if( lReturn == SCARD_E_NO_MEMORY )
			throw Pkcs11Exception( CKR_HOST_MEMORY );
		if( lReturn != SCARD_S_SUCCESS )
			return sToken;

		// Ningún error
		// Hay token en el slot, y tenemos su ATR.
		// Aún no sabemos si es uno de los soportados
		sToken = UNSUPPORTED_TOKEN;

		// Para cada una de las tarjetas soportadas
		for (unsigned int i = 0;
			(sToken.compare(UNSUPPORTED_TOKEN) == 0) && (i < g_rTokenTypeList.size());
			i++)
		{
			CTokenType * pTokenType = g_rTokenTypeList.at(i);

			// Si se trata de una tarjeta FNMT (no DNIe)
			// no la gestionará el driver de certs. privados.
			if( (pTokenType->GetName().compare("DNIe") != 0) &&
				(g_rTokenTypeList.m_PrivateOnly) )
				continue;

			if( pTokenType->IsValidAtr( cbAtr, dwAtrLen ) )
			{
			    #if defined WIN32
                    // Si se trata de un DNIe
                    if( (pTokenType->GetName().compare("DNIe") == 0) &&
                        (g_rTokenTypeList.m_PrivateOnly || g_rTokenTypeList.m_PublicOnly) )
                    {
                        DBG_TRACE(( "[PRIV/PUB] ENCONTRADO DNIe en %s\n", m_sName.c_str() ));
                        DBG_TRACE(( "[PRIV/PUB] Valor m_PrivateOnly: %d, m_PublicOnly: %d.\n", g_rTokenTypeList.m_PrivateOnly, g_rTokenTypeList.m_PublicOnly ));

                        // Nos aseguramos de que el driver de privados no haya capturado el DNIe
                        HANDLE hEvento = NULL;
                        unsigned char ucNombreEvento[256];
                        memset(ucNombreEvento, 0x00, 256);
                        // Driver público --> busca bloqueo del driver privado
                        if(g_rTokenTypeList.m_PublicOnly)
                            memcpy(ucNombreEvento, "LOCKED_PRI_", 11);

                        // Driver privado --> busca bloqueo del driver público
                        if(g_rTokenTypeList.m_PrivateOnly)
                            memcpy(ucNombreEvento, "LOCKED_PUB_", 11);

                        memcpy(ucNombreEvento+11, m_sName.c_str(), m_sName.length());

                        DBG_TRACE(( "[PRIV/PUB] Buscamos el evento %s\n", ucNombreEvento ));

                        // Comprobamos si existe un evento que indique que el lector ya está capturado
                        hEvento = OpenEvent(EVENT_ALL_ACCESS, false, (char*)ucNombreEvento);
                        if(hEvento)
                        {
                            DBG_TRACE(( "[PRIV/PUB] Evento %s: ENCONTRADO.\n", ucNombreEvento ));
                            CloseHandle(hEvento);
                            hEvento = NULL;
                            break;
                        }

                        DBG_TRACE(( "[PRIV/PUB] Evento %s: NO ENCONTRADO.\n", ucNombreEvento ));

                        if( !connected )
                            m_numPublic = GetNumberPublicFE(CComm_Plain(*this));

                        DBG_TRACE(( "[PRIV/PUB] Valores driver actual:\n \tm_PrivateOnly: %d,\n \tm_PublicOnly: %d,\n \tm_numPublic: %i\n", g_rTokenTypeList.m_PrivateOnly, g_rTokenTypeList.m_PublicOnly, m_numPublic ));

                        if( g_rTokenTypeList.m_PrivateOnly && m_numPublic>1)
                            continue;

                        if( g_rTokenTypeList.m_PublicOnly && m_numPublic<=1)
                            continue;

                        // Incluimos el CreateEvent de DNI
                        if(m_hEvento!=NULL)
                            CloseHandle(m_hEvento);

                        m_hEvento = NULL;

                        if(g_rTokenTypeList.m_PrivateOnly)
                            memcpy(ucNombreEvento, "LOCKED_PRI_", 11);

                        if(g_rTokenTypeList.m_PublicOnly)
                            memcpy(ucNombreEvento, "LOCKED_PUB_", 11);

                        m_hEvento = CreateEvent(NULL, false, false, (char*)ucNombreEvento);

                        DBG_TRACE(( "[PRIV/PUB] Creamos el evento %s\n", ucNombreEvento ));
                    }
                #endif

				// Si la tarjeta es un DNIe, debemos indicar si es 3.0 o anterior.
				if(pTokenType->GetName().compare("DNIe") == 0)
					m_DniType = (cbAtr[dwAtrLen - 5] >= 0x04 ? 0x02 : 0x01);

				m_pTokenType = pTokenType;
				sToken = pTokenType->GetName();
				break;
			}
		}

		// Liberamos la memoria reservada para el ATR de la tarjeta
		//lReturn = SCardFreeMemory(g_hContext, cbAtr);

		//if (SCARD_S_SUCCESS != lReturn)
		//{
		//	throw Pkcs11Exception(CKR_HOST_MEMORY);
		//}
	}
	catch (Pkcs11Exception & e)
	{
		switch( e.getErrId() )
		{
		case CKR_HOST_MEMORY:
		case CKR_CRYPTOKI_NOT_INITIALIZED:
		case CKR_SLOT_ID_INVALID:
		case CKR_DEVICE_ERROR:
			throw e;
			break;

		// Salvamos el resto de excepciones generadas, indicando en el valor de retorno que el
		// el lector no tienen tarjeta
		default:
			sToken = "";
		}
	}
	catch(...)
	{
		// Cualquier otro tipo de error, lo capturamos aquí
	    throw Pkcs11Exception(CKR_FUNCTION_FAILED);
	}

	return sToken;
}

/********************************************************************************************
	Método que indica el número de ficheros elementales hijos del DF Publico (6061).
	Corresponde con el número de objetos públicos en el PKCS#15.

	Devuelve:
		- devuelve el número de FE encontrados
********************************************************************************************/
int CSlot::GetNumberPublicFE( CCommunicator & comm )
{
	int iFound = 0;

	CSlotTransaction	transaction(*this);

	// Accedemos al DF, cuya ruta es MF->6061
	comm.SelectFileByName( MF_NAME );
	comm.SelectFileById( 0x6061 );

	for (ushort idx=0x7004; idx<=0x7008; idx++)
	{
		try
		{
			comm.SelectFileById( idx );

			iFound++;
		}
		catch(Pkcs11Exception &)
		{}
	}

	return iFound;
}

/********************************************************************************************
	Método que comprueba si el lector tiene una tarjeta de las soportadas por el módulo.

	Devuelve:
		- true si el lector tiene una tarjeta de las soportadas por el módulo.
		- false en caso contrario.
********************************************************************************************/
bool CSlot::SupportedTokenPresent()
{
	return (!CheckToken().empty() && (CheckToken().compare(UNSUPPORTED_TOKEN) != 0));
}


CTokenType & CSlot::getTokenType(void)
{
	// Si no conocemos el TokenType intentamos identificarlo
	if( !m_pTokenType )
		CheckToken();

	// Si seguimos sin conocerlo, la tarjeta es desconocida y lanzamos la excepción
	if( !m_pTokenType )
		throw Pkcs11Exception(CKR_SLOT_ID_INVALID);

	return *m_pTokenType;
};


void CSlot::TransmitData( byteBuffer & cmd, byteBuffer & answer )
{
	TransmitData( &cmd[0], cmd.size(), answer );
}

void CSlot::TransmitData( byte * pCmd, ulong cmdLen, byteBuffer & answer )
{
	LONG		result;
	DWORD		answerLen;

	if(!m_hCard)
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	answerLen = MAX_CMD_ANSWER;
	answer.resize(answerLen);

	result = SCardTransmit( m_hCard, SCARD_PCI_T0, pCmd, cmdLen, NULL, &answer[0], &answerLen );
	if( result != SCARD_S_SUCCESS )
	{
		//
		// Error en la transmisión del comando.
		// Desconectamos y propagamos el error.
		//
		answer.clear();
		
        if( result == SCARD_W_REMOVED_CARD ){
            Disconnect(true);
			throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );
        }else
		{
			#if defined _YOSEMITE_
				if(numTransaction < 2 && result == SCARD_W_RESET_CARD || result == SCARD_W_UNPOWERED_CARD){
					DBG_TRACE(("Retransmisi—n BeginTransaction num retrans 0x%08x", numTransaction));
                    numTransaction++;
					
					Connect();

					if(transactionBegun)
						BeginTransaction();

					TransmitData( pCmd, cmdLen, answer );
                } else {
                    numTransaction = 0;
                    throw Pkcs11Exception( CKR_DEVICE_ERROR );
                }
			#else
                Disconnect(true);
				throw Pkcs11Exception( CKR_DEVICE_ERROR );
			#endif
		}
	}
	else
	{
        numTransaction = 0;
		//
		// Comando correcto. Reducimos el tamaño de answer al realmente devuelto
		//
		answer.resize(answerLen);
	}
}

void CSlot::BeginTransaction( void )
{	
	LONG		result;

	transactionBegun = true;

	if(!m_hCard)
		Connect();

	if(!m_hCard)
		throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );

	result = SCardBeginTransaction( m_hCard );
	if( result != SCARD_S_SUCCESS )
	{
		if( result == SCARD_W_REMOVED_CARD )
			throw Pkcs11Exception( CKR_SESSION_HANDLE_INVALID );
		else 
		{
			#if defined _YOSEMITE_
				if(numTransaction < 2 && result == SCARD_W_RESET_CARD || result == SCARD_W_UNPOWERED_CARD){
                    DBG_TRACE(("Retransmisi—n BeginTransaction num retrans 0x%08x", numTransaction));
					numTransaction++;
					
					Connect();
					
					BeginTransaction();
                    
                } else {
                    numTransaction = 0;
                    throw Pkcs11Exception( CKR_DEVICE_ERROR );
                }
			#else
				throw Pkcs11Exception( CKR_DEVICE_ERROR );
			#endif
		}
	}
    
    numTransaction = 0;

}
void CSlot::EndTransaction( void )
{
	transactionBegun = false;
	if( m_hCard )
		SCardEndTransaction( m_hCard, SCARD_LEAVE_CARD );

	//
	// Permitimos continuar, sin tener en cuenta el resultado de SCardEndTransaction()
	//
}

//
// Conversión de errores PCSC a errores PKCS#11
//
ulong CSlot::PCSCErr2P11Err( ulong ulPCSCErr )
{
	switch( ulPCSCErr )
	{
	case SCARD_S_SUCCESS: // Ningún error
		return CKR_OK;

	case SCARD_E_NO_MEMORY: // No hay suficiente memoria para completar la operación
		return CKR_HOST_MEMORY;

	case SCARD_E_NO_SMARTCARD: // No hay tarjeta en el lector
		return CKR_TOKEN_NOT_PRESENT;

	case SCARD_E_NOT_READY: // El lector o la tarjeta no están listos para aceptar comandos
		return CKR_DEVICE_ERROR;

	case SCARD_E_READER_UNAVAILABLE: // El lector no está actualmente disponible
		return CKR_SLOT_ID_INVALID;

	case SCARD_E_READER_UNSUPPORTED: // El controlador del lector no tiene los requisitos mínimos para
									 // soportar la acción.
		return CKR_DEVICE_ERROR;

	case SCARD_E_UNKNOWN_READER: // Lector desconocido
		return CKR_SLOT_ID_INVALID;

	case SCARD_W_REMOVED_CARD: // Se ha extraído la tarjeta del lector
		return CKR_TOKEN_NOT_PRESENT;

	default:
		return CKR_GENERAL_ERROR;
	}

	return CKR_GENERAL_ERROR;		// Keep compiler happy
}
