/* TODO : this function hasn't really been tested */ int readerGetContactlessStatus( tReaderManager *pManager, tReader *pReader ) { DWORD dwRecvLength; BYTE pbRecvBuffer[20]; LONG rv; int i; BOOL automatic; /* Duh. If you pass me a NULL pointer then I'm out of here */ if ( ( pManager == NULL ) || ( pManager->hContext == NULL ) ) return( SCARD_E_INVALID_PARAMETER ); automatic = readersSettingBitmapBitTest( pManager, READER_BIT_AUTO ); if ( automatic ) { /* re-enumerate the readers in the system as it may have changed */ rv = readersConnect( pManager ); if ( rv != SCARD_S_SUCCESS ) return( rv ); } sprintf(messageString, "Requesting contactles status"); readersLogMessage( pManager, LOG_INFO, 2, messageString); /* then check all readers we were ABLE to connect to */ for ( i = 0; i < pManager->nbReaders; i++ ) { /* check we have connected and have a driver for this reader */ if ( ( pReader[i].hCard != NULL ) && ( pReader[i].pDriver != NULL ) && ( automatic || readersSettingBitmapNumberTest( pManager, i ) ) ) { dwRecvLength = sizeof(pbRecvBuffer); rv = ((tReaderDriver *)(pReader[i].pDriver))->getContactlessStatus(pReader, pbRecvBuffer, &dwRecvLength); if ( rv == SCARD_S_SUCCESS ) { if (pManager->libVerbosityLevel) { sprintf(messageString, "Reader %d Status: ", i); sPrintBufferHex( (messageString + strlen("Reader %d Status: ") ), dwRecvLength, pbRecvBuffer); readersLogMessage( pManager, LOG_INFO, 2, messageString); sprintf(messageString, "Number of Tags = %d", pbRecvBuffer[4]); readersLogMessage( pManager, LOG_INFO, 2, messageString); } } else PCSC_ERROR( pManager, rv, "driver->getConnectlessStatus:"); } } return (rv); }
/* we can detect in this reader */ LONG acr122UGetTagList( tReader *pReader, tTag pTags[] /* an array of tags that will be filled */ /* this must to the max specified */ /* in the driver structure */ ) { LONG rv; DWORD dwRecvLength; BYTE pbRecvBuffer[40]; /* TODO find a constant for the max size */ int i, other; BOOL known, processTag; pReader->tagList.numTags = 0; /* This reader has some wierd behaviour, in that sometimes the ONLY tag is */ /* reported on the SECOND call to poll it not the first that is why we write*/ /* into the array using pReader->tagList.numTags as an index, and not 'i' */ /* loop until we have read possible number of unique tags on the reader */ for (i = 0; i < ACR122U_MAX_NUM_TAGS; i++) { /* we are not going to read ANY tag's contents and so it's a NULL pointer */ pTags[i].contents.pData = NULL; pTags[i].contents.dataSize = 0; pTags[i].contents.extensionHook = NULL; dwRecvLength = sizeof(pbRecvBuffer); rv = apduSend(pReader->hCard, APDU_POLL_MIFARE, sizeof(APDU_POLL_MIFARE), pbRecvBuffer, &dwRecvLength); if (rv == SCARD_S_SUCCESS) { processTag = FALSE; /* ACS_TAG_FOUND = {0xD5, 0x4B} */ if ( (pbRecvBuffer[SW1] == 0xD5) && (pbRecvBuffer[SW2] == 0x4B) && (pbRecvBuffer[NUM_TAGS_FOUND] != 0x00) ) /* not sure it reports 2 when there are 2 */ { /* how the rest is organized depends on tag Type */ switch ( pbRecvBuffer[SEL_RES_BYTE] ) { case SEL_RES_MIFARE_ULTRA: case SEL_RES_MIFARE_1K: case SEL_RES_MIFARE_MINI: /* not sure but I think so */ case SEL_RES_MIFARE_4K: case SEL_RES_MIFARE_DESFIRE: /* the uid is in the next 'uid_length' number bytes */ sPrintBufferHex(pTags[pReader->tagList.numTags].uid, pbRecvBuffer[UID_LENGTH], (pbRecvBuffer + UID_START) ); /* store tag type in tag struct */ pTags[pReader->tagList.numTags].tagType = (tTagType)(pbRecvBuffer[SEL_RES_BYTE]); processTag = TRUE; break; case SEL_RES_JCOP30: pTags[pReader->tagList.numTags].tagType = (tTagType)(pbRecvBuffer[SEL_RES_BYTE]); sprintf( pTags[pReader->tagList.numTags].uid, "UID Unknown" ); break; case SEL_RES_GEMPLUS_MPCOS: pTags[pReader->tagList.numTags].tagType = (tTagType)(pbRecvBuffer[SEL_RES_BYTE]); sprintf( pTags[pReader->tagList.numTags].uid, "UID Unknown" ); break; default: pTags[pReader->tagList.numTags].tagType = UNKNOWN_TYPE; sprintf( pTags[pReader->tagList.numTags].uid, "UID Unknown" ); break; } /* avoid processing tags we don't know enough about */ if ( processTag ) { /* TODO look into using the TARGET_NUMBER byte returned... */ /* first one ? */ if ( i == 0 ) (pReader->tagList.numTags)++; /* got first one */ else { /* check if this ID is already in our list - avoid duplication */ known = FALSE; for (other = 0; ((other < pReader->tagList.numTags) && (!known)) ; other++) { /* is this tag id already in this reader's list? */ if (strcmp(pTags[i].uid, pTags[other].uid) == 0) known = TRUE; } if (!known) (pReader->tagList.numTags)++; /* got another one */ } } } } else return (rv); } /* for */ return (rv); }
/************************ READER CHECK ***************************/ LONG acr122UReaderCheck( tReader *pReader, BOOL *pReaderSupported ) { static char pbReader[MAX_READERNAME] = ""; DWORD dwAtrLen, dwReaderLen, dwState, dwProt; DWORD dwRecvLength; BYTE pbAtr[MAX_ATR_SIZE] = ""; int i; LONG rv; BYTE pbRecvBuffer[MAX_FIRMWARE_STRING_LENGTH]; *pReaderSupported = FALSE; /* First check the name reported by pcscd to see if it's possible supported */ for (i = 0; (i < SUPPORTED_READER_NAME_ARRAY_COUNT) & (*pReaderSupported == FALSE) ; i++) { if ( strncmp( pReader->name, SUPPORTED_READER_NAME_ARRAY[i], sizeof(SUPPORTED_READER_NAME_ARRAY[i])-1) == 0 ) *pReaderSupported = TRUE; } /* If even the name is not supported, then we're done */ if ( *pReaderSupported == FALSE ) return (SCARD_S_SUCCESS ); /* just because the name was OK, doesn't mean it'll actually work! */ *pReaderSupported = FALSE; /* Get firmware version and check it's really a ACR122* */ dwRecvLength = sizeof(pbRecvBuffer); rv = apduSend(pReader->hCard, APDU_GET_READER_FIRMWARE, sizeof(APDU_GET_READER_FIRMWARE), pbRecvBuffer, &dwRecvLength); if (rv != SCARD_S_SUCCESS) return (rv); /* Search the list of supported firmware versions (reader versions) */ /* to check we are compatible with it - or have tested with it */ /* NULL terminate the BYTE array for subsequent string compares */ pbRecvBuffer[dwRecvLength] = '\0'; for (i = 0; (i < SUPPORTED_READER_FIRMWARE_ARRAY_COUNT) & (*pReaderSupported == FALSE) ; i++) { if ( strncmp( ((char *)pbRecvBuffer), SUPPORTED_READER_FIRMWARE_ARRAY[i], sizeof(SUPPORTED_READER_FIRMWARE_ARRAY[i])-1) == 0 ) *pReaderSupported = TRUE; } /* if we didn't find that we support it, then we're done */ if ( *pReaderSupported == FALSE ) return( SCARD_S_SUCCESS ); /* no actual communication errors to report */ /* If we got this far then the general name and specific firmware version is supported */ /* Get ATR so we can tell if there is a SAM in the reader */ dwAtrLen = sizeof(pbAtr); dwReaderLen = sizeof(pbReader); SCardStatus( (SCARDHANDLE) (pReader->hCard), pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen); dwRecvLength = sizeof(pbRecvBuffer); rv = apduSend(pReader->hCard, APDU_SET_RETRY, sizeof(APDU_SET_RETRY), pbRecvBuffer, &dwRecvLength); if (rv != SCARD_S_SUCCESS) return (rv); /* get card status ATR first two bytes = ACS_NO_SAM= '3B00' */ if ( (pbAtr[SW1] != 0x3B) || (pbAtr[SW2] != 0x00) ) { dwRecvLength = sizeof(pbRecvBuffer); rv = apduSend(pReader->hCard, APDU_GET_SAM_SERIAL, sizeof(APDU_GET_SAM_SERIAL), pbRecvBuffer, &dwRecvLength); if (rv == SCARD_S_SUCCESS) { sPrintBufferHex(pReader->SAM_serial, dwRecvLength, pbRecvBuffer); #if 0 sprintf(messageString, "SAM Serial: %s", pReader->SAM_serial); readersLogMessage(LOG_INFO, 1, messageString); #endif } else return (rv); dwRecvLength = sizeof(pbRecvBuffer); rv = apduSend(pReader->hCard, APDU_GET_SAM_ID, sizeof(APDU_GET_SAM_ID), pbRecvBuffer, &dwRecvLength); if (rv == SCARD_S_SUCCESS) { sPrintBufferHex(pReader->SAM_id, dwRecvLength, pbRecvBuffer); #if 0 sprintf(messageString, "SAM ID: %s", pReader->SAM_id); readersLogMessage(LOG_INFO, 1, messageString); #endif } else return( rv ); pReader->SAM = TRUE; } /* Turning RATS off thus a JCOP tag will be detected as emulating a DESFIRE */ apduSend(pReader->hCard, APDU_RATS_14443_4_OFF, sizeof(APDU_RATS_14443_4_OFF), pbRecvBuffer, &dwRecvLength); return( SCARD_S_SUCCESS ); }
/**************************** APDU SEND ************************/ static LONG apduSend ( tCardHandle hCard, const BYTE *apdu, DWORD apduLength, BYTE *pbRecvBuffer, DWORD *dwRecvLength ) { LONG rv; SCARD_IO_REQUEST pioRecvPci; BYTE pbSendBuffer[40]; DWORD dwSendLength = 0; DWORD rBufferMax; BOOL psuedoAPDU = FALSE; /* remember the size of the input buffer passed to us, so we don't exceed */ rBufferMax = *dwRecvLength; /* The special psuedo APDU's to talk to a tag, need to have their */ /* response read back in two chunks, using GET_RESPONSE for the second */ if (apdu[0] == 0xd4) { psuedoAPDU = TRUE; /* prepend the DIRECT_TRANSMIT APDU */ memcpy(pbSendBuffer, APDU_DIRECT_TRANSMIT, sizeof(APDU_DIRECT_TRANSMIT)); /* then a byte that tells it the length of the psuedo APDU to follow */ pbSendBuffer[sizeof(APDU_DIRECT_TRANSMIT)] = (BYTE)apduLength; dwSendLength += (sizeof(APDU_DIRECT_TRANSMIT) + 1); } /* Add the APDU that was requested to be sent and increase length to send */ memcpy((pbSendBuffer + dwSendLength), apdu, apduLength); dwSendLength += apduLength; #if 0 sprintf(messageString, "APDU: "); sPrintBufferHex((messageString + strlen("APDU: ")), dwSendLength, pbSendBuffer); readersLogMessage( pManager, LOG_INFO, 3, messageString); #endif rv = SCardTransmit((SCARDHANDLE) hCard, pioSendPci, pbSendBuffer, dwSendLength, &pioRecvPci, pbRecvBuffer, dwRecvLength); /* if it was a psuedo APDU then we need to get the response */ if ( (rv == SCARD_S_SUCCESS) && psuedoAPDU ) { #if 0 sprintf(messageString, "Received: "); sPrintBufferHex((messageString + strlen("Received: ")), *dwRecvLength, pbRecvBuffer); readersLogMessage(LOG_INFO, 3, messageString); #endif /* command went OK? */ if (pbRecvBuffer[SW1] != SW1_SUCCESS) { #if 0 sprintf(messageString, "APDU failed: SW1 = %02X", pbRecvBuffer[SW1]); readersLogMessage(LOG_ERR, 0, messageString); #endif return ( SCARD_F_COMM_ERROR ); } /* are their response bytes to get? */ if (pbRecvBuffer[SW2] > 0) { #if 0 sprintf(messageString, "Requesting Response Data (%d)", pbRecvBuffer[SW2]); readersLogMessage(LOG_INFO, 3, messageString); #endif /* copy the get_response APDU into the first bytes */ memcpy(pbSendBuffer, APDU_GET_RESPONSE, sizeof(APDU_GET_RESPONSE)); /* the second response byte tells us how many bytes are pending */ /* add that value at the end of the GET_RESPONSE APDU */ pbSendBuffer[sizeof(APDU_GET_RESPONSE)] = pbRecvBuffer[SW2]; dwSendLength = sizeof(APDU_GET_RESPONSE) + 1; /* specify the maximum size of the buffer that was passed in */ *dwRecvLength = rBufferMax; rv = SCardTransmit((SCARDCONTEXT)hCard, pioSendPci, pbSendBuffer, dwSendLength, &pioRecvPci, pbRecvBuffer, dwRecvLength ); #if 0 PCSC_ERROR(rv, "SCardTransmit"); sprintf(messageString, "Received: "); sPrintBufferHex(messageString, rBufferMax, pbRecvBuffer); readersLogMessage(LOG_INFO, 3, messageString); #endif } else *dwRecvLength = 0; } return(rv); }