# -*- coding: latin-1 -*- # ----------------------------------------------------------------------------- # Copyright 2009, 2017 Stephen Tiedemann <stephen.tiedemann@gmail.com> # # Licensed under the EUPL, Version 1.1 or - as soon they # will be approved by the European Commission - subsequent # versions of the EUPL (the "Licence"); # You may not use this work except in compliance with the # Licence. # You may obtain a copy of the Licence at: # # https://joinup.ec.europa.eu/software/page/eupl # # Unless required by applicable law or agreed to in # writing, software distributed under the Licence is # distributed on an "AS IS" basis, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either # express or implied. # See the Licence for the specific language governing # permissions and limitations under the Licence. # ----------------------------------------------------------------------------- """This is not really a device driver but a base module that implements common functionality for the PN53x family of contactless interface chips, namely the NXP PN531, PN532, PN533 and the Sony RC-S956. """ import nfc.clf from . import device import os import time import errno from binascii import hexlify from struct import pack, unpack import logging log = logging.getLogger(__name__) class Chipset(object): SOF = bytearray.fromhex('0000FF') ACK = bytearray.fromhex('0000FF00FF00') REG = { 0x6331: "CIU_Command", 0x6332: "CIU_CommIEn", 0x6333: "CIU_DivIEn", 0x6334: "CIU_CommIRq", 0x6335: "CIU_DivIRq", 0x6336: "CIU_Error", 0x6337: "CIU_Status1", 0x6338: "CIU_Status2", 0x6339: "CIU_FIFOData", 0x633A: "CIU_FIFOLevel", 0x633B: "CIU_WaterLevel", 0x633C: "CIU_Control", 0x633D: "CIU_BitFraming", 0x633E: "CIU_Coll", 0x6301: "CIU_Mode", 0x6302: "CIU_TxMode", 0x6303: "CIU_RxMode", 0x6304: "CIU_TxControl", 0x6305: "CIU_TxAuto", 0x6306: "CIU_TxSel", 0x6307: "CIU_RxSel", 0x6308: "CIU_RxThreshold", 0x6309: "CIU_Demod", 0x630A: "CIU_FelNFC1", 0x630B: "CIU_FelNFC2", 0x630C: "CIU_MifNFC", 0x630D: "CIU_ManualRCV", 0x630E: "CIU_TypeB", 0x630F: "CIU_SerialSpeed", 0x6311: "CIU_CRCResultMSB", 0x6312: "CIU_CRCResultLSB", 0x6313: "CIU_GsNOff", 0x6314: "CIU_ModWidth", 0x6315: "CIU_TxBitPhase", 0x6316: "CIU_RFCfg", 0x6317: "CIU_GsNOn", 0x6318: "CIU_CWGsP", 0x6319: "CIU_ModGsP", 0x631A: "CIU_TMode", 0x631B: "CIU_TPrescaler", 0x631C: "CIU_TReloadHi", 0x631D: "CIU_TReloadLo", 0x631E: "CIU_TCounterHi", 0x631F: "CIU_TCounterLo", 0x6321: "CIU_TestSel1", 0x6322: "CIU_TestSel2", 0x6323: "CIU_TestPinEn", 0x6324: "CIU_TestPinValue", 0x6325: "CIU_TestBus", 0x6326: "CIU_AutoTest", 0x6327: "CIU_Version", 0x6328: "CIU_AnalogTest", 0x6329: "CIU_TestDAC1", 0x632A: "CIU_TestDAC2", 0x632B: "CIU_TestADC", 0x632C: "CIU_RFT1", 0x632D: "CIU_RFT2", 0x632E: "CIU_RFT3", 0x632F: "CIU_RFT4", } REGBYNAME = {v: k for k, v in REG.items()} class Error(Exception): def __init__(self, errno, strerr): self.errno, self.strerr = errno, strerr def __str__(self): return "Error 0x{0:02X}: {1}".format(self.errno, self.strerr) def chipset_error(self, cause): if cause is None: errno = 0xff elif type(cause) is int: errno = cause else: errno = cause[0] strerr = self.ERR.get(errno, "Unknown error code") raise Chipset.Error(errno, strerr) def __init__(self, transport, logger): self.transport = transport self.log = logger def close(self): self.transport.close() self.transport = None def command(self, cmd_code, cmd_data, timeout): """Send a host command and return the chip response. The chip command is selected by the 8-bit integer *cmd_code*. The command parameters, if any, are supplied with *cmd_data* as a bytearray or byte string. The fully constructed command frame is sent with :meth:`write_frame` and the chip acknowledgement and response is received with :meth:`read_frame`, those methods are used by some drivers for additional framing. The implementation waits 100 ms for the command acknowledgement and then polls every 100 ms for a response frame until *timeout* seconds have elapsed. If the response frame is correct and the response code matches *cmd_code* the data bytes that follow the response code are returned as a bytearray (without the trailing checksum and postamble). **Exceptions** * :exc:`~exceptions.IOError` :const:`errno.ETIMEDOUT` if no response frame was received before *timeout* seconds. * :exc:`~exceptions.IOError` :const:`errno.EIO` if response frame errors were detected. * :exc:`Chipset.Error` if an error response frame or status error was received. """ if cmd_data is not None: assert len(cmd_data) <= self.host_command_frame_max_size - 2 self.log.log(logging.DEBUG-1, "{} {} {:.3f}".format( self.CMD[cmd_code], hexlify(cmd_data).decode(), timeout)) if len(cmd_data) < 254: head = self.SOF + bytearray([len(cmd_data)+2]) \ + bytearray([254-len(cmd_data)]) else: head = self.SOF + b'\xFF\xFF' + pack(">H", len(cmd_data)+2) head.append((256 - sum(head[-2:])) & 0xFF) data = bytearray([0xD4, cmd_code]) + cmd_data tail = bytearray([(256 - sum(data)) & 0xFF, 0]) try: self.write_frame(head + data + tail) frame = self.read_frame(timeout=100) except IOError: self.log.error("input/output error while waiting for ack") raise IOError(errno.EIO, os.strerror(errno.EIO)) if not frame.startswith(self.SOF): self.log.error("invalid frame start sequence") raise IOError(errno.EIO, os.strerror(errno.EIO)) if frame[0:len(self.ACK)] != self.ACK: self.log.warning("missing ack frame") else: frame = self.ACK if timeout is not None and timeout <= 0: return while frame == self.ACK: try: frame = self.read_frame(int(1000 * timeout)) except IOError as error: if error.errno == errno.ETIMEDOUT: self.write_frame(self.ACK) # cancel command time.sleep(0.001) raise error if frame.startswith(self.SOF + b'\xFF\xFF'): # extended frame if sum(frame[5:8]) & 0xFF != 0: self.log.error("frame lenght checksum error") raise IOError(errno.EIO, os.strerror(errno.EIO)) if unpack(">H", memoryview(frame[5:7]))[0] != len(frame) - 10: self.log.error("frame lenght value mismatch") raise IOError(errno.EIO, os.strerror(errno.EIO)) del frame[0:8] elif frame.startswith(self.SOF): # normal frame if sum(frame[3:5]) & 0xFF != 0: self.log.error("frame lenght checksum error") raise IOError(errno.EIO, os.strerror(errno.EIO)) if frame[3] != len(frame) - 7: self.log.error("frame lenght value mismatch") raise IOError(errno.EIO, os.strerror(errno.EIO)) del frame[0:5] else: self.log.debug("invalid frame start sequence") raise IOError(errno.EIO, os.strerror(errno.EIO)) if not sum(frame) & 0xFF == 0: self.log.error("frame data checksum error") raise IOError(errno.EIO, os.strerror(errno.EIO)) if frame[0] == 0x7F: # error frame self.chipset_error(0x7F) if not frame[0] == 0xD5: self.log.error("invalid frame identifier") raise IOError(errno.EIO, os.strerror(errno.EIO)) if not frame[1] == cmd_code + 1: self.log.error("unexpected response code") raise IOError(errno.EIO, os.strerror(errno.EIO)) return frame[2:-2] def write_frame(self, frame): """Write a command *frame* to the chipset.""" self.transport.write(frame) def read_frame(self, timeout): """Wait *timeout* milliseconds to return a chip response frame.""" return self.transport.read(timeout) def send_ack(self): # Send an ACK frame, usually to terminate most recent command. self.transport.write(Chipset.ACK) def diagnose(self, test, test_data=None): """Send a Diagnose command. The *test* argument selects the diagnose function either by number or the string ``line``, ``rom``, or ``ram``. For a ``line`` test the implementation sends the longest possible command frame and verifies that the response data is identical. For a ``ram`` or ``rom`` test the implementation verfies the response status. For a *test* number the implementation appends the byte string *test_data* and returns the response data bytes. """ if test == "line": size = self.host_command_frame_max_size - 3 data = b'\x00' + bytearray([x & 0xFF for x in range(size)]) return self.command(0x00, data, timeout=1.0) == data if test == "rom": data = self.command(0x00, b'\x01', timeout=1.0) return data and data[0] == 0 if test == "ram": data = self.command(0x00, b'\x02', timeout=1.0) return data and data[0] == 0 return self.command(0x00, pack('B', test) + test_data, timeout=1.0) def get_firmware_version(self): """Send a GetFirmwareVersion command and return the response data bytes. """ return self.command(0x02, b'', timeout=0.1) def get_general_status(self): """Send a GetGeneralStatus command and return the response data bytes. """ data = self.command(0x04, b'', timeout=0.1) if data is None or len(data) < 3: raise self.chipset_error(None) return data def read_register(self, *args): """Send a ReadRegister command for the positional register address or name arguments. The register values are returned as a list for multiple arguments or an integer for a single argument. :: tx_mode = Chipset.read_register(0x6302) rx_mode = Chipset.read_register("CIU_RxMode") tx_mode, rx_mode = Chipset.read_register("CIU_TxMode", "CIU_RxMode") """ def addr(r): return self.REGBYNAME[r] if type(r) is str else r args = [addr(reg) for reg in args] data = b''.join([pack(">H", reg) for reg in args]) data = self._read_register(data) return list(data) if len(data) > 1 else data[0] def _read_register(self, data): cname = self.__class__.__module__ + '.' + self.__class__.__name__ raise NotImplementedError(cname + "._read_register") def write_register(self, *args): """Send a WriteRegister command. Each positional argument must be an (address, value) tuple except if exactly two arguments are supplied as register address and value. A register can also be selected by name. There is no return value. :: Chipset.write_register(0x6301, 0x00) Chipset.write_register("CIU_Mode", 0x00) Chipset.write_register((0x6301, 0x00), ("CIU_TxMode", 0x00)) """ def addr(r): return self.REGBYNAME[r] if type(r) is str else r assert type(args) in (tuple, list) if len(args) == 2 and type(args[1]) == int: args = [args] args = [(addr(reg), val) for reg, val in args] data = b''.join([pack(">HB", reg, val) for reg, val in args]) self._write_register(data) def _write_register(self, data): cname = self.__class__.__module__ + '.' + self.__class__.__name__ raise NotImplementedError(cname + "._write_register") def set_parameters(self, flags): """Send a SetParameters command with the 8-bit *flags* integer.""" self.command(0x12, bytearray([flags]), timeout=0.1) def rf_configuration(self, cfg_item, cfg_data): """Send an RFConfiguration command.""" self.command(0x32, bytearray([cfg_item]) + bytearray(cfg_data), timeout=0.1) def in_jump_for_dep(self, act_pass, br, passive_data, nfcid3, gi): """Send an InJumpForDEP command. """ assert act_pass in (False, True) assert br in (106, 212, 424) assert len(passive_data) in (0, 4, 5) assert len(nfcid3) in (0, 10) assert len(gi) <= 48 cm = int(bool(act_pass)) br = (106, 212, 424).index(br) nf = (bool(passive_data) | bool(nfcid3) << 1 | bool(gi) << 2) data = bytearray([cm, br, nf]) + passive_data + nfcid3 + gi data = self.command(0x56, bytearray(data), timeout=3.0) if data is None or data[0] != 0: self.chipset_error(data) return data[2:] def in_jump_for_psl(self, act_pass, br, passive_data, nfcid3, gi): """Send an InJumpForPSL command. """ assert act_pass in (False, True) assert br in (106, 212, 424) assert len(passive_data) in (0, 4, 5) assert len(nfcid3) in (0, 10) assert len(gi) <= 48 cm = int(bool(act_pass)) br = (106, 212, 424).index(br) nf = (bool(passive_data) | bool(nfcid3) << 1 | bool(gi) << 2) data = bytearray([cm, br, nf]) + passive_data + nfcid3 + gi data = self.command(0x46, data, timeout=3.0) if data is None or data[0] != 0: self.chipset_error(data) return data[2:] def in_list_passive_target(self, max_tg, brty, initiator_data): assert max_tg <= self.in_list_passive_target_max_target assert brty in self.in_list_passive_target_brty_range data = bytearray([1, brty]) + initiator_data data = self.command(0x4A, data, timeout=1.0) return data[2:] if data and data[0] > 0 else None def in_atr(self, nfcid3i=b'', gi=b''): flag = int(bool(nfcid3i)) | (int(bool(gi)) << 1) data = bytearray([1, flag]) + nfcid3i + gi data = self.command(0x50, data, timeout=1.5) if data is None or data[0] != 0: self.chipset_error(data) return data[1:] def in_psl(self, br_it, br_ti): data = bytearray([1, br_it, br_ti]) data = self.command(0x4E, data, timeout=1.0) if data is None or data[0] != 0: self.chipset_error(data) def in_data_exchange(self, data, timeout, more=False): data = self.command(0x40, bytearray([int(more) << 6 | 0x01]) + data, timeout) if data is None or data[0] & 0x3f != 0: self.chipset_error(data[0] & 0x3f if data else None) return data[1:], bool(data[0] & 0x40) def in_communicate_thru(self, data, timeout): data = self.command(0x42, data, timeout) if timeout > 0: if data and data[0] == 0: return data[1:] else: self.chipset_error(data) def tg_set_general_bytes(self, gb): data = self.command(0x92, gb, timeout=0.1) if data is None or data[0] != 0: self.chipset_error(data) def tg_get_data(self, timeout): data = self.command(0x86, b'', timeout) if data is None or data[0] & 0x3f != 0: self.chipset_error(data[0] & 0x3f if data else None) return data[1:], bool(data[0] & 0x40) def tg_set_data(self, data, timeout): data = self.command(0x8E, data, timeout) if data is None or data[0] != 0: self.chipset_error(data) def tg_set_meta_data(self, data, timeout): data = self.command(0x94, data, timeout) if data is None or data[0] != 0: self.chipset_error(data) def tg_get_initiator_command(self, timeout): data = self.command(0x88, b'', timeout) if timeout > 0: if data and data[0] == 0: return data[1:] else: self.chipset_error(data) def tg_response_to_initiator(self, data): data = self.command(0x90, data, timeout=1.0) if data is None or data[0] != 0: self.chipset_error(data) def tg_get_target_status(self): data = self.command(0x8A, b'', timeout=0.1) if data[0] == 0x01: br_tx = (106, 212, 424)[data[1] >> 4 & 7] br_rx = (106, 212, 424)[data[1] & 7] else: br_tx, br_rx = (0, 0) return data[0], br_tx, br_rx class Device(device.Device): # Base class for devices with an NXP PN531, PN532, PN533 or Sony # RC-S956 contactless interface chip. This class implements the # functionality that is identical or needed by most of the drivers # that inherit from pn53x. def __init__(self, chipset, logger): self.chipset = chipset self.log = logger try: chipset_communication = self.chipset.diagnose('line') except Chipset.Error: chipset_communication = False if chipset_communication is False: self.log.error("chipset communication test failed") raise IOError(errno.EIO, os.strerror(errno.EIO)) # for line in self._print_ciu_register_page(0, 1, 2, 3): # self.log.debug(line) # for addr in range(0, 0x03FF, 16): # xram = self.chipset.read_register(*range(addr, addr+16)) # xram = ' '.join(["%02X" % x for x in xram]) # self.log.debug("0x%04X: %s", addr, xram) def close(self): self.chipset.close() self.chipset = None def mute(self): self.chipset.rf_configuration(0x01, bytearray([0b00000010])) def sense_tta(self, target): brty = {"106A": 0}.get(target.brty) if brty not in self.chipset.in_list_passive_target_brty_range: message = "unsupported bitrate {0}".format(target.brty) self.log.warning(message) raise ValueError(message) uid = target.sel_req if target.sel_req else bytearray() if len(uid) > 4: uid = b'\x88' + uid if len(uid) > 8: uid = uid[0:4] + b'\x88' + uid[4:] rsp = self.chipset.in_list_passive_target(1, 0, uid) if rsp is not None: sens_res, sel_res, sdd_res = rsp[1::-1], rsp[2:3], rsp[4:] if sel_res[0] & 0x60 == 0x00: self.log.debug("disable crc check for type 2 tag") rxmode = self.chipset.read_register("CIU_RxMode") self.chipset.write_register("CIU_RxMode", rxmode & 0x7F) return nfc.clf.RemoteTarget( "106A", sens_res=sens_res, sel_res=sel_res, sdd_res=sdd_res) if self.chipset.read_register("CIU_FIFOData") == 0x26: # If we still see the SENS_REQ command in the CIU FIFO # then there was no SENS_RES, thus no tag present. return None self.log.debug("sens_res but no sdd_res, try as type 1 tag") if 4 not in self.chipset.in_list_passive_target_brty_range: self.log.warning("The {0} can not read Type 1 Tags.".format(self)) return None rsp = self.chipset.in_list_passive_target(1, 4, b"") if rsp is not None: rid_cmd = bytearray.fromhex("78 0000 00000000") try: rid_res = self.chipset.in_data_exchange(rid_cmd, 0.01)[0] return nfc.clf.RemoteTarget( "106A", sens_res=rsp[1::-1], rid_res=rid_res) except Chipset.Error: pass def sense_ttb(self, target, did=None): brty = {"106B": 3, "212B": 6, "424B": 7, "848B": 8}.get(target.brty) if brty not in self.chipset.in_list_passive_target_brty_range: message = "unsupported bitrate {0}".format(target.brty) self.log.warning(message) raise ValueError(message) afi = target.sensb_req[0:1] if target.sensb_req else b'\x00' rsp = self.chipset.in_list_passive_target(1, brty, afi) if rsp and rsp[10] & 0b00001001 == 0b00000001: # This is an ISO tag and the chipset has now activated it # with 64-byte max frame size and maybe a DID. Because we # implement ISO-DEP in software and can do without DID and # use a full 256 byte response frame size, we'll send a # DESELECT and WUPB to allow ATTRIB from the activation # code in tags/tt4.py. try: deselect_command = (b'\xCA' + did) if did else b'\xC2' wupb_command = b'\x05' + afi + b'\x08' self.chipset.in_communicate_thru(deselect_command, 0.5) rsp = self.chipset.in_communicate_thru(wupb_command, 0.5) return nfc.clf.RemoteTarget(target.brty, sensb_res=rsp) except (Chipset.Error, IOError) as error: self.log.debug(error) def sense_ttf(self, target): brty = {"212F": 1, "424F": 2}.get(target.brty) if brty not in self.chipset.in_list_passive_target_brty_range: message = "unsupported bitrate {0}".format(target.brty) self.log.warning(message) raise ValueError(message) if not self.chipset.read_register("CIU_TxControl") & 0b00000011: # Some FeliCa cards need more time from power up to # polling. If the field was not already activated, do this # now and wait about 5 ms. self.chipset.rf_configuration(0x01, b'\x01') time.sleep(0.005) default_sensf_req = bytearray.fromhex("00FFFF0100") sensf_req = target.sensf_req if target.sensf_req else default_sensf_req rsp = self.chipset.in_list_passive_target(1, brty, sensf_req) if rsp is not None: return nfc.clf.RemoteTarget(target.brty, sensf_res=rsp[1:]) def sense_dep(self, target): # Attempt active communication mode target activation. assert target.atr_req, "the target.atr_req attribute is required" assert len(target.atr_req) >= 16, "minimum lenght of atr_req is 16" assert len(target.atr_req) <= 64, "maximum lenght of atr_req is 64" # bitrate and modulation type for send/recv must be set and equal assert target.brty_send and target.brty_recv assert target.brty_send == target.brty_recv br = int(target.brty[0:-1]) nfcid3 = target.atr_req[2:12] gbytes = target.atr_req[16:] try: data = self.chipset.in_jump_for_psl(1, br, b'', nfcid3, gbytes) atr_res = b'\xD5\x01' + data except Chipset.Error as error: if error.errno not in (0x01, 0x0A): self.log.error(error) return None finally: # unset the detect-sync bit, 106A sync byte is handled in dep.py self.chipset.write_register("CIU_Mode", 0b00111011) self.log.debug("running DEP in {0} kbps active mode".format(br)) return nfc.clf.RemoteTarget(target.brty, atr_res=atr_res, atr_req=target.atr_req) def get_max_send_data_size(self, target): return self.chipset.host_command_frame_max_size - 2 def get_max_recv_data_size(self, target): return self.chipset.host_command_frame_max_size - 3 def send_cmd_recv_rsp(self, target, data, timeout): def bitrate(brty): return [106 << i for i in range(6)].index(int(brty[:-1])) def framing(brty): return {'A': 0b00, 'B': 0b11, 'F': 0b10}[brty[-1:]] # Set bitrate and modulation type for send and receive. acm = target.atr_res and not (target.sens_res or target.sensf_res) reg = ("CIU_TxMode", "CIU_RxMode", "CIU_TxAuto") txm, rxm, txa = self.chipset.read_register(*reg) txm = (txm & 0b10001111) | (bitrate(target.brty_send) << 4) rxm = (rxm & 0b10001111) | (bitrate(target.brty_recv) << 4) txm = (txm & 0b11111100) | (0b01 if acm else framing(target.brty_send)) rxm = (rxm & 0b11111100) | (0b01 if acm else framing(target.brty_recv)) txa = (txa & 0b10111111) | (target.brty_send.endswith("A") << 6) reg = (("CIU_TxMode", txm), ("CIU_RxMode", rxm), ("CIU_TxAuto", txa)) self.chipset.write_register(*reg) # Calculate the timeout index for InCommunicateThru. The # effective timeout is T(us) = 100 * 2**(n-1) for 1 <= n <= 16 # and "no timeout" for n = 0. For a given timeout we calculate # the index as the first effective timeout that is longer. timeout_microsec = int(timeout * 1E6) try: index = [i+1 for i in range(16) if timeout_microsec >> i <= 100][0] except IndexError: index = 16 timeout_microsec = 100 << (index-1) timeout = (100 << (index-1)) / 1E6 self.log.log(logging.DEBUG-1, "set response timeout %.6f sec", timeout) self.chipset.rf_configuration(0x02, bytearray([10, 11, index])) # Send the command data and return the response. All cases # where a response is not received raise either an IOError # or one of the nfc.clf.CommunicationError specializations. data = bytearray(data) if not isinstance(data, bytearray) else data try: if target.sens_res and not target.atr_res: if target.rid_res: # TT1 return self._tt1_send_cmd_recv_rsp(data, timeout+0.1) if target.sel_res[0] & 0x60 == 0x00: # TT2 return self._tt2_send_cmd_recv_rsp(data, timeout+0.1) return self.chipset.in_communicate_thru(data, timeout+0.1) except Chipset.Error as error: self.log.debug(error) if error.errno == 1: raise nfc.clf.TimeoutError else: raise nfc.clf.TransmissionError(str(error)) except IOError as error: self.log.debug(error) if not error.errno == errno.ETIMEDOUT: raise error else: raise nfc.clf.TimeoutError("send_cmd_recv_rsp") def _tt1_send_cmd_recv_rsp(self, data, timeout): cname = self.__class__.__module__ + '.' + self.__class__.__name__ raise NotImplementedError(cname + "._tt1_send_cmd_recv_rsp()") def _tt2_send_cmd_recv_rsp(self, data, timeout): # The Type2Tag implementation needs to receive the Mifare # ACK/NAK responses but the chipset reports them as crc error # (indistinguishable from a real crc error). We thus have to # switch off the crc check and do it here. data = self.chipset.in_communicate_thru(data, timeout) if len(data) > 2 and self.check_crc_a(data) is False: raise nfc.clf.TransmissionError("crc_a check error") return data[:-2] if len(data) > 2 else data def listen_tta(self, target, timeout): if target.brty != "106A": info = "unsupported bitrate/type: %r" % target.brty raise nfc.clf.UnsupportedTargetError(info) if target.rid_res: info = "listening for type 1 tag activation is not supported" raise nfc.clf.UnsupportedTargetError(info) try: assert target.sens_res is not None, "sens_res is required" assert target.sdd_res is not None, "sdd_res is required" assert target.sel_res is not None, "sel_res is required" assert len(target.sens_res) == 2, "sens_res must be 2 byte" assert len(target.sdd_res) == 4, "sdd_res must be 4 byte" assert len(target.sel_res) == 1, "sel_res must be 1 byte" assert target.sdd_res[0] == 0x08, "sdd_res[0] must be 08h" except AssertionError as error: raise ValueError(str(error)) nfcf_params = bytearray(range(18)) nfca_params = target.sens_res + target.sdd_res[1:4] + target.sel_res self.log.debug("nfca_params %s", hexlify(nfca_params).decode()) # We can use TgInitAsTarget to exclusively answer Type A # activation when the CIU automatic mode detector is disabled # (the firmware does not unset or even check this bit). When # TgInitAsTarget prepares for AutoColl, the firmware also sets # the CIU_TxMode and CIU_RXMode to 106A. self.chipset.write_register("CIU_Mode", 0b00111111) time_to_return = time.time() + timeout while time.time() < time_to_return: try: wait = max(time_to_return - time.time(), 0.5) args = (1, nfca_params, nfcf_params, wait) data = self._init_as_target(*args) except IOError as error: if error.errno != errno.ETIMEDOUT: raise error else: return None brty = ("106A", "212F", "424F")[(data[0] & 0x70) >> 4] self.log.debug("%s rcvd %s", brty, hexlify(memoryview(data)[1:]).decode()) if brty != target.brty or len(data) < 2: log.debug("received bitrate does not match %s", target.brty) continue if target.sel_res[0] & 0x60 == 0x00: self.log.debug("rcvd TT2_CMD %s", hexlify(memoryview(data)[1:]).decode()) target = nfc.clf.LocalTarget(brty, tt2_cmd=data[1:]) target.sens_res = nfca_params[0:2] target.sdd_res = b'\x08' + nfca_params[2:5] target.sel_res = nfca_params[5:6] return target elif target.sel_res[0] & 0x20 == 0x20 and data[1] == 0xE0: default_rats_res = bytearray.fromhex("05 78 80 70 02") (rats_cmd, rats_res) = (data[1:], target.rats_res) if not rats_res: rats_res = default_rats_res self.log.debug("rcvd RATS_CMD %s", hexlify(rats_cmd).decode()) self.log.debug("send RATS_RES %s", hexlify(rats_res).decode()) try: self.chipset.tg_response_to_initiator(rats_res) data = self.chipset.tg_get_initiator_command(1.0) except (Chipset.Error, IOError) as error: self.log.error(error) return if data and data[0] & 0xF0 == 0xC0: # S(DESELECT) self.log.debug("rcvd S(DESELECT) %s", hexlify(data).decode()) self.log.debug("send S(DESELECT) %s", hexlify(data).decode()) self.chipset.tg_response_to_initiator(data) elif data: self.log.debug("rcvd TT4_CMD %s", hexlify(data).decode()) target = nfc.clf.LocalTarget(brty, tt4_cmd=data) target.sens_res = nfca_params[0:2] target.sdd_res = b'\x08' + nfca_params[2:5] target.sel_res = nfca_params[5:6] return target elif (target.sel_res[0] & 0x40 and data[1] == 0xF0 and len(data) >= 19 and data[2] == len(data)-2 and data[3:5] == b'\xD4\x00'): self.log.debug("rcvd ATR_REQ %s", hexlify(memoryview(data)[3:]).decode()) target = nfc.clf.LocalTarget(brty, atr_req=data[3:]) target.sens_res = nfca_params[0:2] target.sdd_res = b'\x08' + nfca_params[2:5] target.sel_res = nfca_params[5:6] return target def listen_ttf(self, target, timeout): # For NFC-F listen we can not use TgInitAsTarget because it # always sets CIU_TxMode and CIU_RxMode to 106A. Best we can # do is to program the CIU AutoColl command and then work with # the CIU to receive tag commands in _tt3_send_rsp_recv_cmd # (InCommunicateThru does not work probably because the # firmware is not in target state). With the 64-bit only CIU # FIFO it means that a tag can only allow two blocks for read # and write. if target.brty not in ("212F", "424F"): info = "unsupported bitrate/type: %r" % target.brty raise nfc.clf.UnsupportedTargetError(info) try: assert target.sensf_res is not None, "sensf_res is required" assert len(target.sensf_res) == 19, "sensf_res must be 19 byte" except AssertionError as error: raise ValueError(str(error)) nfca_params = bytearray(6) nfcf_params = bytearray(target.sensf_res[1:]) self.log.debug("nfcf_params %s", hexlify(nfcf_params).decode()) regs = [ ("CIU_Command", 0b00000000), # Idle command ("CIU_FIFOLevel", 0b10000000), # clear fifo ] regs.extend(zip(25*["CIU_FIFOData"], nfca_params + nfcf_params + b"\0")) regs.append(("CIU_Command", 0b00000001)) # Configure command self.chipset.write_register(*regs) regs = [ ("CIU_Control", 0b00000000), # act as target (b4=0) ("CIU_Mode", 0b00111111), # disable mode detector (b2=1) ("CIU_FelNFC2", 0b10000000), # wait until selected (b7=1) ("CIU_TxMode", 0b10000010 | (int(target.brty[:-1])//212) << 4), ("CIU_RxMode", 0b10001010 | (int(target.brty[:-1])//212) << 4), ("CIU_TxControl", 0b10000000), # disable output on TX1/TX2 ("CIU_TxAuto", 0b00100000), # wake up when rf level detected ("CIU_Demod", 0b01100001), # use Q channel, freeze PLL in recv ("CIU_CommIRq", 0b01111111), # clear interrupt request bits ("CIU_DivIRq", 0b01111111), # clear interrupt request bits ("CIU_Command", 0b00001101), # AutoColl command ] self.chipset.write_register(*regs) regs = ("CIU_Status1", "CIU_Status2", "CIU_CommIRq", "CIU_DivIRq") time_to_return = time.time() + timeout while time.time() < time_to_return: time.sleep(0.01) status1, status2, commirq, divirq \ = self.chipset.read_register(*regs) if commirq & 0b00110000 == 0b00110000: self.chipset.write_register("CIU_CommIRq", 0b00110000) fifo_size = self.chipset.read_register("CIU_FIFOLevel") fifo_read = fifo_size * ["CIU_FIFOData"] fifo_data = bytearray(self.chipset.read_register(*fifo_read)) if fifo_data and len(fifo_data) == fifo_data[0]: self.log.debug("%s rcvd %s", target.brty, hexlify(fifo_data).decode()) if fifo_data[2:10] == nfcf_params[0:8]: target = nfc.clf.LocalTarget(target.brty) target.sensf_res = b'\x01' + nfcf_params target.tt3_cmd = fifo_data[1:] return target # Restart the AutoColl command. self.chipset.write_register("CIU_Command", 0b00001101) self.chipset.write_register("CIU_Command", 0) # Idle command def listen_dep(self, target, timeout): assert target.sensf_res is not None assert target.sens_res is not None assert target.sdd_res is not None assert target.sel_res is not None assert target.atr_res is not None nfca_params = target.sens_res + target.sdd_res[1:4] + target.sel_res nfcf_params = target.sensf_res[1:19] self.log.debug("nfca_params %s", hexlify(nfca_params).decode()) self.log.debug("nfcf_params %s", hexlify(nfcf_params).decode()) assert len(nfca_params) == 6 assert len(nfcf_params) == 18 # enable the automatic mode detector (b2 <= 0) self.chipset.write_register( ("CIU_Mode", 0b01111011), # b2 - enable mode detector ("CIU_TxMode", 0b10110000), # 848 kbps Type A framing ("CIU_RxMode", 0b10110000)) # 848 kbps Type A framing time_to_return = time.time() + timeout while time.time() < time_to_return: try: wait = max(time_to_return - time.time(), 0.5) data = self._init_as_target(2, nfca_params, nfcf_params, wait) except IOError as error: if error.errno != errno.ETIMEDOUT: raise error else: if not (data[1] == len(data)-1 and data[2:4] == b'\xD4\x00'): self.log.debug("expected ATR_REQ but got %s", hexlify(memoryview(data)[1:]).decode()) else: break else: return brty = ("106A", "212F", "424F")[(data[0] & 0b01110000) >> 4] mode = ("passive", "active")[data[0] & 1] self.log.debug("activated in %s %s communication mode", brty, mode) atr_req = data[2:] atr_res = target.atr_res[:] atr_res[12] = atr_req[12] # copy DID activation_params = ((nfca_params if brty == "106A" else nfcf_params) if mode == "passive" else None) try: self.log.debug("%s send ATR_RES %s", brty, hexlify(atr_res).decode()) data = self._send_atr_response(atr_res, timeout=1.0) except Chipset.Error as error: self.log.error(error) return except IOError as error: if error.errno != errno.ETIMEDOUT: raise self.log.debug(error) return psl_req = psl_res = None if data and data.startswith(b'\x06\xD4\x04'): self.log.debug("%s rcvd PSL_REQ %s", brty, hexlify(memoryview(data)[1:]).decode()) try: psl_req = data[1:] assert len(psl_req) == 5, "psl_req length mismatch" assert psl_req[2] == atr_req[12], "psl_req has wrong did" except AssertionError as error: log.debug(str(error)) return None try: psl_res = b'\xD5\x05' + psl_req[2:3] self.log.debug("%s send PSL_RES %s", brty, hexlify(psl_res).decode()) brty = self._send_psl_response(psl_req, psl_res, timeout=0.5) data = self.chipset.tg_get_initiator_command(timeout) except Chipset.Error as error: self.log.error(error) return except IOError as error: if error.errno != errno.ETIMEDOUT: raise self.log.debug(error) return if data and data[0] == len(data) and data[1:3] == b'\xD4\x06': # set detect-sync bit to 0, the 106A sync byte is handled by dep.py self.chipset.write_register("CIU_Mode", 0b00111011) # prepare the target description to return, exact content # depends on how we were activated (A or F with or w/o PSL) target = nfc.clf.LocalTarget(brty, dep_req=data[1:]) target.atr_req, target.atr_res = atr_req, atr_res if psl_req: target.psl_req = psl_req if psl_res: target.psl_res = psl_res if activation_params == nfca_params: target.sens_res = nfca_params[0:2] target.sdd_res = b'\x08' + nfca_params[2:5] target.sel_res = nfca_params[5:6] if activation_params == nfcf_params: target.sensf_res = b'\x01' + nfcf_params return target def _init_as_target(self, mode, tta_params, ttf_params, timeout): cname = self.__class__.__module__ + '.' + self.__class__.__name__ raise NotImplementedError(cname + '._init_as_target()') def _send_atr_response(self, atr_res, timeout): self.chipset.tg_response_to_initiator( bytearray([len(atr_res)+1]) + atr_res) return self.chipset.tg_get_initiator_command(timeout) def _send_psl_response(self, psl_req, psl_res, timeout): dsi = psl_req[3] >> 3 & 0b111 dri = psl_req[3] & 0b111 rx_mode = self.chipset.read_register("CIU_RxMode") rx_mode = (rx_mode & 0b10001111) | (dsi << 4) if rx_mode & 0b00000011 != 1: # if not active mode rx_mode = (rx_mode & 0b11111100) | ((0, 2)[dsi > 0]) self.log.debug("set CIU_RxMode to {:08b}".format(rx_mode)) self.chipset.write_register(("CIU_RxMode", rx_mode)) self.log.debug("send PSL_RES %s", hexlify(psl_res).decode()) data = bytearray([len(psl_res)+1]) + psl_res self.chipset.tg_response_to_initiator(data) tx_mode = self.chipset.read_register("CIU_TxMode") tx_mode = (tx_mode & 0b10001111) | (dri << 4) if tx_mode & 0b00000011 != 1: # if not active mode tx_mode = (tx_mode & 0b11111100) | ((0, 2)[dri > 0]) self.log.debug("set CIU_TxMode to {:08b}".format(tx_mode)) self.chipset.write_register(("CIU_TxMode", tx_mode)) return ("106A", "212F", "424F")[dri] def _tt3_send_rsp_recv_cmd(self, target, data, timeout): regs = [ ("CIU_FIFOLevel", 0b10000000), # clear fifo read/write pointer ("CIU_CommIRq", 0b01111111), # clear interrupt request bits ("CIU_DivIRq", 0b01111111), # clear interrupt request bits ] if data is not None: regs.extend(zip(len(data)*["CIU_FIFOData"], data)) regs.append(("CIU_BitFraming", 0b10000000)) # StartSend (b7=1) self.chipset.write_register(*regs) irq_regs = ("CIU_CommIRq", "CIU_DivIRq") time_to_return = time.time() + (timeout if timeout else 0) while timeout is None or time.time() < time_to_return: time.sleep(0.01) commirq, divirq = self.chipset.read_register(*irq_regs) if divirq & 0b00000001: raise nfc.clf.BrokenLinkError("external field switched off") if commirq & 0b00100000: self.chipset.write_register("CIU_CommIRq", 0b00100000) fifo_size = self.chipset.read_register("CIU_FIFOLevel") fifo_read = fifo_size * ["CIU_FIFOData"] fifo_data = bytearray(self.chipset.read_register(*fifo_read)) if fifo_data[0] != len(fifo_data): raise nfc.clf.TransmissionError("frame length byte error") return fifo_data if timeout > 0: info = "no data received within %.3f s" % timeout self.log.debug(info) raise nfc.clf.TimeoutError(info) def send_rsp_recv_cmd(self, target, data, timeout): # print("\n".join(self._print_ciu_register_page(0, 1))) if target.tt3_cmd: return self._tt3_send_rsp_recv_cmd(target, data, timeout) try: if data: self.chipset.tg_response_to_initiator(data) return self.chipset.tg_get_initiator_command(timeout) except Chipset.Error as error: if error.errno in (0x0A, 0x29, 0x31): self.log.debug("Error: %s", error) raise nfc.clf.BrokenLinkError(str(error)) else: self.log.warning(error) raise nfc.clf.TransmissionError(str(error)) except IOError as error: if error.errno == errno.ETIMEDOUT: info = "no data received within %.3f s" % timeout self.log.debug(info) raise nfc.clf.TimeoutError(info) else: # host-controller communication broken self.log.error(error) raise error def _print_ciu_register_page(self, *pages): lines = list() for page in pages: base = (0x6331, 0x6301, 0x6311, 0x6321)[page] regs = set(self.chipset.REG) regs = sorted(regs.intersection(range(base, base+16))) vals = self.chipset.read_register(*regs) regs = [self.chipset.REG[r] for r in regs] for r, v in zip(regs, vals): lines.append("{0:16s} {1:08b}b {2:02X}h".format(r, v, v)) return lines def init(transport): log.warning("pn53x is not a driver module, use pn531, pn532, or pn533") raise IOError(errno.ENODEV, os.strerror(errno.ENODEV))