Chipcard-PCSC

 view release on metacpan or  search on metacpan

PCSC.xs  view on Meta::CPAN

	void*          pvReserved2
	PREINIT:
		SCARDCONTEXT hContext = 0;
	CODE:
		ST(0) = sv_newmortal();
		gnLastError = hEstablishContext (dwScope, pvReserved1, pvReserved2, &hContext);

		/* Then we either return an explicit 'UNDEF' value or the handle */
		if (gnLastError != SCARD_S_SUCCESS) {
			ST(0) = &PL_sv_undef;
		} else {
			sv_setiv (ST(0), hContext);
		}

#///////////////////////////////////////////////////////////////////////////
#//   ReleaseContext ()
#//
#// INPUT :
#// - $hContext -> Connection context to be closed
#//
#// OUTPUT :
#// - ReleaseContext returns a true value on successful operation and a
#// false value otherwise.
bool
_ReleaseContext (hContext)
	unsigned long hContext
	CODE:
		gnLastError = hReleaseContext (hContext);

		/* Then returns true or false according to the return code */
		if (gnLastError != SCARD_S_SUCCESS) {
			RETVAL = FALSE;
		} else {
			RETVAL = TRUE;
		}
	OUTPUT:
		RETVAL
			
#///////////////////////////////////////////////////////////////////////////
#//   ListReaders ()
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC resource manager.
#// - $mszGroups -> List of groups to list readers. (as of this writing,
#// this is not used and should be 0
#//
#// OUTPUT :
#// ListReaders returns the 'undef' value if an error occurs otherwise it
#// returns the list of available readers. Note that this can be an empty
#// list...
#//
SV *
_ListReaders(hContext, svGroups)
	unsigned long hContext
	SV*           svGroups
	PREINIT:
		DWORD nBufferSize = 0;
		char* szBuffer = NULL;
		char* szCurrentToken = NULL;
		char* mszGroups;
	PPCODE:
		/* Before doing anything, we check that we have a valid group. */
		if (SvPOK(svGroups)) {
			/* TODO : see how this works... multistring stuff with time
			 *svGroups may become a reference to an array of groups... or undef
			 */
			mszGroups = SvPV (svGroups, PL_na);
		} else {
			mszGroups = 0;
		}

		/*   The first call to SCardListReaders gives us the size of the
		 * buffer we must allocate for the list.
		 */
		gnLastError = hListReaders (hContext, mszGroups, 0, &nBufferSize);

		/* In case of any error we immediately return an explicit UNDEF value */
		if (gnLastError != SCARD_S_SUCCESS) {
			XSRETURN_UNDEF;
		}

		if (nBufferSize > 0) {
			/*   At this point, nBufferSize contains the size of the buffer to
		 	 * alloate. We use New as recomended by perlguts(3pm). The
		 	 * buffer will be freed with Safefree()...
		 	 */
			New (2018, szBuffer, nBufferSize, char);
			if (szBuffer == NULL) {
				gnLastError = SCARD_E_NO_MEMORY;
				warn ("Could not allocate buffer at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}

			/*   The buffer is ready, so we can now retrieve the whole list.  */
			gnLastError = hListReaders (hContext, mszGroups, szBuffer, &nBufferSize);

			/* Then check for any error */
			if (gnLastError != SCARD_S_SUCCESS) {
				Safefree (szBuffer);
				XSRETURN_UNDEF;
			}

			/*   The string must be NULL terminated
		 	 * May be just too much paranoid but...
		 	 */
			if (szBuffer[nBufferSize-1] != 0) {
				Safefree (szBuffer);
				gnLastError = SCARD_F_INTERNAL_ERROR;
				warn ("PCSC did not return a NULL terminated multistring at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}

			/*   Now we need to push each reader separately on the stack. If
		 	 * no readers are found, the stack is left empty...
		 	 */
			szCurrentToken = szBuffer;
			while (strlen(szCurrentToken)) {
				XPUSHs (sv_2mortal(newSVpv(szCurrentToken,0)));
				szCurrentToken = strchr (szCurrentToken, 0) + 1;
			}
			/* Free our buffer...
			 * DO NOT USE free() or delete() here ! Let Perl deal with this.
			 */
			Safefree (szBuffer);
		} else {
			gnLastError = SCARD_F_INTERNAL_ERROR;
			warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

#///////////////////////////////////////////////////////////////////////////
#//   Connect ()
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC Resource Manager
#// - $szReader -> ReaderName to connect to
#// - dwShareMode -> Mode of connection : exclusive or shared
#// - dwPreferredProtocols -> Desired protocol use
#//
#// OUTPUT :
#// Connect returns an array with the handle to the connection and the
#// active protocol for this connection ($hCard, $dwActiveProtocol).
#// If a problem occurs, it just return the 'undef' value.
SV*
_Connect (hContext, szReader, dwShareMode, dwPreferredProtocols)
	unsigned long hContext
	const char*   szReader
	unsigned long dwShareMode
	unsigned long dwPreferredProtocols
	PREINIT:
		SCARDHANDLE hCard = 0;
		DWORD dwActiveProtocol = 0;
	PPCODE:
		gnLastError = hConnect (hContext, szReader, dwShareMode, dwPreferredProtocols, &hCard, &dwActiveProtocol);

		/* We return immediately in case of an error */
		if (gnLastError != SCARD_S_SUCCESS)
			XSRETURN_UNDEF;

		/* If anything was successful, push the two scalar values */
		XPUSHs (sv_2mortal(newSViv(hCard)));
		XPUSHs (sv_2mortal(newSViv(dwActiveProtocol)));

#///////////////////////////////////////////////////////////////////////////
#//   Reconnect ()
#//
#// INPUT :
#// - $hCard -> Handle to a previous call to connect
#// - dwShareMode -> Mode of connection : exclusive or shared
#// - dwPreferredProtocols -> Desired protocol use
#// - $dwInitialization -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// Reconnect returns the new active protocol or the 'undef' value if an
#// error occurs
SV*
_Reconnect (hCard, dwShareMode, dwPreferredProtocols, dwInitialization)
	unsigned long hCard
	unsigned long dwShareMode
	unsigned long dwPreferredProtocols
	unsigned long dwInitialization
	PREINIT:
		DWORD dwActiveProtocol = 0;
	CODE:
		ST(0) = sv_newmortal();
		gnLastError = hReconnect (hCard, dwShareMode, dwPreferredProtocols, dwInitialization, &dwActiveProtocol);
		/* Return either an UNDEF value if an error occurs or the current
		 * active protocol as returned by PCSC
		 */
		if (gnLastError == SCARD_S_SUCCESS)
			sv_setiv (ST(0), dwActiveProtocol);
		else
			ST(0) = &PL_sv_undef;

#///////////////////////////////////////////////////////////////////////////
#//   Disconnect ()
#//
#// INPUT :
#// - $hCard -> Connection made from Connect
#// - $dwDisposition -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// Disconnect returns TRUE upon successful result. Oppositely, it
#// returns FALSE if somthing went bang
bool
_Disconnect (hCard, dwDisposition)
	unsigned long hCard;
	unsigned long dwDisposition;
	CODE:
		gnLastError = hDisconnect (hCard, dwDisposition);

		/* Then check for an error */
		if (gnLastError != SCARD_S_SUCCESS)
			RETVAL = FALSE;
		else
			RETVAL = TRUE;
	OUTPUT:
		RETVAL

#///////////////////////////////////////////////////////////////////////////
#//   Status ()
#//
#// INPUT :
#// - $hCard -> Connection made from Connect
#//
#// OUTPUT :
#// Status returns a list holding different informations about the
#// reader : ($szReaderName, $dwState, $dwProtocol, \@bAttr). If an
#// error pops up, the 'undef' value is returned.
#//
#// Important note:
#//   We return the ATR in the form of a reference to an array of bytes.
#// When we build this reference, we start to build an array wich is
#// made mortal, then we fill it with non mortal items usin av_push().
#// These items need to be immortal at this point because av_push does
#// not increase the reference count of the scalar values it pushes into
#// the array. As our array dies when it passes out of scope, perl
#// would free its content and then any attempt to use them would result
#// in an 'Attempt to free unreferenced scalar' error...
SV*
_Status (hCard)
	long hCard
	PREINIT:
		int            nCount = 0;
#ifdef WIN32
		char           tmpReaderName[200];
		char*          szReaderName = tmpReaderName;
		unsigned long  cchReaderLen = sizeof(tmpReaderName);
		char           tmpAtr[MAX_ATR_SIZE];
		unsigned char* pbAtr = tmpAtr;
		unsigned long  cbAtrLen = sizeof(tmpAtr);
#else
		char*          szReaderName = NULL;
		DWORD cchReaderLen = 0;
		unsigned char* pbAtr = NULL;
		DWORD cbAtrLen = 0;
#endif
		DWORD dwState = 0;
		DWORD dwProtocol = 0;
		AV*            aATR = 0;
	PPCODE:
		/* We call the function with a null cchReaderLen : this should
		 * gives us the length of the buffer to allocate
		 */
		gnLastError = hStatus (hCard, szReaderName, &cchReaderLen,
		                       &dwState, &dwProtocol, (BYTE *)pbAtr, &cbAtrLen);
		
		/* Behaviour differs here from PCSC and PCSClite :
		 * PCSC returns SUCCESS while PCSClite returns an error
		 */
		if (gnLastError == SCARD_E_INSUFFICIENT_BUFFER || gnLastError == SCARD_S_SUCCESS) {
			/* The call was hopefuly successful, so we allocate the
			 * buffer for the ATR
			 */
#ifndef WIN32
			/* This hack should be temporary as PCSClite should eventually behave like PCSC */
			cbAtrLen = MAX_ATR_SIZE;
#endif
			if (cbAtrLen <= 0) {
				gnLastError = SCARD_F_INTERNAL_ERROR;
				warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}
			New (2018, pbAtr, cbAtrLen, unsigned char);
			if (pbAtr == NULL) {
				gnLastError = SCARD_E_NO_MEMORY;
				warn ("Could not allocate buffer at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}
			/* we then allocate the buffer for the reader name */
			if (cbAtrLen <= 0) {
				gnLastError = SCARD_F_INTERNAL_ERROR;
				warn ("PCSC did not return a valid buffer length at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}
			New (2018, szReaderName, cchReaderLen, char);
			if (szReaderName == NULL) {
				Safefree (pbAtr);
				gnLastError = SCARD_E_NO_MEMORY;
				warn ("Could not allocate buffer at %s line %d\n\t",
				      __FILE__, __LINE__);
				XSRETURN_UNDEF;
			}
			/* Now we perform the real call to SCardStatus */
			gnLastError = hStatus (hCard, szReaderName, &cchReaderLen,
			                       &dwState, &dwProtocol, (BYTE *)pbAtr, &cbAtrLen);
			if (gnLastError != SCARD_S_SUCCESS) {
				Safefree (szReaderName);
				Safefree (pbAtr);
				XSRETURN_UNDEF;
			}
		} else {
			/* As our first call should trigger the SCARD_E_INSUFFICIENT_BUFFER
			 * error or no error, we consider any other case as a failure...
			 */
			XSRETURN_UNDEF;
		}

PCSC.xs  view on Meta::CPAN

			/* Then we fill it with every byte from the ATR
			 * note that we do not make this data mortal because av_push()
			 * does not increment the reference count. See the note in the
			 * function header above
			 */
			for (nCount=0; nCount < cbAtrLen; nCount++) {
				av_push (aATR, newSViv(pbAtr[nCount]));
			}
		}
		/* In the event that no ATR is available, we used to fill aATR
		 * with '(AV*) &PL_sv_undef' However, pushing a reference to
		 * this seems is hard to handle I therefore prefer to do
		 * nothing and leave aATR with the default null value and
		 * the code below will not push the reference to the ATR array
		 */

		/* eventually, we end up pushing all the values */
		XPUSHs (sv_2mortal(newSVpv(szReaderName,0)));
		XPUSHs (sv_2mortal(newSViv(dwState)));
		XPUSHs (sv_2mortal(newSViv(dwProtocol)));
		/* as well as a reference to the ATR array if available */
		if (aATR) {
			XPUSHs (sv_2mortal(newRV((SV*)aATR)));
		}

		/* As a conclusion, we just free what we took */
		Safefree (szReaderName);
		Safefree (pbAtr);

#///////////////////////////////////////////////////////////////////////////
#//   Transmit ()
#//
#// INPUT :
#// - $hCard
#// - @inBuffer = ($Protocol, \@BytesToSend)
#// $Protocol contains the protocol (T0|T1)
#// @BytesToSend contains the bytes to transmit
#//   Note: please note that @inBuffer is actually appended to the
#// parameters list, therefore, the following calls are equivalent:
#// @inBuffer ($Protocol, [0x00, 0x12, 0x33]); = ;Transmit ($hCard, @inBuffer);
#// Transmit ($hCard, $Protocol, [0x00, 0x12, 0x33]);
#//
#// OUTPUT :
#// - @outBuffer = ($Protocol, \@BytesRead)
#//   - $Protocol may be undef
#//   - @BytesRead contains the returned bytes
#// Transmit can return the 'undef' value alone if an error occurs.
SV*
_Transmit (hCard, dwProtocol, psvSendData)
	unsigned long hCard;
	unsigned long dwProtocol;
	SV*           psvSendData;
	PREINIT:
		int                        nCount = 0;
		static char*               pbSendBuffer = NULL;
		static unsigned char       pbRecvBuffer [MAX_BUFFER_SIZE_EXTENDED];
		unsigned long              cbSendLength = 0;
		DWORD                      cbRecvLength = sizeof (pbRecvBuffer);
		SCARD_IO_REQUEST           ioSendPci, ioRecvPci;
		AV*                        aRecvBuffer = NULL;
	PPCODE:
		/* We make sure that the array is sane */
		if (psvSendData == NULL) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvSendData is a NULL pointer at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		/* Should the second parameter not be a reference, we return the
		 * SCARD_E_INVALID_PARAMETER error code.
		 */
		if ((!SvROK(psvSendData))||(SvTYPE(SvRV(psvSendData)) != SVt_PVAV)) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvSendData is not a RVAV at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}
		/* We have to build up our IO_REQUEST structures according to
		 * $dwProtocol
		 */
		switch (dwProtocol) {
		case SCARD_PROTOCOL_T0:
		case SCARD_PROTOCOL_T1:
		case SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1:
		case SCARD_PROTOCOL_RAW:
			ioSendPci.dwProtocol  = dwProtocol;
			ioSendPci.cbPciLength = sizeof(ioSendPci);
			ioRecvPci.dwProtocol  = dwProtocol;
			ioRecvPci.cbPciLength = sizeof(ioRecvPci);
			break;
		default:
			/* If $dwProtocol holds an invalid value, we exist reporting
			 * the error SCARD_E_INVALID_VALUE.
			 */
			gnLastError = SCARD_E_INVALID_VALUE;
			warn ("unknown protocol %ld given at %s line %d\n\t",
			      dwProtocol, __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		/* Let's allocate some space for the send buffer */
		cbSendLength = av_len((AV*)SvRV(psvSendData)) + 1;
		if (cbSendLength <= 0) {
			gnLastError = SCARD_E_INVALID_VALUE;
			warn ("empty array given at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}
 		New (2018, pbSendBuffer, cbSendLength, char);
		if (pbSendBuffer == NULL) {
			gnLastError = SCARD_E_NO_MEMORY;
			warn ("Could not allocate buffer at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		/* We have to extract data from the array referenced by psvSendData */
		for (nCount = 0; nCount < cbSendLength ; nCount++)
			pbSendBuffer[nCount] = (char)SvIV(*av_fetch((AV*)SvRV(psvSendData), nCount, 0));

		/* Everything is ready : call the real function... */
		gnLastError = hTransmit (hCard, &ioSendPci, (BYTE *)pbSendBuffer, cbSendLength, &ioRecvPci, pbRecvBuffer, &cbRecvLength);
		if (gnLastError != SCARD_S_SUCCESS) {
			/* Free the buffer if something went wrong */
			Safefree (pbSendBuffer);
			XSRETURN_UNDEF;
		}

		/* At this point, the command was successful. We still need to
		 * return all the values from our buffer...
		 * so we build an array for the ATR
		 */
		aRecvBuffer = (AV*) sv_2mortal((SV*)newAV());

		/* we fill this array with every byte from the Response
		 * note that we do not make this data mortal because av_push()
		 * does not increment the reference count. See the note in the
		 * function header above
		 */
		for (nCount = 0; nCount < cbRecvLength; nCount++)
 			av_push (aRecvBuffer, newSViv(pbRecvBuffer[nCount]));

		XPUSHs (sv_2mortal(newSViv(ioRecvPci.dwProtocol)));
		XPUSHs (sv_2mortal(newRV((SV*)aRecvBuffer)));

		/* Do not forget to free the dynamically allocated buffer */
		Safefree (pbSendBuffer);

#///////////////////////////////////////////////////////////////////////////
#//   Control ()
#//
#// INPUT :
#// - $hCard
#// - $dwControlCode
#// - @inBuffer = (\@BytesToSend)
#// @BytesToSend contains the bytes to transmit
#//
#// OUTPUT :
#// - @outBuffer = (\@BytesRead)
#// - @BytesRead contains the returned bytes
#// Control can return the 'undef' value alone if an error occurs.
SV*
_Control (hCard, dwControlCode, psvSendData)
	unsigned long hCard;
	unsigned long dwControlCode;
	SV*           psvSendData;
	PREINIT:
		int                        nCount = 0;
		static char*               pbSendBuffer = NULL;
		static unsigned char       pbRecvBuffer [MAX_BUFFER_SIZE];
		unsigned long              cbSendLength = 0;
		DWORD                      cbRecvLength = sizeof (pbRecvBuffer);
		AV*                        aRecvBuffer = NULL;
	PPCODE:
		/* We make sure that the array is sane */
		if (psvSendData == NULL) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvSendData is a NULL pointer at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		/* Should the second parameter not be a reference, we return the
		 * SCARD_E_INVALID_PARAMETER error code.
		 */
		if ((!SvROK(psvSendData))||(SvTYPE(SvRV(psvSendData)) != SVt_PVAV)) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvSendData is not a RVAV at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		/* Let's allocate some space for the send buffer, if needed */
		cbSendLength = av_len((AV*)SvRV(psvSendData)) + 1;
		if (cbSendLength <= 0) {
			gnLastError = SCARD_E_INVALID_VALUE;
			warn ("empty array given at %s line %d\n\t",
				  __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}
		New (2018, pbSendBuffer, cbSendLength, char);
		if (pbSendBuffer == NULL) {
			gnLastError = SCARD_E_NO_MEMORY;
			warn ("Could not allocate buffer at %s line %d\n\t",
				  __FILE__, __LINE__);
			XSRETURN_UNDEF;
		}

		for (nCount = 0; nCount < cbSendLength ; nCount++)
			pbSendBuffer[nCount] = (char)SvIV(*av_fetch((AV*)SvRV(psvSendData), nCount, 0));

		/* Everything is ready : call the real function... */
		gnLastError = hControl (hCard, dwControlCode,
			(cbSendLength > 0 ? (BYTE *)pbSendBuffer : NULL), cbSendLength, 
			pbRecvBuffer, sizeof(pbRecvBuffer), &cbRecvLength);

		if (gnLastError != SCARD_S_SUCCESS) {
			/* Free the buffer if something went wrong */
			Safefree (pbSendBuffer);
			XSRETURN_UNDEF;
		}

		/* At this point, the command was successful. We still need to
		 * return all the values from our buffer...
		 */
		aRecvBuffer = (AV*) sv_2mortal((SV*)newAV());

		/* we fill this array with every byte from the Response
		 * note that we do not make this data mortal because av_push()
		 * does not increment the reference count. See the note in the
		 * function header above
		 */
		for (nCount = 0; nCount < cbRecvLength; nCount++)
 			av_push (aRecvBuffer, newSViv(pbRecvBuffer[nCount]));

PCSC.xs  view on Meta::CPAN

	unsigned long hCard;
	CODE:
		gnLastError = hBeginTransaction (hCard);

		/* Then we check for an error */
		if (gnLastError != SCARD_S_SUCCESS)
			RETVAL = FALSE;
		else
			RETVAL = TRUE;
	OUTPUT:
		RETVAL

#///////////////////////////////////////////////////////////////////////////
#//   EndTransaction ()
#//
#// INPUT :
#// - $hCard -> connection made from Connect()
#// - $dwDisposition -> Desired action taken on the card/reader
#//
#// OUTPUT :
#// EndTransaction returns true or false depending on its successful
#// completion
unsigned long
_EndTransaction (hCard, dwDisposition)
	unsigned long hCard;
	unsigned long dwDisposition;
	CODE:
		gnLastError = hEndTransaction (hCard, dwDisposition);

		/* Then we check for an error */
		if (gnLastError != SCARD_S_SUCCESS)
			RETVAL = FALSE;
		else
			RETVAL = TRUE;
	OUTPUT:
		RETVAL

#///////////////////////////////////////////////////////////////////////////
#//   GetStatusChange ()
#//
#// This
#//
#// INPUT :
#// - $hContext -> Connection context to the PC/SC resource manager.
#// - $nTimeout -> Time to wait for a change (or SCARD_INFINITE)
#// - \@ReaderStates -> array of reader states
#//
#// OUTPUT :
bool
_GetStatusChange (hContext, dwTimeout, psvReaderStates)
	unsigned long hContext;
	unsigned long dwTimeout;
	SV*           psvReaderStates;
	PREINIT:
		static SCARD_READERSTATE *rgReaderStates_t = NULL;
		unsigned int               nCount = 0;
		unsigned int               nATRCount = 0;
		unsigned int               nReaders = 0;
		AV*                        aRecvBuffer = NULL;

	PPCODE:
		if (psvReaderStates == NULL) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvReaderStates is a NULL pointer at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_NO;
		}

		/* Should the second parameter not be a reference, we return the
		 * SCARD_E_INVALID_PARAMETER error code.
		 */
		if ((!SvROK(psvReaderStates))||(SvTYPE(SvRV(psvReaderStates)) != SVt_PVAV)) {
			gnLastError = SCARD_E_INVALID_PARAMETER;
			warn ("psvReaderStates is not a RVAV at %s line %d\n\t",
			      __FILE__, __LINE__);
			XSRETURN_NO;
		}

		/* Get the total number of elements in our array */
		nReaders = av_len((AV*)SvRV(psvReaderStates)) + 1;
		
		/* free the memory allocated during previous call to GetStatusChange */
		if (rgReaderStates_t)
			Safefree(rgReaderStates_t);

		/* allocate the Reader States table */
		Newz(2018, rgReaderStates_t, nReaders, SCARD_READERSTATE);
		if (rgReaderStates_t == NULL)
		{
			warn ("Could not allocate buffer at %s line %d\n\t",
				__FILE__, __LINE__);
			XSRETURN_NO;
		}

		for (nCount = 0; nCount < nReaders; nCount++) {
			/* As long as psvReaderStates is a reference to a PVAV we
			 * should be able to use av_fetch() without error
			 */
			SV *svCurrentToken = (*av_fetch((AV*)SvRV(psvReaderStates), nCount, 0));

			/* Now see if the elements in the array are reference to hasharrays */
			if ((!SvROK(svCurrentToken)) || (SvTYPE(SvRV(svCurrentToken)) != SVt_PVHV)) {
				gnLastError = SCARD_E_INVALID_PARAMETER;
				warn ("psvReaderStates[%d] is not a RVHV at %s line %d\n\t", nCount,
				      __FILE__, __LINE__);
				XSRETURN_NO;
			}

			/* Checkout if the 'name' argument has been passed */
			if (hv_exists((HV*)SvRV(svCurrentToken), "reader_name", 11)) {
				SV** psvName         = NULL;

				/* fetch the value of reader_name */
				psvName = hv_fetch((HV*)SvRV(svCurrentToken), "reader_name", 11, 0);

				/* Link the internal structure and the fetched pointer if appropriate */
				if ((psvName != NULL) && (SvTYPE(*psvName) == SVt_PV)) {
//					printf ("We got a name\n");
					rgReaderStates_t[nCount].szReader = SvPV (*psvName, PL_na);
//					printf ("which is : %s\n", rgReaderStates_t[nCount].szReader);
				} else {



( run in 0.565 second using v1.01-cache-2.11-cpan-71847e10f99 )