import utils, binascii from generic_card import * class PN532_Virtual_Card(Card): # This is a virtual card that is enabled for the ACS ACR reader that # contains a PN532 module DRIVER_NAME = ["PN532"] STATUS_WORDS = dict(Card.STATUS_WORDS) COMMANDS = dict(Card.COMMANDS) APDU_TRANSCEIVE_PN532 = C_APDU(cla=0xff, ins=0, p1=0, p2=0) # The ACR122 has a maximum response data size of 0xf8 APDU_READ_BINARY = C_APDU(cla=0, ins=0xb0, le=0xf8) def cmd_pn532(self, *cmd): "Transmit a command to the PN532 and receive the response" result = self.pn532_transceive(binascii.unhexlify("".join("".join(cmd).split()))) print utils.hexdump(result.data) parsed = self.pn532_parse(result.data) if len(parsed) > 0: print "\n".join(parsed) + "\n" def cmd_pn532_poll(self): "Poll for cards in the field" self.cmd_pn532("d4 4a 01 00") def pn532_transceive(self, cmd): if len(cmd) > 1: if cmd[0] == "\xd4": s = "pn532_prepare_command_%02X" % ord(cmd[1]) if hasattr(self, s): cmd = getattr(self, s)(cmd) if hasattr(self.reader, "pn532_transceive_raw"): return R_APDU(self.reader.pn532_transceive_raw(cmd)) else: apdu = C_APDU(self.APDU_TRANSCEIVE_PN532, lc=len(cmd), data=cmd) response = self.send_apdu(apdu) return response def pn532_parse(self, response): result = [] type = None cmd = None if len(response) == 0: pass elif response[0] == "\xd4": type="command" elif response[0] == "\xd5": type="response" else: result.append("Invalid PN532 direction header") if len(response) > 1: cmd = ord(response[1]) if type is not None: desc = "PN532 %s (%s)" % (type, self.PN532_COMMANDS.get(cmd & 0xfe, "Unknown command") ) result.append(desc) if len(response) > 1: s = "pn532_parse_%s_%02X" % (type, cmd) if hasattr(self, s): result.extend( getattr(self, s)(response[2:]) ) elif len(response) > 2: result.append( "No detailed decoding available" ) return result def pn532_parse_response_03(self, response): return [ "Version: PN5%02X, firmware %i.%i (cap: %02X)" % tuple(map(ord, response)) ] def pn532_parse_response_05(self, response): result = ["Last error: %02X" % ord(response[0]), "External field: %s" % ( response[1] == "\x01" and "present" or "not present" ), "Number of targets: %i" % ord(response[2])] for i in range( (len(response)-4)/4 ): t = "Target %i: %i kbps receive, %i kbps send, type: %s" % ( ord(response[3+i*4]), self.PN532_BIT_RATES.get( ord(response[3+i*4+1]), "XXX" ), self.PN532_BIT_RATES.get( ord(response[3+i*4+2]), "XXX" ), self.PN532_TAG_TYPES.get( ord(response[3+i*4+3]), "unknown" ), ) result.append(t) result.append( "SAM status: %02X" % ord(response[-1]) ) return result def pn532_prepare_command_4A(self, cmd): if len(cmd) > 3: self._last_baudrate_polled = ord(cmd[3]) else: self._last_baudrate_polled = None return cmd def pn532_parse_response_4B(self, response): response = map(ord, response) numtg = response[0] result = ["Targets detected: %i" % numtg] pos = 1 last_pos = pos while pos < len(response): if self._last_baudrate_polled == 0: s = "Target %i: ISO 14443-A, SENS_RES: %02X %02X, SEL_RES: %02X" % \ ( response[pos], response[pos+1], response[pos+2], response[pos+3] ) pos = pos + 4 if response[pos] > 0: s = s+", NFCID (%02X bytes): %s" % (response[pos], " ".join(map(lambda a: "%02X" % a, response[pos+1:(pos+1+response[pos])]))) pos = pos + response[pos] pos = pos + 1 # NFCID length does not count length byte if len(response) > pos and response[pos] > 0: s = s+", ATS (%02X bytes): %s" % (response[pos], " ".join(map(lambda a: "%02X" % a, response[pos:(pos+response[pos])]))) pos = pos + response[pos] # ATS length does count length byte result.append(s) if last_pos == pos: print "Implementation error, no advance, abort" break return result def can_handle(cls, reader): """Determine whether this class can handle a given reader object.""" if reader.name.startswith("ACS ACR 38U-CCID"): return True return False can_handle = classmethod(can_handle) STATUS_WORDS.update( { '\x63\x00': "Operation failed", '\x63\x01': "PN532 did not respond", '\x63\x27': "PN532 response checksum wrong", '\x63\x7f': "PN532 command wrong", } ) COMMANDS.update( { "pn532": cmd_pn532, "pn532_poll": cmd_pn532_poll, } ) PN532_COMMANDS = { 0x00: "Diagnose", 0x02: "GetFirmwareVersion", 0x04: "GetGeneralStatus", 0x06: "ReadRegister", 0x08: "WriteRegister", 0x0c: "ReadGPIO", 0x0e: "WriteGPIO", 0x10: "SetSerialBaudrate", 0x12: "SetParameters", 0x14: "SAMConfiguration", 0x16: "PowerDown", 0x32: "RFConfiguration", 0x58: "RFRegulationTest", 0x56: "InJumpForDEP", 0x46: "InJumpForPSL", 0x4A: "InListPassiveTarget", 0x50: "InATR", 0x4E: "InPSL", 0x40: "InDataExchange", 0x42: "InCommunicateThru", 0x44: "InDeselect", 0x52: "InRelease", 0x54: "InSelect", 0x60: "InPoll", 0x8C: "TgInitAsTarget", 0x92: "TgSetGeneralBytes", 0x86: "TgGetData", 0x8E: "TgSetData", 0x94: "TgSetMetaData", 0x88: "TgGetInitiatorCommand", 0x90: "TgResponseToInitiator", 0x8A: "TgGetTargetStatus", } PN532_BIT_RATES = { 0x0: 106, 0x1: 212, 0x2: 424, } PN532_TAG_TYPES = { 0x00: "Mifare, ISO 14443-3 A/B or ISO 18092 passive 106 kbps", 0x10: "FeliCa or ISO 18092 passive 212/424 kbps", 0x01: "ISO 18092 active", 0x02: "Innovision Jewel", }