From 6b4ba0dbf37cd318dbe7b6fc66a42a6e620df512 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Thu, 11 Jun 2015 22:31:46 +0200 Subject: [PATCH 01/15] WIP support for MFRC522 --- cmake/modules/LibnfcDrivers.cmake | 7 + libnfc/chips/Makefile.am | 2 +- libnfc/chips/rc522-internal.h | 95 +++++++++ libnfc/chips/rc522.c | 212 +++++++++++++++++++ libnfc/chips/rc522.h | 35 ++++ libnfc/drivers/Makefile.am | 4 + libnfc/drivers/rc522_uart.c | 334 ++++++++++++++++++++++++++++++ libnfc/drivers/rc522_uart.h | 30 +++ m4/libnfc_drivers.m4 | 14 +- 9 files changed, 729 insertions(+), 4 deletions(-) create mode 100644 libnfc/chips/rc522-internal.h create mode 100644 libnfc/chips/rc522.c create mode 100644 libnfc/chips/rc522.h create mode 100644 libnfc/drivers/rc522_uart.c create mode 100644 libnfc/drivers/rc522_uart.h diff --git a/cmake/modules/LibnfcDrivers.cmake b/cmake/modules/LibnfcDrivers.cmake index 7a2a11d..3582074 100644 --- a/cmake/modules/LibnfcDrivers.cmake +++ b/cmake/modules/LibnfcDrivers.cmake @@ -11,6 +11,7 @@ ELSE(WIN32) ENDIF(WIN32) SET(LIBNFC_DRIVER_PN532_UART ON CACHE BOOL "Enable PN532 UART support (Use serial port)") SET(LIBNFC_DRIVER_PN53X_USB ON CACHE BOOL "Enable PN531 and PN531 USB support (Depends on libusb)") +SET(LIBNFC_DRIVER_RC522_UART ON CACHE BOOL "Enable MFRC522/FM17222 UART support (Use serial port)") IF(LIBNFC_DRIVER_ACR122_PCSC) FIND_PACKAGE(PCSC REQUIRED) @@ -61,4 +62,10 @@ IF(LIBNFC_DRIVER_PN53X_USB) SET(USB_REQUIRED TRUE) ENDIF(LIBNFC_DRIVER_PN53X_USB) +IF(LIBNFC_DRIVER_RC522_UART) + ADD_DEFINITIONS("-DDRIVER_RC522_UART_ENABLED") + SET(DRIVERS_SOURCES ${DRIVERS_SOURCES} "drivers/rc522_uart") + SET(UART_REQUIRED TRUE) +ENDIF(LIBNFC_DRIVER_RC522_UART) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/drivers) diff --git a/libnfc/chips/Makefile.am b/libnfc/chips/Makefile.am index 08b46a0..db3c397 100644 --- a/libnfc/chips/Makefile.am +++ b/libnfc/chips/Makefile.am @@ -3,6 +3,6 @@ AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS) noinst_LTLIBRARIES = libnfcchips.la -libnfcchips_la_SOURCES = pn53x.c pn53x.h pn53x-internal.h +libnfcchips_la_SOURCES = pn53x.c pn53x.h pn53x-internal.h rc522.c rc522.h rc522-internal.h libnfcchips_la_CFLAGS = -I$(top_srcdir)/libnfc diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h new file mode 100644 index 0000000..4415207 --- /dev/null +++ b/libnfc/chips/rc522-internal.h @@ -0,0 +1,95 @@ + + +#ifndef __NFC_CHIPS_RC522_INTERNAL_H__ +#define __NFC_CHIPS_RC522_INTERNAL_H__ + +#define RC522_REG_CommandReg 0x01 + +#define RC522_REG_ComlEnReg 0x02 + +#define RC522_REG_DivlEnReg 0x03 + +#define RC522_REG_ComIrqReg 0x04 + +#define RC522_REG_DivIrqReg 0x05 + +#define RC522_REG_ErrorReg 0x06 + +#define RC522_REG_Status1Reg 0x07 + +#define RC522_REG_Status2Reg 0x08 +#define RC522_REG_Status2Reg_MFCrypto1On (1 << 3) + +#define RC522_REG_FIFODataReg 0x09 + +#define RC522_REG_FIFOLevelReg 0x0A + +#define RC522_REG_WaterLevelReg 0x0B + +#define RC522_REG_ControlReg 0x0C + +#define RC522_REG_BitFramingReg 0x0D + +#define RC522_REG_CollReg 0x0E + +#define RC522_REG_ModeReg 0x11 + +#define RC522_REG_TxModeReg 0x12 +#define RC522_REG_TxModeReg_TxCRCEn (1 << 7) +#define RC522_REG_TxModeReg_TxSpeed_106k (0 << 4) +#define RC522_REG_TxModeReg_TxSpeed_212k (1 << 4) +#define RC522_REG_TxModeReg_TxSpeed_424k (2 << 4) +#define RC522_REG_TxModeReg_TxSpeed_847k (3 << 4) +#define RC522_REG_TxModeReg_TxSpeed_MASK (7 << 4) + +#define RC522_REG_RxModeReg 0x13 +#define RC522_REG_RxModeReg_RxCRCEn (1 << 7) +#define RC522_REG_RxModeReg_RxSpeed_106k (0 << 4) +#define RC522_REG_RxModeReg_RxSpeed_212k (1 << 4) +#define RC522_REG_RxModeReg_RxSpeed_424k (2 << 4) +#define RC522_REG_RxModeReg_RxSpeed_847k (3 << 4) +#define RC522_REG_RxModeReg_RxSpeed_MASK (7 << 4) + +#define RC522_REG_TxControlReg 0x14 +#define RC522_REG_TxControlReg_Tx2RFEn (1 << 1) +#define RC522_REG_TxControlReg_Tx1RFEn (1 << 0) + +#define RC522_REG_TxASKReg 0x15 + +#define RC522_REG_TxSelReg 0x16 + +#define RC522_REG_RxSelReg 0x17 + +#define RC522_REG_RxThresholdReg 0x18 + +#define RC522_REG_DemodReg 0x19 + +#define RC522_REG_MfTxReg 0x1C + +#define RC522_REG_MfRxReg 0x1D +#define RC522_REG_MfRxReg_ParityDisable (1 << 4) + +#define RC522_REG_SerialSpeedReg 0x1F +#define RC522_REG_CRCResultReg 0x21 +#define RC522_REG_ModWidthReg 0x24 +#define RC522_REG_RFCfgReg 0x26 +#define RC522_REG_GsNReg 0x27 +#define RC522_REG_CWGsPReg 0x28 +#define RC522_REG_ModGsPReg 0x29 +#define RC522_REG_TModeReg 0x2A +#define RC522_REG_TPrescalerReg 0x2B +#define RC522_REG_TReloadReg 0x2C +#define RC522_REG_TCounterValReg 0x2E +#define RC522_REG_TestSel1Reg 0x31 +#define RC522_REG_TestSel2Reg 0x32 +#define RC522_REG_TestPinEnReg 0x33 +#define RC522_REG_TestPinValueReg 0x34 +#define RC522_REG_TestBusReg 0x35 +#define RC522_REG_AutoTestReg 0x36 +#define RC522_REG_VersionReg 0x37 +#define RC522_REG_AnalogTestReg 0x38 +#define RC522_REG_TestDAC1Reg 0x39 +#define RC522_REG_TestDAC2Reg 0x3A +#define RC522_REG_TestADCReg 0x3B + +#endif diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c new file mode 100644 index 0000000..d4545a8 --- /dev/null +++ b/libnfc/chips/rc522.c @@ -0,0 +1,212 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#include + +#include "rc522.h" +#include "rc522-internal.h" + +#include "nfc/nfc.h" +#include "nfc-internal.h" + +#define LOG_CATEGORY "libnfc.chip.rc522" +#define LOG_GROUP NFC_LOG_GROUP_CHIP + +#define RC522_TIMEOUT 5 + +const nfc_modulation_type rc522_initiator_modulation[] = { NMT_ISO14443A, 0 }; +const nfc_modulation_type rc522_target_modulation[] = { 0 }; + +const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, NBR_212, NBR_106, 0 }; + +struct rc522_chip_data { + const struct rc522_io * io; +}; + +#define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) + +int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { + pnd->chip_data = malloc(sizeof(struct rc522_chip_data)); + if (!pnd->chip_data) { + perror("malloc"); + return NFC_ESOFT; + } + + CHIP_DATA(pnd)->io = io; + return NFC_SUCCESS; +} + +int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { + uint8_t val; + + int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1, RC522_TIMEOUT); + if (ret < 0) { + return ret; + } + + return val; +} + +int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t mask) { + if (mask != 0xFF) { + int oldval = rc522_read_reg(pnd, reg); + if (oldval < 0) { + return oldval; + } + + val = (val & mask) | (oldval & ~mask); + } + + return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1, RC522_TIMEOUT); +} + +int rc522_set_speed(struct nfc_device * pnd, nfc_baud_rate speed) { + uint8_t txVal, rxVal; + + switch (speed) { + case NBR_106: + txVal = RC522_REG_TxModeReg_TxSpeed_106k; + rxVal = RC522_REG_RxModeReg_RxSpeed_106k; + break; + + case NBR_212: + txVal = RC522_REG_TxModeReg_TxSpeed_212k; + rxVal = RC522_REG_RxModeReg_RxSpeed_212k; + break; + + case NBR_424: + txVal = RC522_REG_TxModeReg_TxSpeed_424k; + rxVal = RC522_REG_RxModeReg_RxSpeed_424k; + break; + + case NBR_847: + txVal = RC522_REG_TxModeReg_TxSpeed_847k; + rxVal = RC522_REG_RxModeReg_RxSpeed_847k; + break; + + default: + return NFC_EINVARG; + } + + return + rc522_write_reg(pnd, RC522_REG_TxModeReg, txVal, RC522_REG_TxModeReg_TxSpeed_MASK) || + rc522_write_reg(pnd, RC522_REG_RxModeReg, rxVal, RC522_REG_RxModeReg_RxSpeed_MASK); +} + +int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { + switch (mode) { + case N_INITIATOR: + *supported_mt = rc522_initiator_modulation; + break; + + case N_TARGET: + *supported_mt = rc522_target_modulation; + break; + + default: + return NFC_EINVARG; + } + + return NFC_SUCCESS; +} + +int rc522_get_supported_baud_rate(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br) { + switch (mode) { + case N_INITIATOR: + switch (nmt) { + case NMT_ISO14443A: + *supported_br = rc522_iso14443a_supported_baud_rates; + break; + + default: + return NFC_EINVARG; + } + break; + + case N_TARGET: + default: + return NFC_EINVARG; + } + + return NFC_SUCCESS; +} + +int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable) { + int ret; + + switch (property) { + case NP_HANDLE_CRC: + if (pnd->bCrc == enable) { + return NFC_SUCCESS; + } + + ret = rc522_write_reg(pnd, RC522_REG_TxModeReg, enable ? ~0 : 0, RC522_REG_TxModeReg_TxCRCEn) || + rc522_write_reg(pnd, RC522_REG_RxModeReg, enable ? ~0 : 0, RC522_REG_RxModeReg_RxCRCEn); + if (ret) { + return ret; + } + + pnd->bCrc = enable; + return NFC_SUCCESS; + + case NP_HANDLE_PARITY: + if (pnd->bPar == enable) { + return NFC_SUCCESS; + } + + ret = rc522_write_reg(pnd, RC522_REG_MfRxReg, enable ? 0 : ~0, RC522_REG_MfRxReg_ParityDisable); + if (ret) { + return ret; + } + + pnd->bPar = enable; + return NFC_SUCCESS; + + case NP_EASY_FRAMING: + pnd->bEasyFraming = enable; + return NFC_SUCCESS; + + case NP_ACTIVATE_FIELD: + return rc522_write_reg(pnd, RC522_REG_TxControlReg, enable ? ~0 : 0, RC522_REG_TxControlReg_Tx2RFEn | RC522_REG_TxControlReg_Tx1RFEn); + + case NP_ACTIVATE_CRYPTO1: + return rc522_write_reg(pnd, RC522_REG_Status2Reg, enable ? ~0 : 0, RC522_REG_Status2Reg_MFCrypto1On); + + case NP_FORCE_ISO14443_A: + // ISO14443-A is the only mode supported by MFRC522 + return NFC_SUCCESS; + + case NP_FORCE_SPEED_106: + if (!enable) { + return NFC_SUCCESS; + } + + return rc522_set_speed(pnd, NBR_106); + + case NP_AUTO_ISO14443_4: + case NP_ACCEPT_INVALID_FRAMES: + case NP_INFINITE_SELECT: + case NP_FORCE_ISO14443_B: + case NP_TIMEOUT_COMMAND: + case NP_TIMEOUT_ATR: + case NP_TIMEOUT_COM: + return NFC_EINVARG; + } + + return NFC_EINVARG; +} diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h new file mode 100644 index 0000000..dac87bb --- /dev/null +++ b/libnfc/chips/rc522.h @@ -0,0 +1,35 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#ifndef __NFC_CHIPS_RC522_H__ +#define __NFC_CHIPS_RC522_H__ + +#include +#include + +struct rc522_io { + int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout); + int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size, unsigned int timeout); +}; + +int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); +int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); +int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); +int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable); + +#endif diff --git a/libnfc/drivers/Makefile.am b/libnfc/drivers/Makefile.am index 1ac65b2..6bbd00d 100644 --- a/libnfc/drivers/Makefile.am +++ b/libnfc/drivers/Makefile.am @@ -39,6 +39,10 @@ if DRIVER_PN532_I2C_ENABLED libnfcdrivers_la_SOURCES += pn532_i2c.c pn532_i2c.h endif +if DRIVER_RC522_UART_ENABLED +libnfcdrivers_la_SOURCES += rc522_uart.c rc522_uart.h +endif + if PCSC_ENABLED libnfcdrivers_la_CFLAGS += @libpcsclite_CFLAGS@ libnfcdrivers_la_LIBADD += @libpcsclite_LIBS@ diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c new file mode 100644 index 0000000..3c4088a --- /dev/null +++ b/libnfc/drivers/rc522_uart.c @@ -0,0 +1,334 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +/** + * @file rc522_uart.c + * @brief Driver for MFRC522- and FM17222-based devices connected with an UART + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "rc522_uart.h" + +#include +#include + +#include + +#include "drivers.h" +#include "nfc-internal.h" +#include "chips/rc522.h" +#include "uart.h" + +#define RC522_UART_DEFAULT_SPEED 9600 +#define RC522_UART_DRIVER_NAME "rc522_uart" + +#define LOG_CATEGORY "libnfc.driver.rc522_uart" +#define LOG_GROUP NFC_LOG_GROUP_DRIVER + +// Internal data structs +const struct rc522_io rc522_uart_io; +struct rc522_uart_data { + serial_port port; +}; + +#define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) + +static size_t +rc522_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) +{ + size_t device_found = 0; + serial_port sp; + char **acPorts = uart_list_ports(); + const char *acPort; + int iDevice = 0; + + while ((acPort = acPorts[iDevice++])) { + sp = uart_open(acPort); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find RC522 device on serial port: %s at %d baud.", acPort, RC522_UART_DEFAULT_SPEED); + + if ((sp != INVALID_SERIAL_PORT) && (sp != CLAIMED_SERIAL_PORT)) { + // We need to flush input to be sure first reply does not comes from older byte transceive + uart_flush_input(sp, true); + // Serial port claimed but we need to check if a RC522_UART is opened. + uart_set_speed(sp, RC522_UART_DEFAULT_SPEED); + + nfc_connstring connstring; + snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, RC522_UART_DRIVER_NAME, acPort, RC522_UART_DEFAULT_SPEED); + nfc_device *pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("malloc"); + uart_close(sp); + iDevice = 0; + while ((acPort = acPorts[iDevice++])) { + free((void *)acPort); + } + free(acPorts); + return 0; + } + pnd->driver = &rc522_uart_driver; + pnd->driver_data = sp; + + // Alloc and init chip's data + if (rc522_data_new(pnd, &rc522_uart_io) == NULL) { + perror("rc522_data_new"); + uart_close(DRIVER_DATA(pnd)->port); + nfc_device_free(pnd); + iDevice = 0; + while ((acPort = acPorts[iDevice++])) { + free((void *)acPort); + } + free(acPorts); + return 0; + } + // SAMConfiguration command if needed to wakeup the chip and rc522_SAMConfiguration check if the chip is a RC522 + CHIP_DATA(pnd)->type = RC522; + // This device starts in LowVBat power mode + CHIP_DATA(pnd)->power_mode = LOWVBAT; + + // Check communication using "Diagnose" command, with "Communication test" (0x00) + int res = rc522_check_communication(pnd); + uart_close(DRIVER_DATA(pnd)->port); + rc522_data_free(pnd); + nfc_device_free(pnd); + if (res < 0) { + continue; + } + + memcpy(connstrings[device_found], connstring, sizeof(nfc_connstring)); + device_found++; + + // Test if we reach the maximum "wanted" devices + if (device_found >= connstrings_len) + break; + } + } + iDevice = 0; + while ((acPort = acPorts[iDevice++])) { + free((void *)acPort); + } + free(acPorts); + return device_found; +} + +void rc522_uart_close(nfc_device * pnd) { + rc522_idle(pnd); + + // Release UART port + uart_close(DRIVER_DATA(pnd)->port); + free(DRIVER_DATA(pnd)); + + rc522_data_free(pnd); + nfc_device_free(pnd); +} + +struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_connstring connstring) { + char * port_str; + char * baud_str; + uint32_t baudrate; + + int decodelvl = connstring_decode(connstring, RC522_UART_DRIVER_NAME, NULL, &port_str, &baud_str); + switch (decodelvl) { + case 2: // Got port but no speed + baudrate = RC522_UART_DEFAULT_SPEED; + break; + + case 3: // Got port and baud rate + char * endptr; + baudrate = (uint32_t) strtol(speed_s, &endptr, 10); + if (*endptr != '\0') { + free(port_str); + free(bps_str); + return NULL; + } + + free(bps_str); + break; + + default: // Got unparseable gibberish + free(port_str); + free(bps_str); + return NULL; + } + + serial_port sp; + struct nfc_device * pnd = NULL; + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s at %d baud.", port_str, baudrate); + sp = uart_open(ndd.port); + + if (sp == INVALID_SERIAL_PORT) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid serial port: %s", port_str); + free(port_str); + return NULL; + } + + if (sp == CLAIMED_SERIAL_PORT) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Serial port already claimed: %s", port_str); + free(port_str); + return NULL; + } + + // We need to flush input to be sure first reply does not comes from older byte transceive + uart_flush_input(sp, true); + uart_set_speed(sp, baudrate); + + // We have a connection + pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("nfc_device_new"); + free(port_str); + uart_close(sp); + return NULL; + } + + snprintf(pnd->name, sizeof(pnd->name), "%s:%s", RC522_UART_DRIVER_NAME, port_str); + free(port_str); + pnd->driver = &rc522_uart_driver; + pnd->driver_data = malloc(sizeof(struct rc522_uart_data)); + if (!pnd->driver_data) { + perror("malloc"); + uart_close(sp); + nfc_device_free(pnd); + return NULL; + } + DRIVER_DATA(pnd)->port = sp; + + // Alloc and init chip's data + if (rc522_data_new(pnd, &rc522_uart_io) == NULL) { + perror("rc522_data_new"); + uart_close(sp); + nfc_device_free(pnd); + return NULL; + } + + if (rc522_check_communication(pnd) < 0) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_check_communication error"); + rc522_uart_close(pnd); + return NULL; + } + + rc522_init(pnd); + return pnd; +} + +int rc522_uart_wakeup(struct nfc_device *pnd) { + /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. */ + const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int res = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), 0); + CHIP_DATA(pnd)->power_mode = NORMAL; // RC522 should now be awake + return res; +} + +#define READ 1 +#define WRITE 0 +uint8_t rc522_uart_pack(int reg, int op) { + assert(reg < 64); + assert(op == READ | op == WRITE); + + return op << 7 | reg; +} + +int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout) { + uint8_t cmd = rc522_uart_pack(reg, READ); + + while (size > 0) { + pnd->last_error = uart_send(pnd->driver_data, &cmd, sizeof(cmd), timeout); + if (pnd->last_error < 0) { + goto error; + } + + pnd->last_error = uart_receive(pnd->driver_data, data, 1, timeout); + if (pnd->last_error < 0) { + goto error; + } + + size--; + data++; + } + + return 0; + +error: + uart_flush_input(DRIVER_DATA(pnd)->port, true); + return pnd->last_error; +} + +int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout) { + uint8_t cmd[2]; + cmd[0] = rc522_uart_pack(reg, WRITE); + + while (size > 0) { + cmd[1] = *data; + pnd->last_error = uart_send(pnd->driver_data, &cmd, sizeof(cmd), timeout); + if (pnd->last_error < 0) { + return pnd->last_error; + } + + size--; + data++; + } + + return 0; +} + +const struct rc522_io rc522_uart_io = { + .read = rc522_uart_read, + .write = rc522_uart_write, +}; + +const struct nfc_driver rc522_uart_driver = { + .name = RC522_UART_DRIVER_NAME, + .scan_type = INTRUSIVE, + .scan = rc522_uart_scan, + .open = rc522_uart_open, + .close = rc522_uart_close, + .strerror = rc522_strerror, + + .initiator_init = rc522_initiator_init, + // MFRC522 has no secure element + .initiator_init_secure_element = NULL, + .initiator_select_passive_target = rc522_initiator_select_passive_target, + .initiator_poll_target = rc522_initiator_poll_target, + .initiator_select_dep_target = NULL, + .initiator_deselect_target = rc522_initiator_deselect_target, + .initiator_transceive_bytes = rc522_initiator_transceive_bytes, + .initiator_transceive_bits = rc522_initiator_transceive_bits, + .initiator_transceive_bytes_timed = rc522_initiator_transceive_bytes_timed, + .initiator_transceive_bits_timed = rc522_initiator_transceive_bits_timed, + .initiator_target_is_present = rc522_initiator_target_is_present, + + // MFRC522 is unable to work as target + .target_init = NULL, + .target_send_bytes = NULL, + .target_receive_bytes = NULL, + .target_send_bits = NULL, + .target_receive_bits = NULL, + + .device_set_property_bool = rc522_set_property_bool, + .device_set_property_int = rc522_set_property_int, + .get_supported_modulation = rc522_get_supported_modulation, + .get_supported_baud_rate = rc522_get_supported_baud_rate, + .device_get_information_about = rc522_get_information_about, + + .abort_command = rc522_uart_abort_command, + .idle = rc522_idle, + .powerdown = rc522_softdown, +}; + diff --git a/libnfc/drivers/rc522_uart.h b/libnfc/drivers/rc522_uart.h new file mode 100644 index 0000000..a131a16 --- /dev/null +++ b/libnfc/drivers/rc522_uart.h @@ -0,0 +1,30 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +/** + * @file rc522_uart.h + * @brief Driver for MFRC522- and FM17222-based devices connected with an UART + */ + +#ifndef __NFC_DRIVER_RC522_UART_H__ +#define __NFC_DRIVER_RC555_UART_H__ + +#include + +extern const struct nfc_driver rc522_uart_driver; + +#endif diff --git a/m4/libnfc_drivers.m4 b/m4/libnfc_drivers.m4 index 4672539..1894462 100644 --- a/m4/libnfc_drivers.m4 +++ b/m4/libnfc_drivers.m4 @@ -4,7 +4,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], [ AC_MSG_CHECKING(which drivers to build) AC_ARG_WITH(drivers, - AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_i2c', 'pn532_spi', 'pn532_uart' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]), + AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_i2c', 'pn532_spi', 'pn532_uart', 'pn53x_usb' and 'rc522_uart'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb,rc522_uart'. The special driver set 'all' compile all available drivers.]), [ case "${withval}" in yes | no) dnl ignore calls without any arguments @@ -25,7 +25,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], case "${DRIVER_BUILD_LIST}" in default) - DRIVER_BUILD_LIST="acr122_usb acr122s arygon pn53x_usb pn532_uart" + DRIVER_BUILD_LIST="acr122_usb acr122s arygon pn53x_usb pn532_uart rc522_uart" if test x"$spi_available" = x"yes" then DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi" @@ -36,7 +36,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], fi ;; all) - DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart" + DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart rc522_uart" if test x"$spi_available" = x"yes" then DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi" @@ -58,6 +58,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], driver_pn532_uart_enabled="no" driver_pn532_spi_enabled="no" driver_pn532_i2c_enabled="no" + driver_rc522_uart_enabled="no" for driver in ${DRIVER_BUILD_LIST} do @@ -102,6 +103,11 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], driver_pn532_i2c_enabled="yes" DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_I2C_ENABLED" ;; + rc522_uart) + uart_required="yes" + driver_rc522_uart_enabled="yes" + DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_RC522_UART_ENABLED" + ;; *) AC_MSG_ERROR([Unknow driver: $driver]) ;; @@ -116,6 +122,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], AM_CONDITIONAL(DRIVER_PN532_UART_ENABLED, [test x"$driver_pn532_uart_enabled" = xyes]) AM_CONDITIONAL(DRIVER_PN532_SPI_ENABLED, [test x"$driver_pn532_spi_enabled" = xyes]) AM_CONDITIONAL(DRIVER_PN532_I2C_ENABLED, [test x"$driver_pn532_i2c_enabled" = xyes]) + AM_CONDITIONAL(DRIVER_RC522_UART_ENABLED, [test x"$driver_rc522_uart_enabled" = xyes]) ]) AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[ @@ -129,4 +136,5 @@ echo " pn53x_usb........ $driver_pn53x_usb_enabled" echo " pn532_uart....... $driver_pn532_uart_enabled" echo " pn532_spi....... $driver_pn532_spi_enabled" echo " pn532_i2c........ $driver_pn532_i2c_enabled" +echo " rc522_uart....... $driver_rc522_uart_enabled" ]) From 96839468e872611552cda001756761998f92bb57 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Fri, 12 Jun 2015 20:46:57 +0200 Subject: [PATCH 02/15] Fixed MFRC522 UART driver --- libnfc/drivers/rc522_uart.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index 3c4088a..c65bfbc 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -249,7 +249,7 @@ int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t uint8_t cmd = rc522_uart_pack(reg, READ); while (size > 0) { - pnd->last_error = uart_send(pnd->driver_data, &cmd, sizeof(cmd), timeout); + pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, timeout); if (pnd->last_error < 0) { goto error; } @@ -263,7 +263,7 @@ int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t data++; } - return 0; + return NFC_SUCCESS; error: uart_flush_input(DRIVER_DATA(pnd)->port, true); @@ -271,21 +271,43 @@ error: } int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout) { - uint8_t cmd[2]; - cmd[0] = rc522_uart_pack(reg, WRITE); + uint8_t cmd = rc522_uart_pack(reg, WRITE); while (size > 0) { - cmd[1] = *data; - pnd->last_error = uart_send(pnd->driver_data, &cmd, sizeof(cmd), timeout); + // First: send write request + pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, timeout); + if (pnd->last_error < 0) { + goto error; + } + + // Second: wait for a reply + uint8_t reply; + pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, timeout); if (pnd->last_error < 0) { return pnd->last_error; } + // Third: compare sent and received. They must match. + if (cmd != reply) { + pnd->last_error = NFC_EIO; + goto error; + } + + // Fourth: send register data + pnd->last_error = uart_send(pnd->driver_data, data, 1, timeout); + if (pnd->last_error < 0) { + goto error; + } + size--; data++; } - return 0; + return NFC_SUCCESS; + +error: + uart_flush_input(DRIVER_DATA(pnd)->port, true); + return pnd->last_error; } const struct rc522_io rc522_uart_io = { From 6953cbea2caa55e5c1e3e3254f0e9274f9bdcfd5 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 13 Jun 2015 23:10:36 +0200 Subject: [PATCH 03/15] Further work for RC522 --- libnfc/chips/rc522-internal.h | 15 ++++++++++++++ libnfc/chips/rc522.c | 37 +++++++++++++++++++++++++++++++---- libnfc/chips/rc522.h | 4 ++-- libnfc/drivers/rc522_uart.c | 17 ++++++++-------- 4 files changed, 59 insertions(+), 14 deletions(-) diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index 4415207..4e9186b 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -3,7 +3,11 @@ #ifndef __NFC_CHIPS_RC522_INTERNAL_H__ #define __NFC_CHIPS_RC522_INTERNAL_H__ +#define RC522_FIFO_SIZE 64 + #define RC522_REG_CommandReg 0x01 +#define RC522_REG_CommandReg_RcvOff (1 << 5) +#define RC522_REG_CommandReg_PowerDown (1 << 4) #define RC522_REG_ComlEnReg 0x02 @@ -92,4 +96,15 @@ #define RC522_REG_TestDAC2Reg 0x3A #define RC522_REG_TestADCReg 0x3B +#define RC522_CMD_Idle 0x0 +#define RC522_CMD_Mem 0x1 +#define RC522_CMD_GenerateRandomId 0x2 +#define RC522_CMD_CalcCRC 0x3 +#define RC522_CMD_Transmit 0x4 +#define RC522_CMD_NoCmdChange 0x7 +#define RC522_CMD_Receive 0x8 +#define RC522_CMD_Transceive 0xC +#define RC522_CMD_MFAuthent 0xE +#define RC522_CMD_SoftReset 0xF + #endif diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index d4545a8..7a6539e 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -54,7 +54,7 @@ int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { uint8_t val; - int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1, RC522_TIMEOUT); + int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1); if (ret < 0) { return ret; } @@ -72,11 +72,12 @@ int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t m val = (val & mask) | (oldval & ~mask); } - return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1, RC522_TIMEOUT); + return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1); } -int rc522_set_speed(struct nfc_device * pnd, nfc_baud_rate speed) { +int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { uint8_t txVal, rxVal; + int ret; switch (speed) { case NBR_106: @@ -108,6 +109,23 @@ int rc522_set_speed(struct nfc_device * pnd, nfc_baud_rate speed) { rc522_write_reg(pnd, RC522_REG_RxModeReg, rxVal, RC522_REG_RxModeReg_RxSpeed_MASK); } +int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) +{ + int ret; + + if (nm.nmt != NMT_ISO14443A) { + return NFC_EINVARG; + } + + ret = rc522_set_baud_rate(pnd, nm.nbr); + if (ret < 0) { + return ret; + } + + // TODO + return NFC_ENOTIMPL; +} + int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { switch (mode) { case N_INITIATOR: @@ -196,7 +214,7 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - return rc522_set_speed(pnd, NBR_106); + return rc522_set_baud_rate(pnd, NBR_106); case NP_AUTO_ISO14443_4: case NP_ACCEPT_INVALID_FRAMES: @@ -210,3 +228,14 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_EINVARG; } + +int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value) { + // TODO + return NFC_ENOTIMPL; +} + +int rc522_idle(struct nfc_device * pnd) { + // Set idle and disable RX demodulator to save energy + return rc522_write_reg(pnd, RC522_REG_CommandReg, RC522_CMD_Idle | RC522_REG_CommandReg_PowerDown, 0xFF); +} + diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index dac87bb..41153d2 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -23,8 +23,8 @@ #include struct rc522_io { - int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout); - int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size, unsigned int timeout); + int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size); + int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size); }; int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index c65bfbc..8d49b44 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -38,6 +38,7 @@ #define RC522_UART_DEFAULT_SPEED 9600 #define RC522_UART_DRIVER_NAME "rc522_uart" +#define RC522_UART_IO_TIMEOUT 50 #define LOG_CATEGORY "libnfc.driver.rc522_uart" #define LOG_GROUP NFC_LOG_GROUP_DRIVER @@ -245,16 +246,16 @@ uint8_t rc522_uart_pack(int reg, int op) { return op << 7 | reg; } -int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout) { +int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size) { uint8_t cmd = rc522_uart_pack(reg, READ); while (size > 0) { - pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, timeout); + pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { goto error; } - pnd->last_error = uart_receive(pnd->driver_data, data, 1, timeout); + pnd->last_error = uart_receive(pnd->driver_data, data, 1, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { goto error; } @@ -270,19 +271,19 @@ error: return pnd->last_error; } -int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size, unsigned int timeout) { +int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size) { uint8_t cmd = rc522_uart_pack(reg, WRITE); while (size > 0) { // First: send write request - pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, timeout); + pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { goto error; } // Second: wait for a reply uint8_t reply; - pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, timeout); + pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { return pnd->last_error; } @@ -294,7 +295,7 @@ int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_ } // Fourth: send register data - pnd->last_error = uart_send(pnd->driver_data, data, 1, timeout); + pnd->last_error = uart_send(pnd->driver_data, data, 1, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { goto error; } @@ -349,7 +350,7 @@ const struct nfc_driver rc522_uart_driver = { .get_supported_baud_rate = rc522_get_supported_baud_rate, .device_get_information_about = rc522_get_information_about, - .abort_command = rc522_uart_abort_command, + .abort_command = rc522_idle, .idle = rc522_idle, .powerdown = rc522_softdown, }; From 942faef5824b8b707c545b6d68a18c06f6f76738 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 13 Jun 2015 23:14:21 +0200 Subject: [PATCH 04/15] Convert CRLFs to LFs --- libnfc/chips/rc522-internal.h | 220 ++++++++-------- libnfc/chips/rc522.c | 482 +++++++++++++++++----------------- libnfc/chips/rc522.h | 72 ++--- 3 files changed, 388 insertions(+), 386 deletions(-) diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index 4e9186b..7d2923c 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -1,110 +1,110 @@ - - -#ifndef __NFC_CHIPS_RC522_INTERNAL_H__ -#define __NFC_CHIPS_RC522_INTERNAL_H__ - -#define RC522_FIFO_SIZE 64 - -#define RC522_REG_CommandReg 0x01 -#define RC522_REG_CommandReg_RcvOff (1 << 5) -#define RC522_REG_CommandReg_PowerDown (1 << 4) - -#define RC522_REG_ComlEnReg 0x02 - -#define RC522_REG_DivlEnReg 0x03 - -#define RC522_REG_ComIrqReg 0x04 - -#define RC522_REG_DivIrqReg 0x05 - -#define RC522_REG_ErrorReg 0x06 - -#define RC522_REG_Status1Reg 0x07 - -#define RC522_REG_Status2Reg 0x08 -#define RC522_REG_Status2Reg_MFCrypto1On (1 << 3) - -#define RC522_REG_FIFODataReg 0x09 - -#define RC522_REG_FIFOLevelReg 0x0A - -#define RC522_REG_WaterLevelReg 0x0B - -#define RC522_REG_ControlReg 0x0C - -#define RC522_REG_BitFramingReg 0x0D - -#define RC522_REG_CollReg 0x0E - -#define RC522_REG_ModeReg 0x11 - -#define RC522_REG_TxModeReg 0x12 -#define RC522_REG_TxModeReg_TxCRCEn (1 << 7) -#define RC522_REG_TxModeReg_TxSpeed_106k (0 << 4) -#define RC522_REG_TxModeReg_TxSpeed_212k (1 << 4) -#define RC522_REG_TxModeReg_TxSpeed_424k (2 << 4) -#define RC522_REG_TxModeReg_TxSpeed_847k (3 << 4) -#define RC522_REG_TxModeReg_TxSpeed_MASK (7 << 4) - -#define RC522_REG_RxModeReg 0x13 -#define RC522_REG_RxModeReg_RxCRCEn (1 << 7) -#define RC522_REG_RxModeReg_RxSpeed_106k (0 << 4) -#define RC522_REG_RxModeReg_RxSpeed_212k (1 << 4) -#define RC522_REG_RxModeReg_RxSpeed_424k (2 << 4) -#define RC522_REG_RxModeReg_RxSpeed_847k (3 << 4) -#define RC522_REG_RxModeReg_RxSpeed_MASK (7 << 4) - -#define RC522_REG_TxControlReg 0x14 -#define RC522_REG_TxControlReg_Tx2RFEn (1 << 1) -#define RC522_REG_TxControlReg_Tx1RFEn (1 << 0) - -#define RC522_REG_TxASKReg 0x15 - -#define RC522_REG_TxSelReg 0x16 - -#define RC522_REG_RxSelReg 0x17 - -#define RC522_REG_RxThresholdReg 0x18 - -#define RC522_REG_DemodReg 0x19 - -#define RC522_REG_MfTxReg 0x1C - -#define RC522_REG_MfRxReg 0x1D -#define RC522_REG_MfRxReg_ParityDisable (1 << 4) - -#define RC522_REG_SerialSpeedReg 0x1F -#define RC522_REG_CRCResultReg 0x21 -#define RC522_REG_ModWidthReg 0x24 -#define RC522_REG_RFCfgReg 0x26 -#define RC522_REG_GsNReg 0x27 -#define RC522_REG_CWGsPReg 0x28 -#define RC522_REG_ModGsPReg 0x29 -#define RC522_REG_TModeReg 0x2A -#define RC522_REG_TPrescalerReg 0x2B -#define RC522_REG_TReloadReg 0x2C -#define RC522_REG_TCounterValReg 0x2E -#define RC522_REG_TestSel1Reg 0x31 -#define RC522_REG_TestSel2Reg 0x32 -#define RC522_REG_TestPinEnReg 0x33 -#define RC522_REG_TestPinValueReg 0x34 -#define RC522_REG_TestBusReg 0x35 -#define RC522_REG_AutoTestReg 0x36 -#define RC522_REG_VersionReg 0x37 -#define RC522_REG_AnalogTestReg 0x38 -#define RC522_REG_TestDAC1Reg 0x39 -#define RC522_REG_TestDAC2Reg 0x3A -#define RC522_REG_TestADCReg 0x3B - -#define RC522_CMD_Idle 0x0 -#define RC522_CMD_Mem 0x1 -#define RC522_CMD_GenerateRandomId 0x2 -#define RC522_CMD_CalcCRC 0x3 -#define RC522_CMD_Transmit 0x4 -#define RC522_CMD_NoCmdChange 0x7 -#define RC522_CMD_Receive 0x8 -#define RC522_CMD_Transceive 0xC -#define RC522_CMD_MFAuthent 0xE -#define RC522_CMD_SoftReset 0xF - -#endif + + +#ifndef __NFC_CHIPS_RC522_INTERNAL_H__ +#define __NFC_CHIPS_RC522_INTERNAL_H__ + +#define RC522_FIFO_SIZE 64 + +#define RC522_REG_CommandReg 0x01 +#define RC522_REG_CommandReg_RcvOff (1 << 5) +#define RC522_REG_CommandReg_PowerDown (1 << 4) + +#define RC522_REG_ComlEnReg 0x02 + +#define RC522_REG_DivlEnReg 0x03 + +#define RC522_REG_ComIrqReg 0x04 + +#define RC522_REG_DivIrqReg 0x05 + +#define RC522_REG_ErrorReg 0x06 + +#define RC522_REG_Status1Reg 0x07 + +#define RC522_REG_Status2Reg 0x08 +#define RC522_REG_Status2Reg_MFCrypto1On (1 << 3) + +#define RC522_REG_FIFODataReg 0x09 + +#define RC522_REG_FIFOLevelReg 0x0A + +#define RC522_REG_WaterLevelReg 0x0B + +#define RC522_REG_ControlReg 0x0C + +#define RC522_REG_BitFramingReg 0x0D + +#define RC522_REG_CollReg 0x0E + +#define RC522_REG_ModeReg 0x11 + +#define RC522_REG_TxModeReg 0x12 +#define RC522_REG_TxModeReg_TxCRCEn (1 << 7) +#define RC522_REG_TxModeReg_TxSpeed_106k (0 << 4) +#define RC522_REG_TxModeReg_TxSpeed_212k (1 << 4) +#define RC522_REG_TxModeReg_TxSpeed_424k (2 << 4) +#define RC522_REG_TxModeReg_TxSpeed_847k (3 << 4) +#define RC522_REG_TxModeReg_TxSpeed_MASK (7 << 4) + +#define RC522_REG_RxModeReg 0x13 +#define RC522_REG_RxModeReg_RxCRCEn (1 << 7) +#define RC522_REG_RxModeReg_RxSpeed_106k (0 << 4) +#define RC522_REG_RxModeReg_RxSpeed_212k (1 << 4) +#define RC522_REG_RxModeReg_RxSpeed_424k (2 << 4) +#define RC522_REG_RxModeReg_RxSpeed_847k (3 << 4) +#define RC522_REG_RxModeReg_RxSpeed_MASK (7 << 4) + +#define RC522_REG_TxControlReg 0x14 +#define RC522_REG_TxControlReg_Tx2RFEn (1 << 1) +#define RC522_REG_TxControlReg_Tx1RFEn (1 << 0) + +#define RC522_REG_TxASKReg 0x15 + +#define RC522_REG_TxSelReg 0x16 + +#define RC522_REG_RxSelReg 0x17 + +#define RC522_REG_RxThresholdReg 0x18 + +#define RC522_REG_DemodReg 0x19 + +#define RC522_REG_MfTxReg 0x1C + +#define RC522_REG_MfRxReg 0x1D +#define RC522_REG_MfRxReg_ParityDisable (1 << 4) + +#define RC522_REG_SerialSpeedReg 0x1F +#define RC522_REG_CRCResultReg 0x21 +#define RC522_REG_ModWidthReg 0x24 +#define RC522_REG_RFCfgReg 0x26 +#define RC522_REG_GsNReg 0x27 +#define RC522_REG_CWGsPReg 0x28 +#define RC522_REG_ModGsPReg 0x29 +#define RC522_REG_TModeReg 0x2A +#define RC522_REG_TPrescalerReg 0x2B +#define RC522_REG_TReloadReg 0x2C +#define RC522_REG_TCounterValReg 0x2E +#define RC522_REG_TestSel1Reg 0x31 +#define RC522_REG_TestSel2Reg 0x32 +#define RC522_REG_TestPinEnReg 0x33 +#define RC522_REG_TestPinValueReg 0x34 +#define RC522_REG_TestBusReg 0x35 +#define RC522_REG_AutoTestReg 0x36 +#define RC522_REG_VersionReg 0x37 +#define RC522_REG_AnalogTestReg 0x38 +#define RC522_REG_TestDAC1Reg 0x39 +#define RC522_REG_TestDAC2Reg 0x3A +#define RC522_REG_TestADCReg 0x3B + +#define RC522_CMD_Idle 0x0 +#define RC522_CMD_Mem 0x1 +#define RC522_CMD_GenerateRandomId 0x2 +#define RC522_CMD_CalcCRC 0x3 +#define RC522_CMD_Transmit 0x4 +#define RC522_CMD_NoCmdChange 0x7 +#define RC522_CMD_Receive 0x8 +#define RC522_CMD_Transceive 0xC +#define RC522_CMD_MFAuthent 0xE +#define RC522_CMD_SoftReset 0xF + +#endif diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index 7a6539e..ca195c5 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -1,241 +1,241 @@ -/*- - * Free/Libre Near Field Communication (NFC) library - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - * - */ - -#include - -#include "rc522.h" -#include "rc522-internal.h" - -#include "nfc/nfc.h" -#include "nfc-internal.h" - -#define LOG_CATEGORY "libnfc.chip.rc522" -#define LOG_GROUP NFC_LOG_GROUP_CHIP - -#define RC522_TIMEOUT 5 - -const nfc_modulation_type rc522_initiator_modulation[] = { NMT_ISO14443A, 0 }; -const nfc_modulation_type rc522_target_modulation[] = { 0 }; - -const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, NBR_212, NBR_106, 0 }; - -struct rc522_chip_data { - const struct rc522_io * io; -}; - -#define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) - -int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { - pnd->chip_data = malloc(sizeof(struct rc522_chip_data)); - if (!pnd->chip_data) { - perror("malloc"); - return NFC_ESOFT; - } - - CHIP_DATA(pnd)->io = io; - return NFC_SUCCESS; -} - -int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { - uint8_t val; - - int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1); - if (ret < 0) { - return ret; - } - - return val; -} - -int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t mask) { - if (mask != 0xFF) { - int oldval = rc522_read_reg(pnd, reg); - if (oldval < 0) { - return oldval; - } - - val = (val & mask) | (oldval & ~mask); - } - - return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1); -} - -int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { - uint8_t txVal, rxVal; - int ret; - - switch (speed) { - case NBR_106: - txVal = RC522_REG_TxModeReg_TxSpeed_106k; - rxVal = RC522_REG_RxModeReg_RxSpeed_106k; - break; - - case NBR_212: - txVal = RC522_REG_TxModeReg_TxSpeed_212k; - rxVal = RC522_REG_RxModeReg_RxSpeed_212k; - break; - - case NBR_424: - txVal = RC522_REG_TxModeReg_TxSpeed_424k; - rxVal = RC522_REG_RxModeReg_RxSpeed_424k; - break; - - case NBR_847: - txVal = RC522_REG_TxModeReg_TxSpeed_847k; - rxVal = RC522_REG_RxModeReg_RxSpeed_847k; - break; - - default: - return NFC_EINVARG; - } - - return - rc522_write_reg(pnd, RC522_REG_TxModeReg, txVal, RC522_REG_TxModeReg_TxSpeed_MASK) || - rc522_write_reg(pnd, RC522_REG_RxModeReg, rxVal, RC522_REG_RxModeReg_RxSpeed_MASK); -} - -int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) -{ - int ret; - - if (nm.nmt != NMT_ISO14443A) { - return NFC_EINVARG; - } - - ret = rc522_set_baud_rate(pnd, nm.nbr); - if (ret < 0) { - return ret; - } - - // TODO - return NFC_ENOTIMPL; -} - -int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { - switch (mode) { - case N_INITIATOR: - *supported_mt = rc522_initiator_modulation; - break; - - case N_TARGET: - *supported_mt = rc522_target_modulation; - break; - - default: - return NFC_EINVARG; - } - - return NFC_SUCCESS; -} - -int rc522_get_supported_baud_rate(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br) { - switch (mode) { - case N_INITIATOR: - switch (nmt) { - case NMT_ISO14443A: - *supported_br = rc522_iso14443a_supported_baud_rates; - break; - - default: - return NFC_EINVARG; - } - break; - - case N_TARGET: - default: - return NFC_EINVARG; - } - - return NFC_SUCCESS; -} - -int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable) { - int ret; - - switch (property) { - case NP_HANDLE_CRC: - if (pnd->bCrc == enable) { - return NFC_SUCCESS; - } - - ret = rc522_write_reg(pnd, RC522_REG_TxModeReg, enable ? ~0 : 0, RC522_REG_TxModeReg_TxCRCEn) || - rc522_write_reg(pnd, RC522_REG_RxModeReg, enable ? ~0 : 0, RC522_REG_RxModeReg_RxCRCEn); - if (ret) { - return ret; - } - - pnd->bCrc = enable; - return NFC_SUCCESS; - - case NP_HANDLE_PARITY: - if (pnd->bPar == enable) { - return NFC_SUCCESS; - } - - ret = rc522_write_reg(pnd, RC522_REG_MfRxReg, enable ? 0 : ~0, RC522_REG_MfRxReg_ParityDisable); - if (ret) { - return ret; - } - - pnd->bPar = enable; - return NFC_SUCCESS; - - case NP_EASY_FRAMING: - pnd->bEasyFraming = enable; - return NFC_SUCCESS; - - case NP_ACTIVATE_FIELD: - return rc522_write_reg(pnd, RC522_REG_TxControlReg, enable ? ~0 : 0, RC522_REG_TxControlReg_Tx2RFEn | RC522_REG_TxControlReg_Tx1RFEn); - - case NP_ACTIVATE_CRYPTO1: - return rc522_write_reg(pnd, RC522_REG_Status2Reg, enable ? ~0 : 0, RC522_REG_Status2Reg_MFCrypto1On); - - case NP_FORCE_ISO14443_A: - // ISO14443-A is the only mode supported by MFRC522 - return NFC_SUCCESS; - - case NP_FORCE_SPEED_106: - if (!enable) { - return NFC_SUCCESS; - } - - return rc522_set_baud_rate(pnd, NBR_106); - - case NP_AUTO_ISO14443_4: - case NP_ACCEPT_INVALID_FRAMES: - case NP_INFINITE_SELECT: - case NP_FORCE_ISO14443_B: - case NP_TIMEOUT_COMMAND: - case NP_TIMEOUT_ATR: - case NP_TIMEOUT_COM: - return NFC_EINVARG; - } - - return NFC_EINVARG; -} - -int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value) { - // TODO - return NFC_ENOTIMPL; -} - -int rc522_idle(struct nfc_device * pnd) { - // Set idle and disable RX demodulator to save energy - return rc522_write_reg(pnd, RC522_REG_CommandReg, RC522_CMD_Idle | RC522_REG_CommandReg_PowerDown, 0xFF); -} - +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#include + +#include "rc522.h" +#include "rc522-internal.h" + +#include "nfc/nfc.h" +#include "nfc-internal.h" + +#define LOG_CATEGORY "libnfc.chip.rc522" +#define LOG_GROUP NFC_LOG_GROUP_CHIP + +#define RC522_TIMEOUT 5 + +const nfc_modulation_type rc522_initiator_modulation[] = { NMT_ISO14443A, 0 }; +const nfc_modulation_type rc522_target_modulation[] = { 0 }; + +const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, NBR_212, NBR_106, 0 }; + +struct rc522_chip_data { + const struct rc522_io * io; +}; + +#define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) + +int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { + pnd->chip_data = malloc(sizeof(struct rc522_chip_data)); + if (!pnd->chip_data) { + perror("malloc"); + return NFC_ESOFT; + } + + CHIP_DATA(pnd)->io = io; + return NFC_SUCCESS; +} + +int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { + uint8_t val; + + int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1); + if (ret < 0) { + return ret; + } + + return val; +} + +int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t mask) { + if (mask != 0xFF) { + int oldval = rc522_read_reg(pnd, reg); + if (oldval < 0) { + return oldval; + } + + val = (val & mask) | (oldval & ~mask); + } + + return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1); +} + +int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { + uint8_t txVal, rxVal; + int ret; + + switch (speed) { + case NBR_106: + txVal = RC522_REG_TxModeReg_TxSpeed_106k; + rxVal = RC522_REG_RxModeReg_RxSpeed_106k; + break; + + case NBR_212: + txVal = RC522_REG_TxModeReg_TxSpeed_212k; + rxVal = RC522_REG_RxModeReg_RxSpeed_212k; + break; + + case NBR_424: + txVal = RC522_REG_TxModeReg_TxSpeed_424k; + rxVal = RC522_REG_RxModeReg_RxSpeed_424k; + break; + + case NBR_847: + txVal = RC522_REG_TxModeReg_TxSpeed_847k; + rxVal = RC522_REG_RxModeReg_RxSpeed_847k; + break; + + default: + return NFC_EINVARG; + } + + return + rc522_write_reg(pnd, RC522_REG_TxModeReg, txVal, RC522_REG_TxModeReg_TxSpeed_MASK) || + rc522_write_reg(pnd, RC522_REG_RxModeReg, rxVal, RC522_REG_RxModeReg_RxSpeed_MASK); +} + +int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) +{ + int ret; + + if (nm.nmt != NMT_ISO14443A) { + return NFC_EINVARG; + } + + ret = rc522_set_baud_rate(pnd, nm.nbr); + if (ret < 0) { + return ret; + } + + // TODO + return NFC_ENOTIMPL; +} + +int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { + switch (mode) { + case N_INITIATOR: + *supported_mt = rc522_initiator_modulation; + break; + + case N_TARGET: + *supported_mt = rc522_target_modulation; + break; + + default: + return NFC_EINVARG; + } + + return NFC_SUCCESS; +} + +int rc522_get_supported_baud_rate(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br) { + switch (mode) { + case N_INITIATOR: + switch (nmt) { + case NMT_ISO14443A: + *supported_br = rc522_iso14443a_supported_baud_rates; + break; + + default: + return NFC_EINVARG; + } + break; + + case N_TARGET: + default: + return NFC_EINVARG; + } + + return NFC_SUCCESS; +} + +int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable) { + int ret; + + switch (property) { + case NP_HANDLE_CRC: + if (pnd->bCrc == enable) { + return NFC_SUCCESS; + } + + ret = rc522_write_reg(pnd, RC522_REG_TxModeReg, enable ? ~0 : 0, RC522_REG_TxModeReg_TxCRCEn) || + rc522_write_reg(pnd, RC522_REG_RxModeReg, enable ? ~0 : 0, RC522_REG_RxModeReg_RxCRCEn); + if (ret) { + return ret; + } + + pnd->bCrc = enable; + return NFC_SUCCESS; + + case NP_HANDLE_PARITY: + if (pnd->bPar == enable) { + return NFC_SUCCESS; + } + + ret = rc522_write_reg(pnd, RC522_REG_MfRxReg, enable ? 0 : ~0, RC522_REG_MfRxReg_ParityDisable); + if (ret) { + return ret; + } + + pnd->bPar = enable; + return NFC_SUCCESS; + + case NP_EASY_FRAMING: + pnd->bEasyFraming = enable; + return NFC_SUCCESS; + + case NP_ACTIVATE_FIELD: + return rc522_write_reg(pnd, RC522_REG_TxControlReg, enable ? ~0 : 0, RC522_REG_TxControlReg_Tx2RFEn | RC522_REG_TxControlReg_Tx1RFEn); + + case NP_ACTIVATE_CRYPTO1: + return rc522_write_reg(pnd, RC522_REG_Status2Reg, enable ? ~0 : 0, RC522_REG_Status2Reg_MFCrypto1On); + + case NP_FORCE_ISO14443_A: + // ISO14443-A is the only mode supported by MFRC522 + return NFC_SUCCESS; + + case NP_FORCE_SPEED_106: + if (!enable) { + return NFC_SUCCESS; + } + + return rc522_set_baud_rate(pnd, NBR_106); + + case NP_AUTO_ISO14443_4: + case NP_ACCEPT_INVALID_FRAMES: + case NP_INFINITE_SELECT: + case NP_FORCE_ISO14443_B: + case NP_TIMEOUT_COMMAND: + case NP_TIMEOUT_ATR: + case NP_TIMEOUT_COM: + return NFC_EINVARG; + } + + return NFC_EINVARG; +} + +int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value) { + // TODO + return NFC_ENOTIMPL; +} + +int rc522_idle(struct nfc_device * pnd) { + // Set idle and disable RX demodulator to save energy + return rc522_write_reg(pnd, RC522_REG_CommandReg, RC522_CMD_Idle | RC522_REG_CommandReg_PowerDown, 0xFF); +} + diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index 41153d2..4a2a90a 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -1,35 +1,37 @@ -/*- - * Free/Libre Near Field Communication (NFC) library - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by the - * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see - * - */ - -#ifndef __NFC_CHIPS_RC522_H__ -#define __NFC_CHIPS_RC522_H__ - -#include -#include - -struct rc522_io { - int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size); - int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size); -}; - -int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); -int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); -int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); -int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable); - -#endif +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#ifndef __NFC_CHIPS_RC522_H__ +#define __NFC_CHIPS_RC522_H__ + +#include +#include + +struct rc522_io { + int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size); + int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size); +}; + +int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); +int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); +int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); +int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable); +int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value); +int rc522_idle(struct nfc_device * pnd); + +#endif From 62043400591a5577ea36516176b44062d84ddac3 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Mon, 15 Jun 2015 22:45:11 +0200 Subject: [PATCH 05/15] Improved code for PN532 driver --- .gitignore | 1 + libnfc/buses/uart.c | 13 +++++ libnfc/buses/uart.h | 1 + libnfc/chips/pn53x.c | 107 +++++++++++++----------------------- libnfc/drivers/pn532_uart.c | 93 ++++++++++++------------------- libnfc/log.h | 2 + libnfc/nfc-internal.h | 18 ++++-- 7 files changed, 103 insertions(+), 132 deletions(-) diff --git a/.gitignore b/.gitignore index 3a49783..1b216a0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.la *.lo *.o +*.stackdump *~ Doxyfile INSTALL diff --git a/libnfc/buses/uart.c b/libnfc/buses/uart.c index 7f14532..05bfec0 100644 --- a/libnfc/buses/uart.c +++ b/libnfc/buses/uart.c @@ -430,3 +430,16 @@ oom: return res; } + +void +uart_list_free(char ** acPorts) +{ + char *acPort; + size_t iDevice = 0; + + while ((acPort = acPorts[iDevice++])) { + free((void *)acPort); + } + + free(acPorts); +} diff --git a/libnfc/buses/uart.h b/libnfc/buses/uart.h index 49b822a..8519304 100644 --- a/libnfc/buses/uart.h +++ b/libnfc/buses/uart.h @@ -58,5 +58,6 @@ int uart_receive(serial_port sp, uint8_t *pbtRx, const size_t szRx, void *ab int uart_send(serial_port sp, const uint8_t *pbtTx, const size_t szTx, int timeout); char **uart_list_ports(void); +void uart_list_free(char **acPorts); #endif // __NFC_BUS_UART_H__ diff --git a/libnfc/chips/pn53x.c b/libnfc/chips/pn53x.c index e91b3bd..d76006a 100644 --- a/libnfc/chips/pn53x.c +++ b/libnfc/chips/pn53x.c @@ -333,10 +333,7 @@ pn53x_transceive(struct nfc_device *pnd, const uint8_t *pbtTx, const size_t szTx }; if (res < 0) { - pnd->last_error = res; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Chip error: \"%s\" (%02x), returned error: \"%s\" (%d))", pn53x_strerror(pnd), CHIP_DATA(pnd)->last_status_byte, nfc_strerror(pnd), res); - } else { - pnd->last_error = 0; } return res; } @@ -1064,8 +1061,7 @@ pn53x_initiator_select_passive_target_ext(struct nfc_device *pnd, if (nm.nmt == NMT_ISO14443BI || nm.nmt == NMT_ISO14443B2SR || nm.nmt == NMT_ISO14443B2CT) { if (CHIP_DATA(pnd)->type == RCS360) { // TODO add support for RC-S360, at the moment it refuses to send raw frames without a first select - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } // No native support in InListPassiveTarget so we do discovery by hand if ((res = nfc_device_set_property_bool(pnd, NP_FORCE_ISO14443_B, true)) < 0) { @@ -1151,8 +1147,7 @@ pn53x_initiator_select_passive_target_ext(struct nfc_device *pnd, } else { const pn53x_modulation pm = pn53x_nm_to_pm(nm); if ((PM_UNDEFINED == pm) || (NBR_UNDEFINED == nm.nbr)) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } if ((res = pn53x_InListPassiveTarget(pnd, pm, 1, pbtInitData, szInitData, abtTargetsData, &szTargetsData, timeout)) <= 0) @@ -1175,8 +1170,7 @@ pn53x_initiator_select_passive_target_ext(struct nfc_device *pnd, } } if (pn53x_current_target_new(pnd, &nttmp) == NULL) { - pnd->last_error = NFC_ESOFT; - return pnd->last_error; + return NFC_ESOFT; } // Is a tag info struct available if (pnt) { @@ -1209,8 +1203,7 @@ pn53x_initiator_poll_target(struct nfc_device *pnd, for (size_t n = 0; n < szModulations; n++) { const pn53x_target_type ptt = pn53x_nm_to_ptt(pnmModulations[n]); if (PTT_UNDEFINED == ptt) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } apttTargetTypes[szTargetTypes] = ptt; if ((pnd->bAutoIso14443_4) && (ptt == PTT_MIFARE)) { // Hack to have ATS @@ -1227,18 +1220,18 @@ pn53x_initiator_poll_target(struct nfc_device *pnd, return res; switch (res) { case 0: - return pnd->last_error = NFC_SUCCESS; + return NFC_SUCCESS; break; case 1: *pnt = ntTargets[0]; if (pn53x_current_target_new(pnd, pnt) == NULL) { - return pnd->last_error = NFC_ESOFT; + return NFC_ESOFT; } return res; case 2: *pnt = ntTargets[1]; // We keep the selected one if (pn53x_current_target_new(pnd, pnt) == NULL) { - return pnd->last_error = NFC_ESOFT; + return NFC_ESOFT; } return res; default: @@ -1259,8 +1252,8 @@ pn53x_initiator_poll_target(struct nfc_device *pnd, const int timeout_ms = uiPeriod * 150; if ((res = pn53x_initiator_select_passive_target_ext(pnd, pnmModulations[n], pbtInitiatorData, szInitiatorData, pnt, timeout_ms)) < 0) { - if (pnd->last_error != NFC_ETIMEOUT) { - result = pnd->last_error; + if (res != NFC_ETIMEOUT) { + result = res; goto end; } } else { @@ -1401,8 +1394,7 @@ pn53x_initiator_transceive_bytes(struct nfc_device *pnd, const uint8_t *pbtTx, c // We can not just send bytes without parity if while the PN53X expects we handled them if (!pnd->bPar) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } // Copy the data into the command frame @@ -1419,16 +1411,14 @@ pn53x_initiator_transceive_bytes(struct nfc_device *pnd, const uint8_t *pbtTx, c // To transfer command frames bytes we can not have any leading bits, reset this to zero if ((res = pn53x_set_tx_bits(pnd, 0)) < 0) { - pnd->last_error = res; - return pnd->last_error; + return res; } // Send the frame to the PN53X chip and get the answer // We have to give the amount of bytes + (the two command bytes 0xD4, 0x42) uint8_t abtRx[PN53x_EXTENDED_FRAME__DATA_MAX_LEN]; if ((res = pn53x_transceive(pnd, abtCmd, szTx + szExtraTxLen, abtRx, sizeof(abtRx), timeout)) < 0) { - pnd->last_error = res; - return pnd->last_error; + return res; } const size_t szRxLen = (size_t)res - 1; if (pbtRx != NULL) { @@ -1538,18 +1528,15 @@ pn53x_initiator_transceive_bits_timed(struct nfc_device *pnd, const uint8_t *pbt // Sorry, no arbitrary parity bits support for now if (!pnd->bPar) { - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } // Sorry, no easy framing support if (pnd->bEasyFraming) { - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } // TODO CRC support but it probably doesn't make sense for (szTxBits % 8 != 0) ... if (pnd->bCrc) { - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } __pn53x_init_timer(pnd, *cycles); @@ -1636,14 +1623,12 @@ pn53x_initiator_transceive_bytes_timed(struct nfc_device *pnd, const uint8_t *pb // We can not just send bytes without parity while the PN53X expects we handled them if (!pnd->bPar) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } // Sorry, no easy framing support // TODO to be changed once we'll provide easy framing support from libnfc itself... if (pnd->bEasyFraming) { - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } uint8_t txmode = 0; @@ -2046,13 +2031,13 @@ pn53x_initiator_target_is_present(struct nfc_device *pnd, const nfc_target *pnt) // Check if there is a saved target if (CHIP_DATA(pnd)->current_target == NULL) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "target_is_present(): no saved target"); - return pnd->last_error = NFC_EINVARG; + return NFC_EINVARG; } // Check if the argument target nt is equals to current saved target if ((pnt != NULL) && (!pn53x_current_target_is(pnd, pnt))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "target_is_present(): another target"); - return pnd->last_error = NFC_ETGRELEASED; + return NFC_ETGRELEASED; } // Ping target @@ -2096,7 +2081,7 @@ pn53x_initiator_target_is_present(struct nfc_device *pnd, const nfc_target *pnt) } if (ret == NFC_ETGRELEASED) pn53x_current_target_free(pnd); - return pnd->last_error = ret; + return ret; } #define SAK_ISO14443_4_COMPLIANT 0x20 @@ -2115,8 +2100,7 @@ pn53x_target_init(struct nfc_device *pnd, nfc_target *pnt, uint8_t *pbtRx, const case NMT_ISO14443A: ptm = PTM_PASSIVE_ONLY; if ((pnt->nti.nai.abtUid[0] != 0x08) || (pnt->nti.nai.szUidLen != 4)) { - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } pn53x_set_parameters(pnd, PARAM_AUTO_ATR_RES, false); if (CHIP_DATA(pnd)->type == PN532) { // We have a PN532 @@ -2144,8 +2128,7 @@ pn53x_target_init(struct nfc_device *pnd, nfc_target *pnt, uint8_t *pbtRx, const case NMT_ISO14443B2SR: case NMT_ISO14443B2CT: case NMT_JEWEL: - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } // Let the PN53X be activated by the RF level detector from power down mode @@ -2245,8 +2228,7 @@ pn53x_target_init(struct nfc_device *pnd, nfc_target *pnt, uint8_t *pbtRx, const case NMT_ISO14443B2SR: case NMT_ISO14443B2CT: case NMT_JEWEL: - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } bool targetActivated = false; @@ -2308,8 +2290,7 @@ pn53x_target_init(struct nfc_device *pnd, nfc_target *pnt, uint8_t *pbtRx, const pnt->nti.ndi.ndm = ndm; // Update DEP mode } if (pn53x_current_target_new(pnd, pnt) == NULL) { - pnd->last_error = NFC_ESOFT; - return pnd->last_error; + return NFC_ESOFT; } if (ptm & PTM_ISO14443_4_PICC_ONLY) { @@ -2387,8 +2368,7 @@ pn53x_target_receive_bytes(struct nfc_device *pnd, uint8_t *pbtRx, const size_t break; } else { // TODO Support EasyFraming for other cases by software - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } } // NO BREAK @@ -2410,7 +2390,7 @@ pn53x_target_receive_bytes(struct nfc_device *pnd, uint8_t *pbtRx, const size_t size_t szRx = sizeof(abtRx); int res = 0; if ((res = pn53x_transceive(pnd, abtCmd, sizeof(abtCmd), abtRx, szRx, timeout)) < 0) - return pnd->last_error; + return res; szRx = (size_t) res; // Save the received bytes count szRx -= 1; @@ -2492,8 +2472,7 @@ pn53x_target_send_bytes(struct nfc_device *pnd, const uint8_t *pbtTx, const size break; } else { // TODO Support EasyFraming for other cases by software - pnd->last_error = NFC_ENOTIMPL; - return pnd->last_error; + return NFC_ENOTIMPL; } } // NO BREAK @@ -2642,8 +2621,7 @@ pn532_SAMConfiguration(struct nfc_device *pnd, const pn532_sam_mode sam_mode, in if (CHIP_DATA(pnd)->type != PN532) { // This function is not supported by pn531 neither pn533 - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } switch (sam_mode) { @@ -2657,8 +2635,7 @@ pn532_SAMConfiguration(struct nfc_device *pnd, const pn532_sam_mode sam_mode, in szCmd = 3; break; default: - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } CHIP_DATA(pnd)->sam_mode = sam_mode; return (pn53x_transceive(pnd, abtCmd, szCmd, NULL, 0, timeout)); @@ -2709,15 +2686,13 @@ pn53x_InListPassiveTarget(struct nfc_device *pnd, case PM_ISO14443B_106: if (!(pnd->btSupportByte & SUPPORT_ISO14443B)) { // Eg. Some PN532 doesn't support type B! - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } break; case PM_JEWEL_106: if (CHIP_DATA(pnd)->type == PN531) { // These modulations are not supported by pn531 - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } break; case PM_ISO14443B_212: @@ -2725,13 +2700,11 @@ pn53x_InListPassiveTarget(struct nfc_device *pnd, case PM_ISO14443B_847: if ((CHIP_DATA(pnd)->type != PN533) || (!(pnd->btSupportByte & SUPPORT_ISO14443B))) { // These modulations are not supported by pn531 neither pn532 - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } break; case PM_UNDEFINED: - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } abtCmd[2] = pmInitModulation; // BrTy, the type of init modulation used for polling a passive tag @@ -2804,8 +2777,7 @@ pn53x_InAutoPoll(struct nfc_device *pnd, size_t szTargetFound = 0; if (CHIP_DATA(pnd)->type != PN532) { // This function is not supported by pn531 neither pn533 - pnd->last_error = NFC_EDEVNOTSUPP; - return pnd->last_error; + return NFC_EDEVNOTSUPP; } // InAutoPoll frame looks like this { 0xd4, 0x60, 0x0f, 0x01, 0x00 } => { direction, command, pollnr, period, types... } @@ -2903,8 +2875,7 @@ pn53x_InJumpForDEP(struct nfc_device *pnd, break; case NBR_847: case NBR_UNDEFINED: - pnd->last_error = NFC_EINVARG; - return pnd->last_error; + return NFC_EINVARG; } if (pbtNFCID3i) { @@ -3029,25 +3000,25 @@ pn53x_TgInitAsTarget(struct nfc_device *pnd, pn53x_target_mode ptm, int pn53x_check_ack_frame(struct nfc_device *pnd, const uint8_t *pbtRxFrame, const size_t szRxFrameLen) { + (void)pnd; if (szRxFrameLen >= sizeof(pn53x_ack_frame)) { if (0 == memcmp(pbtRxFrame, pn53x_ack_frame, sizeof(pn53x_ack_frame))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PN53x ACKed"); return NFC_SUCCESS; } } - pnd->last_error = NFC_EIO; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unexpected PN53x reply!"); - return pnd->last_error; + return NFC_EIO; } int pn53x_check_error_frame(struct nfc_device *pnd, const uint8_t *pbtRxFrame, const size_t szRxFrameLen) { + (void)pnd; if (szRxFrameLen >= sizeof(pn53x_error_frame)) { if (0 == memcmp(pbtRxFrame, pn53x_error_frame, sizeof(pn53x_error_frame))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PN53x sent an error frame"); - pnd->last_error = NFC_EIO; - return pnd->last_error; + return NFC_EIO; } } return NFC_SUCCESS; diff --git a/libnfc/drivers/pn532_uart.c b/libnfc/drivers/pn532_uart.c index 6dd4a21..69c4c89 100644 --- a/libnfc/drivers/pn532_uart.c +++ b/libnfc/drivers/pn532_uart.c @@ -94,13 +94,9 @@ pn532_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, PN532_UART_DRIVER_NAME, acPort, PN532_UART_DEFAULT_SPEED); nfc_device *pnd = nfc_device_new(context, connstring); if (!pnd) { - perror("malloc"); + perror("nfc_device_new"); uart_close(sp); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); + uart_list_free(acPorts); return 0; } pnd->driver = &pn532_uart_driver; @@ -109,25 +105,17 @@ pn532_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const perror("malloc"); uart_close(sp); nfc_device_free(pnd); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); + uart_list_free(acPorts); return 0; } DRIVER_DATA(pnd)->port = sp; // Alloc and init chip's data if (pn53x_data_new(pnd, &pn532_uart_io) == NULL) { - perror("malloc"); + perror("pn53x_data_new"); uart_close(DRIVER_DATA(pnd)->port); nfc_device_free(pnd); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); + uart_list_free(acPorts); return 0; } // SAMConfiguration command if needed to wakeup the chip and pn53x_SAMConfiguration check if the chip is a PN532 @@ -141,11 +129,7 @@ pn532_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const uart_close(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); + uart_list_free(acPorts); return 0; } #else @@ -169,11 +153,7 @@ pn532_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const break; } } - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); + uart_list_free(acPorts); return device_found; } @@ -344,29 +324,26 @@ pn532_uart_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, in size_t szFrame = 0; if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) { - pnd->last_error = res; - return pnd->last_error; + return res; } res = uart_send(DRIVER_DATA(pnd)->port, abtFrame, szFrame, timeout); if (res != 0) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to transmit data. (TX)"); - pnd->last_error = res; - return pnd->last_error; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to transmit data. (TX)"); + return res; } uint8_t abtRxBuf[PN53x_ACK_FRAME__LEN]; res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, sizeof(abtRxBuf), 0, timeout); if (res != 0) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Unable to read ACK"); - pnd->last_error = res; - return pnd->last_error; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Unable to read ACK"); + return res; } if (pn53x_check_ack_frame(pnd, abtRxBuf, sizeof(abtRxBuf)) == 0) { // The PN53x is running the sent command } else { - return pnd->last_error; + return NFC_ECHIP; } return NFC_SUCCESS; } @@ -384,21 +361,21 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in abort_p = (void *) & (DRIVER_DATA(pnd)->abort_flag); #endif - pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 5, abort_p, timeout); + int res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 5, abort_p, timeout); - if (abort_p && (NFC_EOPABORTED == pnd->last_error)) { + if (abort_p && (NFC_EOPABORTED == res)) { pn532_uart_ack(pnd); - return NFC_EOPABORTED; + return res; } - if (pnd->last_error < 0) { + if (res < 0) { goto error; } const uint8_t pn53x_preamble[3] = { 0x00, 0x00, 0xff }; if (0 != (memcmp(abtRxBuf, pn53x_preamble, 3))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } @@ -406,12 +383,12 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in // Error frame uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 3, 0, timeout); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Application level error detected"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } else if ((0xff == abtRxBuf[3]) && (0xff == abtRxBuf[4])) { // Extended frame - pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 3, 0, timeout); - if (pnd->last_error != 0) { + res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 3, 0, timeout); + if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } @@ -419,7 +396,7 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in len = (abtRxBuf[0] << 8) + abtRxBuf[1] - 2; if (((abtRxBuf[0] + abtRxBuf[1] + abtRxBuf[2]) % 256) != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } } else { @@ -427,7 +404,7 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in if (256 != (abtRxBuf[3] + abtRxBuf[4])) { // TODO: Retry log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } @@ -437,39 +414,39 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in if (len > szDataLen) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", szDataLen, len); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } // TFI + PD0 (CC+1) - pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); - if (pnd->last_error != 0) { + res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); + if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } if (abtRxBuf[0] != 0xD5) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } if (abtRxBuf[1] != CHIP_DATA(pnd)->last_command + 1) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Command Code verification failed"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } if (len) { - pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, pbtData, len, 0, timeout); - if (pnd->last_error != 0) { + res = uart_receive(DRIVER_DATA(pnd)->port, pbtData, len, 0, timeout); + if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } } - pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); - if (pnd->last_error != 0) { + res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); + if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } @@ -482,20 +459,20 @@ pn532_uart_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, in if (btDCS != abtRxBuf[0]) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Data checksum mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } if (0x00 != abtRxBuf[1]) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame postamble mismatch"); - pnd->last_error = NFC_EIO; + res = NFC_EIO; goto error; } // The PN53x command is done and we successfully received the reply return len; error: uart_flush_input(DRIVER_DATA(pnd)->port, true); - return pnd->last_error; + return res; } int diff --git a/libnfc/log.h b/libnfc/log.h index cb1d470..f3625fc 100644 --- a/libnfc/log.h +++ b/libnfc/log.h @@ -27,6 +27,8 @@ #ifndef __LOG_H__ #define __LOG_H__ +#include + #ifdef HAVE_CONFIG_H # include "config.h" #endif // HAVE_CONFIG_H diff --git a/libnfc/nfc-internal.h b/libnfc/nfc-internal.h index 4fa2fc2..37066ad 100644 --- a/libnfc/nfc-internal.h +++ b/libnfc/nfc-internal.h @@ -44,13 +44,19 @@ * @macro HAL * @brief Execute corresponding driver function if exists. */ -#define HAL( FUNCTION, ... ) pnd->last_error = 0; \ - if (pnd->driver->FUNCTION) { \ - return pnd->driver->FUNCTION( __VA_ARGS__ ); \ - } else { \ +#define HAL( FUNCTION, ... ) do { \ + if (!pnd->driver->FUNCTION) { \ pnd->last_error = NFC_EDEVNOTSUPP; \ - return false; \ - } + return pnd->last_error; \ + } \ + int __ret = pnd->driver->FUNCTION( __VA_ARGS__ ); \ + if (__ret < 0) { \ + pnd->last_error = __ret; \ + } else { \ + pnd->last_error = NFC_SUCCESS; \ + } \ + return __ret; \ +} while (0); #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) From 053dc8d5f81046bbcbb47d343aa409e697761378 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Mon, 15 Jun 2015 22:46:47 +0200 Subject: [PATCH 06/15] Basic support for RC522 detection --- libnfc/Makefile.am | 4 +- libnfc/chips/rc522-internal.h | 168 +++++++++++---------- libnfc/chips/rc522.c | 272 ++++++++++++++++++++++++++++++---- libnfc/chips/rc522.h | 7 +- libnfc/drivers/rc522_uart.c | 226 ++++++++++++++-------------- libnfc/nfc.c | 6 + libnfc/timing.c | 75 ++++++++++ libnfc/timing.h | 48 ++++++ 8 files changed, 587 insertions(+), 219 deletions(-) create mode 100644 libnfc/timing.c create mode 100644 libnfc/timing.h diff --git a/libnfc/Makefile.am b/libnfc/Makefile.am index dc3f27f..f3c5f20 100644 --- a/libnfc/Makefile.am +++ b/libnfc/Makefile.am @@ -20,7 +20,9 @@ libnfc_la_SOURCES = \ log-internal.h \ mirror-subr.h \ nfc-internal.h \ - target-subr.h + target-subr.h \ + timing.h \ + timing.c libnfc_la_LDFLAGS = -no-undefined -version-info 5:1:0 -export-symbols-regex '^nfc_|^iso14443a_|^iso14443b_|^str_nfc_|pn53x_transceive|pn532_SAMConfiguration|pn53x_read_register|pn53x_write_register' libnfc_la_CFLAGS = @DRIVERS_CFLAGS@ diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index 7d2923c..23a91e4 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -3,108 +3,124 @@ #ifndef __NFC_CHIPS_RC522_INTERNAL_H__ #define __NFC_CHIPS_RC522_INTERNAL_H__ -#define RC522_FIFO_SIZE 64 +#define FIFO_SIZE 64 -#define RC522_REG_CommandReg 0x01 -#define RC522_REG_CommandReg_RcvOff (1 << 5) -#define RC522_REG_CommandReg_PowerDown (1 << 4) +typedef enum { + RC522_UNKNOWN = 0x00, + FM17522 = 0x88, + MFRC522_V1 = 0x91, + MFRC522_V2 = 0x92 +} rc522_type; -#define RC522_REG_ComlEnReg 0x02 +typedef enum { + CMD_IDLE = 0x0, + CMD_MEM = 0x1, + CMD_GENRANDOMID = 0x2, + CMD_CALCCRC = 0x3, + CMD_TRANSMIT = 0x4, + CMD_NOCMDCHANGE = 0x7, + CMD_RECEIVE = 0x8, + CMD_TRANSCEIVE = 0xC, + CMD_MFAUTHENT = 0xE, + CMD_SOFTRESET = 0xF +} rc522_cmd; -#define RC522_REG_DivlEnReg 0x03 +#define REG_CommandReg 0x01 +#define REG_CommandReg_RcvOff (1 << 5) +#define REG_CommandReg_PowerDown (1 << 4) -#define RC522_REG_ComIrqReg 0x04 +#define REG_ComlEnReg 0x02 -#define RC522_REG_DivIrqReg 0x05 +#define REG_DivlEnReg 0x03 -#define RC522_REG_ErrorReg 0x06 +#define REG_ComIrqReg 0x04 -#define RC522_REG_Status1Reg 0x07 +#define REG_DivIrqReg 0x05 +#define REG_DivIrqReg_MfinActIRq (1 << 4) +#define REG_DivIrqReg_CRCIRq (1 << 2) -#define RC522_REG_Status2Reg 0x08 -#define RC522_REG_Status2Reg_MFCrypto1On (1 << 3) +#define REG_ErrorReg 0x06 -#define RC522_REG_FIFODataReg 0x09 +#define REG_Status1Reg 0x07 -#define RC522_REG_FIFOLevelReg 0x0A +#define REG_Status2Reg 0x08 +#define REG_Status2Reg_MFCrypto1On (1 << 3) -#define RC522_REG_WaterLevelReg 0x0B +#define REG_FIFODataReg 0x09 -#define RC522_REG_ControlReg 0x0C +#define REG_FIFOLevelReg 0x0A -#define RC522_REG_BitFramingReg 0x0D +#define REG_WaterLevelReg 0x0B -#define RC522_REG_CollReg 0x0E +#define REG_ControlReg 0x0C -#define RC522_REG_ModeReg 0x11 +#define REG_BitFramingReg 0x0D -#define RC522_REG_TxModeReg 0x12 -#define RC522_REG_TxModeReg_TxCRCEn (1 << 7) -#define RC522_REG_TxModeReg_TxSpeed_106k (0 << 4) -#define RC522_REG_TxModeReg_TxSpeed_212k (1 << 4) -#define RC522_REG_TxModeReg_TxSpeed_424k (2 << 4) -#define RC522_REG_TxModeReg_TxSpeed_847k (3 << 4) -#define RC522_REG_TxModeReg_TxSpeed_MASK (7 << 4) +#define REG_CollReg 0x0E -#define RC522_REG_RxModeReg 0x13 -#define RC522_REG_RxModeReg_RxCRCEn (1 << 7) -#define RC522_REG_RxModeReg_RxSpeed_106k (0 << 4) -#define RC522_REG_RxModeReg_RxSpeed_212k (1 << 4) -#define RC522_REG_RxModeReg_RxSpeed_424k (2 << 4) -#define RC522_REG_RxModeReg_RxSpeed_847k (3 << 4) -#define RC522_REG_RxModeReg_RxSpeed_MASK (7 << 4) +#define REG_ModeReg 0x11 -#define RC522_REG_TxControlReg 0x14 -#define RC522_REG_TxControlReg_Tx2RFEn (1 << 1) -#define RC522_REG_TxControlReg_Tx1RFEn (1 << 0) +#define REG_TxModeReg 0x12 +#define REG_TxModeReg_TxCRCEn (1 << 7) +#define REG_TxModeReg_TxSpeed_106k (0 << 4) +#define REG_TxModeReg_TxSpeed_212k (1 << 4) +#define REG_TxModeReg_TxSpeed_424k (2 << 4) +#define REG_TxModeReg_TxSpeed_847k (3 << 4) +#define REG_TxModeReg_TxSpeed_MASK (7 << 4) -#define RC522_REG_TxASKReg 0x15 +#define REG_RxModeReg 0x13 +#define REG_RxModeReg_RxCRCEn (1 << 7) +#define REG_RxModeReg_RxSpeed_106k (0 << 4) +#define REG_RxModeReg_RxSpeed_212k (1 << 4) +#define REG_RxModeReg_RxSpeed_424k (2 << 4) +#define REG_RxModeReg_RxSpeed_847k (3 << 4) +#define REG_RxModeReg_RxSpeed_MASK (7 << 4) -#define RC522_REG_TxSelReg 0x16 +#define REG_TxControlReg 0x14 +#define REG_TxControlReg_Tx2RFEn (1 << 1) +#define REG_TxControlReg_Tx1RFEn (1 << 0) -#define RC522_REG_RxSelReg 0x17 +#define REG_TxASKReg 0x15 -#define RC522_REG_RxThresholdReg 0x18 +#define REG_TxSelReg 0x16 -#define RC522_REG_DemodReg 0x19 +#define REG_RxSelReg 0x17 -#define RC522_REG_MfTxReg 0x1C +#define REG_RxThresholdReg 0x18 -#define RC522_REG_MfRxReg 0x1D -#define RC522_REG_MfRxReg_ParityDisable (1 << 4) +#define REG_DemodReg 0x19 -#define RC522_REG_SerialSpeedReg 0x1F -#define RC522_REG_CRCResultReg 0x21 -#define RC522_REG_ModWidthReg 0x24 -#define RC522_REG_RFCfgReg 0x26 -#define RC522_REG_GsNReg 0x27 -#define RC522_REG_CWGsPReg 0x28 -#define RC522_REG_ModGsPReg 0x29 -#define RC522_REG_TModeReg 0x2A -#define RC522_REG_TPrescalerReg 0x2B -#define RC522_REG_TReloadReg 0x2C -#define RC522_REG_TCounterValReg 0x2E -#define RC522_REG_TestSel1Reg 0x31 -#define RC522_REG_TestSel2Reg 0x32 -#define RC522_REG_TestPinEnReg 0x33 -#define RC522_REG_TestPinValueReg 0x34 -#define RC522_REG_TestBusReg 0x35 -#define RC522_REG_AutoTestReg 0x36 -#define RC522_REG_VersionReg 0x37 -#define RC522_REG_AnalogTestReg 0x38 -#define RC522_REG_TestDAC1Reg 0x39 -#define RC522_REG_TestDAC2Reg 0x3A -#define RC522_REG_TestADCReg 0x3B +#define REG_MfTxReg 0x1C -#define RC522_CMD_Idle 0x0 -#define RC522_CMD_Mem 0x1 -#define RC522_CMD_GenerateRandomId 0x2 -#define RC522_CMD_CalcCRC 0x3 -#define RC522_CMD_Transmit 0x4 -#define RC522_CMD_NoCmdChange 0x7 -#define RC522_CMD_Receive 0x8 -#define RC522_CMD_Transceive 0xC -#define RC522_CMD_MFAuthent 0xE -#define RC522_CMD_SoftReset 0xF +#define REG_MfRxReg 0x1D +#define REG_MfRxReg_ParityDisable (1 << 4) + +#define REG_SerialSpeedReg 0x1F +#define REG_CRCResultReg 0x21 +#define REG_ModWidthReg 0x24 +#define REG_RFCfgReg 0x26 +#define REG_GsNReg 0x27 +#define REG_CWGsPReg 0x28 +#define REG_ModGsPReg 0x29 +#define REG_TModeReg 0x2A +#define REG_TPrescalerReg 0x2B +#define REG_TReloadReg 0x2C +#define REG_TCounterValReg 0x2E +#define REG_TestSel1Reg 0x31 +#define REG_TestSel2Reg 0x32 +#define REG_TestPinEnReg 0x33 +#define REG_TestPinValueReg 0x34 +#define REG_TestBusReg 0x35 + +#define REG_AutoTestReg 0x36 +#define REG_AutoTestReg_SelfTest_Disabled (0x0 << 0) +#define REG_AutoTestReg_SelfTest_Enabled (0x9 << 0) +#define REG_AutoTestReg_SelfTest_MASK (0xF << 0) + +#define REG_VersionReg 0x37 +#define REG_AnalogTestReg 0x38 +#define REG_TestDAC1Reg 0x39 +#define REG_TestDAC2Reg 0x3A +#define REG_TestADCReg 0x3B #endif diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index ca195c5..5ba222f 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -23,12 +23,11 @@ #include "nfc/nfc.h" #include "nfc-internal.h" +#include "timing.h" #define LOG_CATEGORY "libnfc.chip.rc522" #define LOG_GROUP NFC_LOG_GROUP_CHIP -#define RC522_TIMEOUT 5 - const nfc_modulation_type rc522_initiator_modulation[] = { NMT_ISO14443A, 0 }; const nfc_modulation_type rc522_target_modulation[] = { 0 }; @@ -36,6 +35,7 @@ const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, struct rc522_chip_data { const struct rc522_io * io; + rc522_type version; }; #define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) @@ -48,14 +48,51 @@ int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { } CHIP_DATA(pnd)->io = io; + CHIP_DATA(pnd)->version = RC522_UNKNOWN; + return NFC_SUCCESS; +} + +void rc522_data_free(struct nfc_device * pnd) { + free(pnd->chip_data); +} + +int rc522_read_bulk(struct nfc_device * pnd, uint8_t reg, uint8_t * val, size_t len) { + int ret = CHIP_DATA(pnd)->io->read(pnd, reg, val, len); + if (ret) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to read register %02X!", reg); + return ret; + } + +#ifdef LOG + char action[8]; + snprintf(action, sizeof(action), "RD %02X", reg); + LOG_HEX(NFC_LOG_GROUP_CHIP, action, val, len); +#endif + + return NFC_SUCCESS; +} + +int rc522_write_bulk(struct nfc_device * pnd, uint8_t reg, const uint8_t * val, size_t len) { + int ret = CHIP_DATA(pnd)->io->write(pnd, reg, val, len); + if (ret) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to write register %02X!", reg); + return ret; + } + +#ifdef LOG + char action[8]; + snprintf(action, sizeof(action), "WR %02X", reg); + LOG_HEX(NFC_LOG_GROUP_CHIP, action, val, len); +#endif + return NFC_SUCCESS; } int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { uint8_t val; + int ret; - int ret = CHIP_DATA(pnd)->io->read(pnd, reg, &val, 1); - if (ret < 0) { + if ((ret = rc522_read_bulk(pnd, reg, &val, 1)) < 0) { return ret; } @@ -72,32 +109,99 @@ int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t m val = (val & mask) | (oldval & ~mask); } - return CHIP_DATA(pnd)->io->write(pnd, reg, &val, 1); + return rc522_write_bulk(pnd, reg, &val, 1); +} + +int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { + bool needsRX = false; + + // Disabling RX saves energy, so based on the command we'll also update the RxOff flag + switch (cmd) { + case CMD_IDLE: + case CMD_MEM: + case CMD_GENRANDOMID: + case CMD_CALCCRC: + case CMD_TRANSMIT: + case CMD_SOFTRESET: + break; + + case CMD_RECEIVE: + case CMD_TRANSCEIVE: + case CMD_MFAUTHENT: + needsRX = true; + break; + + case CMD_NOCMDCHANGE: + return NFC_SUCCESS; + + default: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to execute non-existant command: %02X", cmd); + pnd->last_error = NFC_ESOFT; + return pnd->last_error; + } + + uint8_t regval = cmd; + if (!needsRX) { + regval |= REG_CommandReg_RcvOff; + } + + return rc522_write_reg(pnd, REG_CommandReg, regval, 0xFF); +} + +int rc522_wait_wakeup(struct nfc_device * pnd) { + // NXP does not mention in the datasheet how much time does it take for RC522 to come back to life, so we'll wait up to 50ms + timeout_t to; + timeout_init(&to, 50); + + // rc522_read_reg updates last_error. Backup it to ignore timeouts + int last_error = pnd->last_error; + + while (timeout_check(&to)) { + int ret = rc522_read_reg(pnd, REG_CommandReg); + if (ret < 0 && ret != NFC_ETIMEOUT) { + return ret; + } + + // If the powerdown bit is zero the RC522 is ready to kick asses! + if ((ret & REG_CommandReg_PowerDown) == 0) { + pnd->last_error = last_error; + return NFC_SUCCESS; + } + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_wait_wakeup timeout!"); + pnd->last_error = NFC_ETIMEOUT; + return pnd->last_error; +} + +int rc522_soft_reset(struct nfc_device * pnd) { + return + rc522_start_command(pnd, CMD_SOFTRESET) || + rc522_wait_wakeup(pnd); } int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { uint8_t txVal, rxVal; - int ret; switch (speed) { case NBR_106: - txVal = RC522_REG_TxModeReg_TxSpeed_106k; - rxVal = RC522_REG_RxModeReg_RxSpeed_106k; + txVal = REG_TxModeReg_TxSpeed_106k; + rxVal = REG_RxModeReg_RxSpeed_106k; break; case NBR_212: - txVal = RC522_REG_TxModeReg_TxSpeed_212k; - rxVal = RC522_REG_RxModeReg_RxSpeed_212k; + txVal = REG_TxModeReg_TxSpeed_212k; + rxVal = REG_RxModeReg_RxSpeed_212k; break; case NBR_424: - txVal = RC522_REG_TxModeReg_TxSpeed_424k; - rxVal = RC522_REG_RxModeReg_RxSpeed_424k; + txVal = REG_TxModeReg_TxSpeed_424k; + rxVal = REG_RxModeReg_RxSpeed_424k; break; case NBR_847: - txVal = RC522_REG_TxModeReg_TxSpeed_847k; - rxVal = RC522_REG_RxModeReg_RxSpeed_847k; + txVal = REG_TxModeReg_TxSpeed_847k; + rxVal = REG_RxModeReg_RxSpeed_847k; break; default: @@ -105,8 +209,8 @@ int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { } return - rc522_write_reg(pnd, RC522_REG_TxModeReg, txVal, RC522_REG_TxModeReg_TxSpeed_MASK) || - rc522_write_reg(pnd, RC522_REG_RxModeReg, rxVal, RC522_REG_RxModeReg_RxSpeed_MASK); + rc522_write_reg(pnd, REG_TxModeReg, txVal, REG_TxModeReg_TxSpeed_MASK) || + rc522_write_reg(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK); } int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) @@ -173,8 +277,9 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - ret = rc522_write_reg(pnd, RC522_REG_TxModeReg, enable ? ~0 : 0, RC522_REG_TxModeReg_TxCRCEn) || - rc522_write_reg(pnd, RC522_REG_RxModeReg, enable ? ~0 : 0, RC522_REG_RxModeReg_RxCRCEn); + ret = + rc522_write_reg(pnd, REG_TxModeReg, enable ? ~0 : 0, REG_TxModeReg_TxCRCEn) || + rc522_write_reg(pnd, REG_RxModeReg, enable ? ~0 : 0, REG_RxModeReg_RxCRCEn); if (ret) { return ret; } @@ -187,7 +292,7 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - ret = rc522_write_reg(pnd, RC522_REG_MfRxReg, enable ? 0 : ~0, RC522_REG_MfRxReg_ParityDisable); + ret = rc522_write_reg(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable); if (ret) { return ret; } @@ -200,10 +305,10 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; case NP_ACTIVATE_FIELD: - return rc522_write_reg(pnd, RC522_REG_TxControlReg, enable ? ~0 : 0, RC522_REG_TxControlReg_Tx2RFEn | RC522_REG_TxControlReg_Tx1RFEn); + return rc522_write_reg(pnd, REG_TxControlReg, enable ? ~0 : 0, REG_TxControlReg_Tx2RFEn | REG_TxControlReg_Tx1RFEn); case NP_ACTIVATE_CRYPTO1: - return rc522_write_reg(pnd, RC522_REG_Status2Reg, enable ? ~0 : 0, RC522_REG_Status2Reg_MFCrypto1On); + return rc522_write_reg(pnd, REG_Status2Reg, enable ? ~0 : 0, REG_Status2Reg_MFCrypto1On); case NP_FORCE_ISO14443_A: // ISO14443-A is the only mode supported by MFRC522 @@ -214,8 +319,13 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - return rc522_set_baud_rate(pnd, NBR_106); - + int ret = rc522_set_baud_rate(pnd, NBR_106); + if (ret) { + pnd->last_error = ret; + } + return ret; + + case NP_ACCEPT_MULTIPLE_FRAMES: case NP_AUTO_ISO14443_4: case NP_ACCEPT_INVALID_FRAMES: case NP_INFINITE_SELECT: @@ -223,9 +333,11 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property case NP_TIMEOUT_COMMAND: case NP_TIMEOUT_ATR: case NP_TIMEOUT_COM: + pnd->last_error = NFC_EINVARG; return NFC_EINVARG; } + pnd->last_error = NFC_EINVARG; return NFC_EINVARG; } @@ -234,8 +346,116 @@ int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, return NFC_ENOTIMPL; } -int rc522_idle(struct nfc_device * pnd) { - // Set idle and disable RX demodulator to save energy - return rc522_write_reg(pnd, RC522_REG_CommandReg, RC522_CMD_Idle | RC522_REG_CommandReg_PowerDown, 0xFF); +int rc522_abort(struct nfc_device * pnd) { + return rc522_start_command(pnd, CMD_IDLE); +} + +int rc522_powerdown(struct nfc_device * pnd) { + return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_IDLE, 0xFF); +} + +// NXP MFRC522 datasheet section 16.1.1 +const uint8_t MFRC522_V1_SELFTEST[FIFO_SIZE] = { + 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, + 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, + 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, + 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 +}; +const uint8_t MFRC522_V2_SELFTEST[FIFO_SIZE] = { + 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, + 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, + 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, + 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F +}; + +// Extracted from a FM17522 with version 0x88. Fudan Semiconductor datasheet does not include it, though. +const uint8_t FM17522_SELFTEST[FIFO_SIZE] = { + 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, + 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, + 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, + 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 +}; + +int rc522_self_test(struct nfc_device * pnd) { + int version = rc522_read_reg(pnd, REG_VersionReg); + if (version < 0) { + return version; + } + + const uint8_t * correct; + switch (version) { + case MFRC522_V1: + correct = MFRC522_V1_SELFTEST; + break; + + case MFRC522_V2: + correct = MFRC522_V2_SELFTEST; + break; + + case FM17522: + correct = FM17522_SELFTEST; + break; + + default: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unknown chip version: 0x%02X", version); + return NFC_ECHIP; + } + + int ret; + uint8_t zeroes[25]; + memset(zeroes, 0x00, sizeof(zeroes)); + // MFRC522 datasheet section 16.1.1 + ret = + // 1. Perform a soft reset + rc522_soft_reset(pnd) || + // 2. Clear the internal buffer by writing 25 bytes of 0x00 and execute the Mem command + rc522_write_bulk(pnd, REG_FIFODataReg, zeroes, sizeof(zeroes)) || + rc522_start_command(pnd, CMD_MEM) || + // 3. Enable the self test by writing 0x09 to the AutoTestReg register + rc522_write_reg(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Enabled, REG_AutoTestReg_SelfTest_MASK) || + // 4. Write 0x00h to the FIFO buffer + rc522_write_reg(pnd, REG_FIFODataReg, 0x00, 0xFF) || + // 5. Start the self test with the CalcCRC command + rc522_start_command(pnd, CMD_CALCCRC); + if (ret) { + return ret; + } + + // 6. Wait for the RC522 to calculate the selftest values + // The official datasheet does not mentions how much time does it take, let's use 5ms + timeout_t to; + timeout_init(&to, 5); + + while (1) { + if (!timeout_check(&to)) { + return NFC_ETIMEOUT; + } + + if ((ret = rc522_read_reg(pnd, REG_DivIrqReg)) < 0) { + return ret; + } + + // If the RC522 has finished calculating the CRC proceed + if (ret & REG_DivIrqReg_CRCIRq) { + break; + } + } + + uint8_t response[FIFO_SIZE]; + ret = + // 7. Read selftest result + rc522_read_bulk(pnd, REG_FIFODataReg, response, FIFO_SIZE) || + // 8. Disable selftest operation mode + rc522_write_reg(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Disabled, REG_AutoTestReg_SelfTest_MASK); + if (ret) { + return ret; + } + + if (memcmp(correct, response, FIFO_SIZE) != 0) { + return NFC_ECHIP; + } + + CHIP_DATA(pnd)->version = version; + return NFC_SUCCESS; } diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index 4a2a90a..c266f55 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -28,10 +28,15 @@ struct rc522_io { }; int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); +void rc522_data_free(struct nfc_device * pnd); +int rc522_self_test(struct nfc_device * pnd); +int rc522_wait_wakeup(struct nfc_device * pnd); + int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable); int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value); -int rc522_idle(struct nfc_device * pnd); +int rc522_abort(struct nfc_device * pnd); +int rc522_powerdown(struct nfc_device * pnd); #endif diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index 8d49b44..b3d9975 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -28,6 +28,8 @@ #include #include +#include +#include #include @@ -51,98 +53,90 @@ struct rc522_uart_data { #define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) -static size_t -rc522_uart_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) -{ - size_t device_found = 0; - serial_port sp; - char **acPorts = uart_list_ports(); - const char *acPort; - int iDevice = 0; - - while ((acPort = acPorts[iDevice++])) { - sp = uart_open(acPort); - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find RC522 device on serial port: %s at %d baud.", acPort, RC522_UART_DEFAULT_SPEED); - - if ((sp != INVALID_SERIAL_PORT) && (sp != CLAIMED_SERIAL_PORT)) { - // We need to flush input to be sure first reply does not comes from older byte transceive - uart_flush_input(sp, true); - // Serial port claimed but we need to check if a RC522_UART is opened. - uart_set_speed(sp, RC522_UART_DEFAULT_SPEED); - - nfc_connstring connstring; - snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, RC522_UART_DRIVER_NAME, acPort, RC522_UART_DEFAULT_SPEED); - nfc_device *pnd = nfc_device_new(context, connstring); - if (!pnd) { - perror("malloc"); - uart_close(sp); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); - return 0; - } - pnd->driver = &rc522_uart_driver; - pnd->driver_data = sp; - - // Alloc and init chip's data - if (rc522_data_new(pnd, &rc522_uart_io) == NULL) { - perror("rc522_data_new"); - uart_close(DRIVER_DATA(pnd)->port); - nfc_device_free(pnd); - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); - return 0; - } - // SAMConfiguration command if needed to wakeup the chip and rc522_SAMConfiguration check if the chip is a RC522 - CHIP_DATA(pnd)->type = RC522; - // This device starts in LowVBat power mode - CHIP_DATA(pnd)->power_mode = LOWVBAT; - - // Check communication using "Diagnose" command, with "Communication test" (0x00) - int res = rc522_check_communication(pnd); - uart_close(DRIVER_DATA(pnd)->port); - rc522_data_free(pnd); - nfc_device_free(pnd); - if (res < 0) { - continue; - } - - memcpy(connstrings[device_found], connstring, sizeof(nfc_connstring)); - device_found++; - - // Test if we reach the maximum "wanted" devices - if (device_found >= connstrings_len) - break; - } - } - iDevice = 0; - while ((acPort = acPorts[iDevice++])) { - free((void *)acPort); - } - free(acPorts); - return device_found; -} - void rc522_uart_close(nfc_device * pnd) { - rc522_idle(pnd); +// rc522_idle(pnd); // Release UART port uart_close(DRIVER_DATA(pnd)->port); - free(DRIVER_DATA(pnd)); - rc522_data_free(pnd); nfc_device_free(pnd); } +size_t rc522_uart_scan(const nfc_context * context, nfc_connstring connstrings[], const size_t connstrings_len) { + size_t device_found = 0; + serial_port sp; + char ** acPorts = uart_list_ports(); + const char * acPort; + size_t iDevice = 0; + + while ((acPort = acPorts[iDevice++])) { + sp = uart_open(acPort); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find RC522 device on serial port: %s at %d baud.", acPort, RC522_UART_DEFAULT_SPEED); + + if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { + continue; + } + + // We need to flush input to be sure first reply does not comes from older byte transceive + uart_flush_input(sp, true); + // Serial port claimed but we need to check if a RC522_UART is opened. + uart_set_speed(sp, RC522_UART_DEFAULT_SPEED); + + nfc_connstring connstring; + snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, RC522_UART_DRIVER_NAME, acPort, RC522_UART_DEFAULT_SPEED); + nfc_device * pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("nfc_device_new"); + uart_close(sp); + nfc_device_free(pnd); + uart_list_free(acPorts); + return 0; + } + pnd->driver = &rc522_uart_driver; + + pnd->driver_data = malloc(sizeof(struct rc522_uart_data)); + if (!pnd->driver_data) { + perror("malloc"); + uart_close(sp); + nfc_device_free(pnd); + uart_list_free(acPorts); + return 0; + } + DRIVER_DATA(pnd)->port = sp; + + // Alloc and init chip's data + if (rc522_data_new(pnd, &rc522_uart_io)) { + perror("rc522_data_new"); + uart_close(sp); + nfc_device_free(pnd); + uart_list_free(acPorts); + return 0; + } + + // Check communication using self test + int res = rc522_self_test(pnd); + rc522_uart_close(pnd); + if (res < 0) { + continue; + } + + memcpy(connstrings[device_found], connstring, sizeof(nfc_connstring)); + device_found++; + + // Test if we reach the maximum "wanted" devices + if (device_found >= connstrings_len) + break; + } + + uart_list_free(acPorts); + return device_found; +} + struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_connstring connstring) { char * port_str; char * baud_str; uint32_t baudrate; + char * endptr; int decodelvl = connstring_decode(connstring, RC522_UART_DRIVER_NAME, NULL, &port_str, &baud_str); switch (decodelvl) { @@ -151,20 +145,20 @@ struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_conns break; case 3: // Got port and baud rate - char * endptr; - baudrate = (uint32_t) strtol(speed_s, &endptr, 10); + // TODO: set baud rate AFTER initialization + baudrate = (uint32_t) strtol(baud_str, &endptr, 10); if (*endptr != '\0') { free(port_str); - free(bps_str); + free(baud_str); return NULL; } - free(bps_str); + free(baud_str); break; default: // Got unparseable gibberish free(port_str); - free(bps_str); + free(baud_str); return NULL; } @@ -172,7 +166,7 @@ struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_conns struct nfc_device * pnd = NULL; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s at %d baud.", port_str, baudrate); - sp = uart_open(ndd.port); + sp = uart_open(port_str); if (sp == INVALID_SERIAL_PORT) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid serial port: %s", port_str); @@ -212,51 +206,53 @@ struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_conns DRIVER_DATA(pnd)->port = sp; // Alloc and init chip's data - if (rc522_data_new(pnd, &rc522_uart_io) == NULL) { + if (!rc522_data_new(pnd, &rc522_uart_io)) { perror("rc522_data_new"); uart_close(sp); nfc_device_free(pnd); return NULL; } - if (rc522_check_communication(pnd) < 0) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_check_communication error"); + if (rc522_self_test(pnd)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_self_test error"); rc522_uart_close(pnd); return NULL; } - rc522_init(pnd); return pnd; } -int rc522_uart_wakeup(struct nfc_device *pnd) { +int rc522_uart_wakeup(struct nfc_device * pnd) { + int ret; + /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. */ const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - int res = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), 0); - CHIP_DATA(pnd)->power_mode = NORMAL; // RC522 should now be awake - return res; + if ((ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), 0)) < 0) { + return ret; + } + + return rc522_wait_wakeup(pnd); } #define READ 1 #define WRITE 0 uint8_t rc522_uart_pack(int reg, int op) { assert(reg < 64); - assert(op == READ | op == WRITE); + assert(op == READ || op == WRITE); return op << 7 | reg; } int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size) { uint8_t cmd = rc522_uart_pack(reg, READ); + int ret; while (size > 0) { - pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT); - if (pnd->last_error < 0) { + if ((ret = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } - pnd->last_error = uart_receive(pnd->driver_data, data, 1, RC522_UART_IO_TIMEOUT); - if (pnd->last_error < 0) { + if ((ret = uart_receive(pnd->driver_data, data, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } @@ -271,7 +267,7 @@ error: return pnd->last_error; } -int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size) { +int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size) { uint8_t cmd = rc522_uart_pack(reg, WRITE); while (size > 0) { @@ -283,7 +279,7 @@ int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_ // Second: wait for a reply uint8_t reply; - pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, RC522_UART_IO_TIMEOUT); + pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, NULL, RC522_UART_IO_TIMEOUT); if (pnd->last_error < 0) { return pnd->last_error; } @@ -322,20 +318,20 @@ const struct nfc_driver rc522_uart_driver = { .scan = rc522_uart_scan, .open = rc522_uart_open, .close = rc522_uart_close, - .strerror = rc522_strerror, +// .strerror = rc522_strerror, - .initiator_init = rc522_initiator_init, +// .initiator_init = rc522_initiator_init, // MFRC522 has no secure element .initiator_init_secure_element = NULL, - .initiator_select_passive_target = rc522_initiator_select_passive_target, - .initiator_poll_target = rc522_initiator_poll_target, +// .initiator_select_passive_target = rc522_initiator_select_passive_target, +// .initiator_poll_target = rc522_initiator_poll_target, .initiator_select_dep_target = NULL, - .initiator_deselect_target = rc522_initiator_deselect_target, - .initiator_transceive_bytes = rc522_initiator_transceive_bytes, - .initiator_transceive_bits = rc522_initiator_transceive_bits, - .initiator_transceive_bytes_timed = rc522_initiator_transceive_bytes_timed, - .initiator_transceive_bits_timed = rc522_initiator_transceive_bits_timed, - .initiator_target_is_present = rc522_initiator_target_is_present, +// .initiator_deselect_target = rc522_initiator_deselect_target, +// .initiator_transceive_bytes = rc522_initiator_transceive_bytes, +// .initiator_transceive_bits = rc522_initiator_transceive_bits, +// .initiator_transceive_bytes_timed = rc522_initiator_transceive_bytes_timed, +// .initiator_transceive_bits_timed = rc522_initiator_transceive_bits_timed, +// .initiator_target_is_present = rc522_initiator_target_is_present, // MFRC522 is unable to work as target .target_init = NULL, @@ -348,10 +344,10 @@ const struct nfc_driver rc522_uart_driver = { .device_set_property_int = rc522_set_property_int, .get_supported_modulation = rc522_get_supported_modulation, .get_supported_baud_rate = rc522_get_supported_baud_rate, - .device_get_information_about = rc522_get_information_about, +// .device_get_information_about = rc522_get_information_about, - .abort_command = rc522_idle, - .idle = rc522_idle, - .powerdown = rc522_softdown, + .abort_command = rc522_abort, +// .idle = rc522_idle, + .powerdown = rc522_powerdown, }; diff --git a/libnfc/nfc.c b/libnfc/nfc.c index c774504..2cebb7d 100644 --- a/libnfc/nfc.c +++ b/libnfc/nfc.c @@ -118,6 +118,9 @@ # include "drivers/pn532_i2c.h" #endif /* DRIVER_PN532_I2C_ENABLED */ +#if defined (DRIVER_RC522_UART_ENABLED) +# include "drivers/rc522_uart.h" +#endif /* DRIVER_RC522_UART_ENABLED */ #define LOG_CATEGORY "libnfc.general" #define LOG_GROUP NFC_LOG_GROUP_GENERAL @@ -156,6 +159,9 @@ nfc_drivers_init(void) #if defined (DRIVER_ARYGON_ENABLED) nfc_register_driver(&arygon_driver); #endif /* DRIVER_ARYGON_ENABLED */ +#if defined (DRIVER_RC522_UART_ENABLED) + nfc_register_driver(&rc522_uart_driver); +#endif /* DRIVER_RC522_UART_ENABLED */ } static int diff --git a/libnfc/timing.c b/libnfc/timing.c new file mode 100644 index 0000000..8b17d71 --- /dev/null +++ b/libnfc/timing.c @@ -0,0 +1,75 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#include "timing.h" +#include + +#define MILLIS_PER_SEC 1000 +#define MICROS_PER_SEC 1000000 +#define NANOS_PER_SEC 1000000000 + +// Use Windows' API directly if Win32 for highest possible resolution +#if defined(CYGWIN) || defined(_WIN32) +# include + + static uint64_t timer_hz = 0; + + ms_t time_millis() { + if (!timer_hz) { + // Windows API states that the frequency is fixed at boot and therefore can be cached safely + assert(QueryPerformanceFrequency((LARGE_INTEGER *) &timer_hz)); + } + + uint64_t ticks; + assert(QueryPerformanceCounter((LARGE_INTEGER *) &ticks)); + return MILLIS_PER_SEC * ticks / timer_hz; + } + +// If not Windows use POSIX methods +#else +# include + + ms_t time_millis() { + struct timespec ts; + + // CLOCK_MONOTONIC_RAW isn't affected by NTP updates and therefore really monotonic, but it's Linux-specific +# ifdef CLOCK_MONOTONIC_RAW + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); +# else + clock_gettime(CLOCK_MONOTONIC, &ts); +# endif + + return (ms_t) ts.tv_sec * MILLIS_PER_SEC + ts.tv_nsec / (NANOS_PER_SEC / MILLIS_PER_SEC); + } +#endif + +void timeout_init(timeout_t * to, unsigned int millis) { + *to = time_millis() + millis; +} + +bool timeout_check(timeout_t * to) { + if (*to == 0) { + return false; + } + + ms_t now = time_millis(); + if (now >= *to) { + *to = 0; + } + + return true; +} diff --git a/libnfc/timing.h b/libnfc/timing.h new file mode 100644 index 0000000..beb9e47 --- /dev/null +++ b/libnfc/timing.h @@ -0,0 +1,48 @@ +/*- + * Free/Libre Near Field Communication (NFC) library + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ + +#ifndef __NFC_TIMING_H__ +#define __NFC_TIMING_H__ + +#include +#include + +typedef uint64_t ms_t; + +/** + * @brief Calculates a timestamp from a unspecified start with millisecond accuracy + * @return Time in milliseconds + */ +ms_t time_millis(); + +typedef ms_t timeout_t; + +/** + * @brief Initializes a timeout + * @param to Timeout handle + * @param millis Minimum expiration time in milliseconds + */ +void timeout_init(timeout_t * to, unsigned int millis); + +/** + * @brief Checks if the timeout has NOT expired + * @param to Timeout handle + * @return True if the timeout has NOT expired + */ +bool timeout_check(timeout_t * to); + +#endif From 09ceb92db45cecc80a97cb93c4a4c2d78fdf2831 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Tue, 16 Jun 2015 22:15:52 +0200 Subject: [PATCH 07/15] Improved UART with async pin control --- libnfc/buses/uart.c | 66 +++++++++++++++++++++++++++++++++++++-------- libnfc/buses/uart.h | 8 ++++-- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/libnfc/buses/uart.c b/libnfc/buses/uart.c index 05bfec0..aad519f 100644 --- a/libnfc/buses/uart.c +++ b/libnfc/buses/uart.c @@ -97,6 +97,8 @@ struct serial_port_unix { int fd; // Serial port file descriptor struct termios termios_backup; // Terminal info before using the port struct termios termios_new; // Terminal info during the transaction + int pins_backup; + int pins_new; }; #define UART_DATA( X ) ((struct serial_port_unix *) X) @@ -141,10 +143,15 @@ uart_open(const char *pcPortName) uart_close_ext(sp, true); return INVALID_SERIAL_PORT; } + + ioctl(sp->fd, TIOCMGET, &sp->pins_backup); + sp->pins_new = sp->pins_backup; + uart_set_pins(sp, 0); + return sp; } -void +int uart_flush_input(serial_port sp, bool wait) { // flush commands may seem to be without effect @@ -156,34 +163,37 @@ uart_flush_input(serial_port sp, bool wait) } // This line seems to produce absolutely no effect on my system (GNU/Linux 2.6.35) - tcflush(UART_DATA(sp)->fd, TCIFLUSH); + if (tcflush(UART_DATA(sp)->fd, TCIFLUSH)) { + return NFC_EIO; + } // So, I wrote this byte-eater // Retrieve the count of the incoming bytes int available_bytes_count = 0; int res; res = ioctl(UART_DATA(sp)->fd, FIONREAD, &available_bytes_count); if (res != 0) { - return; + return NFC_EIO; } if (available_bytes_count == 0) { - return; + return NFC_SUCCESS; } char *rx = malloc(available_bytes_count); if (!rx) { perror("malloc"); - return; + return NFC_ESOFT; } // There is something available, read the data if (read(UART_DATA(sp)->fd, rx, available_bytes_count) < 0) { perror("uart read"); free(rx); - return; + return NFC_EIO; } log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%d bytes have eaten.", available_bytes_count); free(rx); + return NFC_SUCCESS; } -void +int uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Serial port speed requested to be set to %d baud.", uiPortSpeed); @@ -224,15 +234,17 @@ uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) default: log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to set serial port speed to %d baud. Speed value must be one of those defined in termios(3).", uiPortSpeed); - return; + return NFC_ESOFT; }; // Set port speed (Input and Output) cfsetispeed(&(UART_DATA(sp)->termios_new), stPortSpeed); cfsetospeed(&(UART_DATA(sp)->termios_new), stPortSpeed); if (tcsetattr(UART_DATA(sp)->fd, TCSADRAIN, &(UART_DATA(sp)->termios_new)) == -1) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to apply new speed settings."); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to apply new speed settings."); + return NFC_EIO; } + return NFC_SUCCESS; } uint32_t @@ -275,11 +287,13 @@ uart_get_speed(serial_port sp) } void -uart_close_ext(const serial_port sp, const bool restore_termios) +uart_close_ext(const serial_port sp, const bool restore_status) { if (UART_DATA(sp)->fd >= 0) { - if (restore_termios) + if (restore_status) { tcsetattr(UART_DATA(sp)->fd, TCSANOW, &UART_DATA(sp)->termios_backup); + ioctl(UART_DATA(sp)->fd, TIOCMSET, &UART_DATA(sp)->pins_backup); + } close(UART_DATA(sp)->fd); } free(sp); @@ -381,6 +395,36 @@ uart_send(serial_port sp, const uint8_t *pbtTx, const size_t szTx, int timeout) return NFC_EIO; } +/** + * @brief Asserts/deasserts asynchronous control signals of RS232 ports + * + * @return 0 on success, otherwise a driver error is returned + */ +int +uart_set_pins(serial_port sp, int status) +{ + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "DTR: %d RTS: %d", (status & UART_DTR) ? 1 : 0, (status & UART_RTS) ? 1 : 0); + + int posix = 0; + if (status & UART_DTR) + posix |= TIOCM_DTR; + if (status & UART_RTS) + posix |= TIOCM_RTS; + + /* TODO - Uncomment after fixing Cygwin tcgetattr, which modifies pin status on its own + if (UART_DATA(sp)->pins_new == posix) + return NFC_SUCCESS; + */ + + if (ioctl(UART_DATA(sp)->fd, TIOCMSET, &posix) == -1) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to set asynchronous control pins."); + return NFC_EIO; + } + + UART_DATA(sp)->pins_new = posix; + return NFC_SUCCESS; +} + char ** uart_list_ports(void) { diff --git a/libnfc/buses/uart.h b/libnfc/buses/uart.h index 8519304..f7ae66c 100644 --- a/libnfc/buses/uart.h +++ b/libnfc/buses/uart.h @@ -49,14 +49,18 @@ typedef void *serial_port; serial_port uart_open(const char *pcPortName); void uart_close(const serial_port sp); -void uart_flush_input(const serial_port sp, bool wait); +int uart_flush_input(const serial_port sp, bool wait); -void uart_set_speed(serial_port sp, const uint32_t uiPortSpeed); +int uart_set_speed(serial_port sp, const uint32_t uiPortSpeed); uint32_t uart_get_speed(const serial_port sp); int uart_receive(serial_port sp, uint8_t *pbtRx, const size_t szRx, void *abort_p, int timeout); int uart_send(serial_port sp, const uint8_t *pbtTx, const size_t szTx, int timeout); +#define UART_DTR (1 << 0) +#define UART_RTS (1 << 1) +int uart_set_pins(serial_port sp, int status); + char **uart_list_ports(void); void uart_list_free(char **acPorts); From 94b2ddab04a15c8f1314209dfd3adc19aaddabf8 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Wed, 17 Jun 2015 20:18:39 +0200 Subject: [PATCH 08/15] Further work for RC522 --- libnfc/buses/uart.c | 42 +---- libnfc/buses/uart.h | 4 - libnfc/chips/pn53x.c | 1 + libnfc/chips/rc522-internal.h | 2 + libnfc/chips/rc522.c | 118 ++++++++------ libnfc/chips/rc522.h | 2 + libnfc/drivers/rc522_uart.c | 295 +++++++++++++++++++--------------- 7 files changed, 245 insertions(+), 219 deletions(-) diff --git a/libnfc/buses/uart.c b/libnfc/buses/uart.c index aad519f..c5cb3d0 100644 --- a/libnfc/buses/uart.c +++ b/libnfc/buses/uart.c @@ -97,8 +97,6 @@ struct serial_port_unix { int fd; // Serial port file descriptor struct termios termios_backup; // Terminal info before using the port struct termios termios_new; // Terminal info during the transaction - int pins_backup; - int pins_new; }; #define UART_DATA( X ) ((struct serial_port_unix *) X) @@ -144,10 +142,6 @@ uart_open(const char *pcPortName) return INVALID_SERIAL_PORT; } - ioctl(sp->fd, TIOCMGET, &sp->pins_backup); - sp->pins_new = sp->pins_backup; - uart_set_pins(sp, 0); - return sp; } @@ -287,13 +281,11 @@ uart_get_speed(serial_port sp) } void -uart_close_ext(const serial_port sp, const bool restore_status) +uart_close_ext(const serial_port sp, const bool restore_termios) { if (UART_DATA(sp)->fd >= 0) { - if (restore_status) { + if (restore_termios) tcsetattr(UART_DATA(sp)->fd, TCSANOW, &UART_DATA(sp)->termios_backup); - ioctl(UART_DATA(sp)->fd, TIOCMSET, &UART_DATA(sp)->pins_backup); - } close(UART_DATA(sp)->fd); } free(sp); @@ -395,36 +387,6 @@ uart_send(serial_port sp, const uint8_t *pbtTx, const size_t szTx, int timeout) return NFC_EIO; } -/** - * @brief Asserts/deasserts asynchronous control signals of RS232 ports - * - * @return 0 on success, otherwise a driver error is returned - */ -int -uart_set_pins(serial_port sp, int status) -{ - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "DTR: %d RTS: %d", (status & UART_DTR) ? 1 : 0, (status & UART_RTS) ? 1 : 0); - - int posix = 0; - if (status & UART_DTR) - posix |= TIOCM_DTR; - if (status & UART_RTS) - posix |= TIOCM_RTS; - - /* TODO - Uncomment after fixing Cygwin tcgetattr, which modifies pin status on its own - if (UART_DATA(sp)->pins_new == posix) - return NFC_SUCCESS; - */ - - if (ioctl(UART_DATA(sp)->fd, TIOCMSET, &posix) == -1) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to set asynchronous control pins."); - return NFC_EIO; - } - - UART_DATA(sp)->pins_new = posix; - return NFC_SUCCESS; -} - char ** uart_list_ports(void) { diff --git a/libnfc/buses/uart.h b/libnfc/buses/uart.h index f7ae66c..712ef49 100644 --- a/libnfc/buses/uart.h +++ b/libnfc/buses/uart.h @@ -57,10 +57,6 @@ uint32_t uart_get_speed(const serial_port sp); int uart_receive(serial_port sp, uint8_t *pbtRx, const size_t szRx, void *abort_p, int timeout); int uart_send(serial_port sp, const uint8_t *pbtTx, const size_t szTx, int timeout); -#define UART_DTR (1 << 0) -#define UART_RTS (1 << 1) -int uart_set_pins(serial_port sp, int status); - char **uart_list_ports(void); void uart_list_free(char **acPorts); diff --git a/libnfc/chips/pn53x.c b/libnfc/chips/pn53x.c index d76006a..f635a86 100644 --- a/libnfc/chips/pn53x.c +++ b/libnfc/chips/pn53x.c @@ -333,6 +333,7 @@ pn53x_transceive(struct nfc_device *pnd, const uint8_t *pbtTx, const size_t szTx }; if (res < 0) { + pnd->last_error = res; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Chip error: \"%s\" (%02x), returned error: \"%s\" (%d))", pn53x_strerror(pnd), CHIP_DATA(pnd)->last_status_byte, nfc_strerror(pnd), res); } return res; diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index 23a91e4..093a5ff 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -28,6 +28,7 @@ typedef enum { #define REG_CommandReg 0x01 #define REG_CommandReg_RcvOff (1 << 5) #define REG_CommandReg_PowerDown (1 << 4) +#define REG_CommandReg_Command_MASK 0x0F #define REG_ComlEnReg 0x02 @@ -49,6 +50,7 @@ typedef enum { #define REG_FIFODataReg 0x09 #define REG_FIFOLevelReg 0x0A +#define REG_FIFOLevelReg_FlushBuffer (1 << 7) #define REG_WaterLevelReg 0x0B diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index 5ba222f..aeb4fb1 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -35,7 +35,7 @@ const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, struct rc522_chip_data { const struct rc522_io * io; - rc522_type version; + uint8_t version; }; #define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) @@ -59,7 +59,7 @@ void rc522_data_free(struct nfc_device * pnd) { int rc522_read_bulk(struct nfc_device * pnd, uint8_t reg, uint8_t * val, size_t len) { int ret = CHIP_DATA(pnd)->io->read(pnd, reg, val, len); if (ret) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to read register %02X!", reg); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to read register %02X (err: %d)", reg, ret); return ret; } @@ -113,7 +113,7 @@ int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t m } int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { - bool needsRX = false; + bool needsRX; // Disabling RX saves energy, so based on the command we'll also update the RxOff flag switch (cmd) { @@ -123,6 +123,7 @@ int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { case CMD_CALCCRC: case CMD_TRANSMIT: case CMD_SOFTRESET: + needsRX = false; break; case CMD_RECEIVE: @@ -136,8 +137,7 @@ int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { default: log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to execute non-existant command: %02X", cmd); - pnd->last_error = NFC_ESOFT; - return pnd->last_error; + return NFC_ESOFT; } uint8_t regval = cmd; @@ -153,9 +153,6 @@ int rc522_wait_wakeup(struct nfc_device * pnd) { timeout_t to; timeout_init(&to, 50); - // rc522_read_reg updates last_error. Backup it to ignore timeouts - int last_error = pnd->last_error; - while (timeout_check(&to)) { int ret = rc522_read_reg(pnd, REG_CommandReg); if (ret < 0 && ret != NFC_ETIMEOUT) { @@ -164,19 +161,68 @@ int rc522_wait_wakeup(struct nfc_device * pnd) { // If the powerdown bit is zero the RC522 is ready to kick asses! if ((ret & REG_CommandReg_PowerDown) == 0) { - pnd->last_error = last_error; return NFC_SUCCESS; } } - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_wait_wakeup timeout!"); - pnd->last_error = NFC_ETIMEOUT; - return pnd->last_error; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_wait_wakeup timeout!"); + return NFC_ETIMEOUT; +} + +int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate) { + uint8_t regval; + + // MFRC522 datasheet 8.1.3.2 + switch (baudrate) { + case 7200: + regval = 0xFA; + break; + case 9600: + regval = 0xEB; + break; + case 14400: + regval = 0xDA; + break; + case 19200: + regval = 0xCB; + break; + case 38400: + regval = 0xAB; + break; + case 57600: + regval = 0x9A; + break; + case 115200: + regval = 0x7A; + break; + case 128000: + regval = 0x74; + break; + case 230400: + regval = 0x5A; + break; + case 460800: + regval = 0x3A; + break; + case 921600: + regval = 0x1C; + break; + case 1288000: + regval = 0x15; + break; + default: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_write_baudrate unsupported baud rate: %d bps.", baudrate); + return NFC_EDEVNOTSUPP; + } + + return rc522_write_reg(pnd, REG_SerialSpeedReg, regval, 0xFF); } int rc522_soft_reset(struct nfc_device * pnd) { return + // 1. Send soft reset rc522_start_command(pnd, CMD_SOFTRESET) || + CHIP_DATA(pnd)->io->reset_baud_rate(pnd) || rc522_wait_wakeup(pnd); } @@ -319,11 +365,7 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - int ret = rc522_set_baud_rate(pnd, NBR_106); - if (ret) { - pnd->last_error = ret; - } - return ret; + return rc522_set_baud_rate(pnd, NBR_106); case NP_ACCEPT_MULTIPLE_FRAMES: case NP_AUTO_ISO14443_4: @@ -333,11 +375,9 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property case NP_TIMEOUT_COMMAND: case NP_TIMEOUT_ATR: case NP_TIMEOUT_COM: - pnd->last_error = NFC_EINVARG; return NFC_EINVARG; } - pnd->last_error = NFC_EINVARG; return NFC_EINVARG; } @@ -347,11 +387,13 @@ int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, } int rc522_abort(struct nfc_device * pnd) { - return rc522_start_command(pnd, CMD_IDLE); + return + rc522_start_command(pnd, CMD_IDLE) || + rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer, 0xFF); } int rc522_powerdown(struct nfc_device * pnd) { - return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_IDLE, 0xFF); + return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_NOCMDCHANGE, 0xFF); } // NXP MFRC522 datasheet section 16.1.1 @@ -368,22 +410,9 @@ const uint8_t MFRC522_V2_SELFTEST[FIFO_SIZE] = { 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F }; -// Extracted from a FM17522 with version 0x88. Fudan Semiconductor datasheet does not include it, though. -const uint8_t FM17522_SELFTEST[FIFO_SIZE] = { - 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, - 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, - 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, - 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 -}; - int rc522_self_test(struct nfc_device * pnd) { - int version = rc522_read_reg(pnd, REG_VersionReg); - if (version < 0) { - return version; - } - const uint8_t * correct; - switch (version) { + switch (CHIP_DATA(pnd)->version) { case MFRC522_V1: correct = MFRC522_V1_SELFTEST; break; @@ -392,20 +421,17 @@ int rc522_self_test(struct nfc_device * pnd) { correct = MFRC522_V2_SELFTEST; break; - case FM17522: - correct = FM17522_SELFTEST; - break; - default: - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unknown chip version: 0x%02X", version); - return NFC_ECHIP; + return NFC_EDEVNOTSUPP; } - int ret; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Executing self test"); + uint8_t zeroes[25]; memset(zeroes, 0x00, sizeof(zeroes)); + // MFRC522 datasheet section 16.1.1 - ret = + int ret = // 1. Perform a soft reset rc522_soft_reset(pnd) || // 2. Clear the internal buffer by writing 25 bytes of 0x00 and execute the Mem command @@ -422,9 +448,9 @@ int rc522_self_test(struct nfc_device * pnd) { } // 6. Wait for the RC522 to calculate the selftest values - // The official datasheet does not mentions how much time does it take, let's use 5ms + // The official datasheet does not mentions how much time does it take, let's use 50ms timeout_t to; - timeout_init(&to, 5); + timeout_init(&to, 50); while (1) { if (!timeout_check(&to)) { @@ -455,7 +481,5 @@ int rc522_self_test(struct nfc_device * pnd) { return NFC_ECHIP; } - CHIP_DATA(pnd)->version = version; return NFC_SUCCESS; } - diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index c266f55..d8ee526 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -25,12 +25,14 @@ struct rc522_io { int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size); int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size); + int (*reset_baud_rate)(struct nfc_device * pnd); }; int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); void rc522_data_free(struct nfc_device * pnd); int rc522_self_test(struct nfc_device * pnd); int rc522_wait_wakeup(struct nfc_device * pnd); +int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate); int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index b3d9975..e9ceed5 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -38,7 +38,8 @@ #include "chips/rc522.h" #include "uart.h" -#define RC522_UART_DEFAULT_SPEED 9600 +#define RC522_UART_BOOT_SPEED 9600 +#define RC522_UART_DEFAULT_SPEED 115200 #define RC522_UART_DRIVER_NAME "rc522_uart" #define RC522_UART_IO_TIMEOUT 50 @@ -49,76 +50,173 @@ const struct rc522_io rc522_uart_io; struct rc522_uart_data { serial_port port; + uint32_t baudrate; }; #define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) -void rc522_uart_close(nfc_device * pnd) { -// rc522_idle(pnd); +int rc522_uart_wakeup(struct nfc_device * pnd) { + int ret; + /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. */ + const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), RC522_UART_IO_TIMEOUT); + if (ret < 0) { + return ret; + } + + return rc522_wait_wakeup(pnd); +} + +bool rc522_uart_test_baudrate(struct nfc_device * pnd, uint32_t baudrate) { + int ret; + + if ((ret = uart_set_speed(DRIVER_DATA(pnd)->port, baudrate)) < 0) { + return false; + } + + ret = rc522_wait_wakeup(pnd); + if (ret != NFC_SUCCESS) { + return false; + } + + DRIVER_DATA(pnd)->baudrate = baudrate; + return true; +} + +int rc522_uart_change_baudrate(struct nfc_device * pnd, uint32_t newBaudRate) { + uint32_t oldBaudRate = DRIVER_DATA(pnd)->baudrate; + int ret; + + if (oldBaudRate == newBaudRate) { + return NFC_SUCCESS; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Switching baud rate from %dbps to %dbps.", oldBaudRate, newBaudRate); + + if ((ret = rc522_send_baudrate(pnd, newBaudRate)) < 0) { + return ret; + } + + if ((ret = uart_set_speed(DRIVER_DATA(pnd)->port, newBaudRate)) < 0) { + return ret; + } + + if ((ret = rc522_wait_wakeup(pnd)) < 0) { + return ret; + } + + DRIVER_DATA(pnd)->baudrate = newBaudRate; + return NFC_SUCCESS; +} + +void rc522_uart_close(nfc_device * pnd) { + rc522_powerdown(pnd); // Release UART port uart_close(DRIVER_DATA(pnd)->port); rc522_data_free(pnd); nfc_device_free(pnd); } +int rc522_uart_create(const nfc_context * context, const nfc_connstring connstring, const char * portPath, uint32_t userBaudRate, struct nfc_device ** pndPtr) { + int ret; + serial_port sp; + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s.", portPath); + sp = uart_open(portPath); + if (sp == INVALID_SERIAL_PORT) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid serial port: %s", portPath); + return NFC_EIO; + } + if (sp == CLAIMED_SERIAL_PORT) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Serial port already claimed: %s", portPath); + return NFC_EIO; + } + + // We need to flush input to be sure first reply does not comes from older byte transceive + if ((ret = uart_flush_input(sp, true)) < 0) { + return ret; + } + + nfc_device * pnd = nfc_device_new(context, connstring); + if (!pnd) { + perror("nfc_device_new"); + uart_close(sp); + return NFC_ESOFT; + } + pnd->driver = &rc522_uart_driver; + + pnd->driver_data = malloc(sizeof(struct rc522_uart_data)); + + if (!pnd->driver_data) { + perror("malloc"); + uart_close(sp); + nfc_device_free(pnd); + return NFC_ESOFT; + } + DRIVER_DATA(pnd)->port = sp; + + // Alloc and init chip's data + if (rc522_data_new(pnd, &rc522_uart_io)) { + perror("rc522_data_new"); + uart_close(sp); + nfc_device_free(pnd); + return NFC_ESOFT; + } + + // Here we'll have to address several posibilities: + // - The hard reset trick did the work, and the RC522 is up and listening at 9600 + // - The hard reset didn't work, but the RC522 hasn't been used yet and therefore listens at 9600 + // - The hard reset didn't work and the RC522 is not using the default, so we'll use the custom provided baud rate + + // Let's try first with boot baud rate + if ( + !rc522_uart_test_baudrate(pnd, RC522_UART_BOOT_SPEED) && + !rc522_uart_test_baudrate(pnd, userBaudRate) + ) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Could not connect with RC522 at %d or %d bps.", RC522_UART_BOOT_SPEED, userBaudRate); + rc522_uart_close(pnd); + return NFC_EIO; + } + + // Change now the baud rate + if ((ret = rc522_uart_change_baudrate(pnd, userBaudRate)) < 0) { + rc522_uart_close(pnd); + return ret; + } + + // Now the device is awake and listening at a known baudrate, execute a selftest + // Note that some devices (FM17522 for instance) aren't able to run it + ret = rc522_self_test(pnd); + if (ret != NFC_SUCCESS && ret != NFC_EDEVNOTSUPP) { + rc522_uart_close(pnd); + return ret; + } + + *pndPtr = pnd; + return NFC_SUCCESS; +} + size_t rc522_uart_scan(const nfc_context * context, nfc_connstring connstrings[], const size_t connstrings_len) { size_t device_found = 0; - serial_port sp; char ** acPorts = uart_list_ports(); const char * acPort; size_t iDevice = 0; while ((acPort = acPorts[iDevice++])) { - sp = uart_open(acPort); - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find RC522 device on serial port: %s at %d baud.", acPort, RC522_UART_DEFAULT_SPEED); - - if (sp == INVALID_SERIAL_PORT || sp == CLAIMED_SERIAL_PORT) { - continue; - } - - // We need to flush input to be sure first reply does not comes from older byte transceive - uart_flush_input(sp, true); - // Serial port claimed but we need to check if a RC522_UART is opened. - uart_set_speed(sp, RC522_UART_DEFAULT_SPEED); - nfc_connstring connstring; snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, RC522_UART_DRIVER_NAME, acPort, RC522_UART_DEFAULT_SPEED); - nfc_device * pnd = nfc_device_new(context, connstring); - if (!pnd) { - perror("nfc_device_new"); - uart_close(sp); - nfc_device_free(pnd); + + nfc_device * pnd; + int ret = rc522_uart_create(context, connstring, acPort, RC522_UART_DEFAULT_SPEED, &pnd); + if (ret == NFC_ESOFT) { uart_list_free(acPorts); return 0; } - pnd->driver = &rc522_uart_driver; - - pnd->driver_data = malloc(sizeof(struct rc522_uart_data)); - if (!pnd->driver_data) { - perror("malloc"); - uart_close(sp); - nfc_device_free(pnd); - uart_list_free(acPorts); - return 0; - } - DRIVER_DATA(pnd)->port = sp; - - // Alloc and init chip's data - if (rc522_data_new(pnd, &rc522_uart_io)) { - perror("rc522_data_new"); - uart_close(sp); - nfc_device_free(pnd); - uart_list_free(acPorts); - return 0; - } - - // Check communication using self test - int res = rc522_self_test(pnd); - rc522_uart_close(pnd); - if (res < 0) { + if (ret != NFC_SUCCESS) { continue; } + rc522_uart_close(pnd); memcpy(connstrings[device_found], connstring, sizeof(nfc_connstring)); device_found++; @@ -133,10 +231,11 @@ size_t rc522_uart_scan(const nfc_context * context, nfc_connstring connstrings[] } struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_connstring connstring) { - char * port_str; - char * baud_str; + char * port_str = NULL; + char * baud_str = NULL; uint32_t baudrate; char * endptr; + struct nfc_device * pnd = NULL; int decodelvl = connstring_decode(connstring, RC522_UART_DRIVER_NAME, NULL, &port_str, &baud_str); switch (decodelvl) { @@ -162,78 +261,11 @@ struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_conns return NULL; } - serial_port sp; - struct nfc_device * pnd = NULL; - - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s at %d baud.", port_str, baudrate); - sp = uart_open(port_str); - - if (sp == INVALID_SERIAL_PORT) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid serial port: %s", port_str); - free(port_str); - return NULL; - } - - if (sp == CLAIMED_SERIAL_PORT) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Serial port already claimed: %s", port_str); - free(port_str); - return NULL; - } - - // We need to flush input to be sure first reply does not comes from older byte transceive - uart_flush_input(sp, true); - uart_set_speed(sp, baudrate); - - // We have a connection - pnd = nfc_device_new(context, connstring); - if (!pnd) { - perror("nfc_device_new"); - free(port_str); - uart_close(sp); - return NULL; - } - - snprintf(pnd->name, sizeof(pnd->name), "%s:%s", RC522_UART_DRIVER_NAME, port_str); + rc522_uart_create(context, connstring, port_str, baudrate, &pnd); free(port_str); - pnd->driver = &rc522_uart_driver; - pnd->driver_data = malloc(sizeof(struct rc522_uart_data)); - if (!pnd->driver_data) { - perror("malloc"); - uart_close(sp); - nfc_device_free(pnd); - return NULL; - } - DRIVER_DATA(pnd)->port = sp; - - // Alloc and init chip's data - if (!rc522_data_new(pnd, &rc522_uart_io)) { - perror("rc522_data_new"); - uart_close(sp); - nfc_device_free(pnd); - return NULL; - } - - if (rc522_self_test(pnd)) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_self_test error"); - rc522_uart_close(pnd); - return NULL; - } - return pnd; } -int rc522_uart_wakeup(struct nfc_device * pnd) { - int ret; - - /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. */ - const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - if ((ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), 0)) < 0) { - return ret; - } - - return rc522_wait_wakeup(pnd); -} - #define READ 1 #define WRITE 0 uint8_t rc522_uart_pack(int reg, int op) { @@ -248,11 +280,11 @@ int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t int ret; while (size > 0) { - if ((ret = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } - if ((ret = uart_receive(pnd->driver_data, data, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_receive(DRIVER_DATA(pnd)->port, data, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } @@ -264,35 +296,34 @@ int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t error: uart_flush_input(DRIVER_DATA(pnd)->port, true); - return pnd->last_error; + return ret; } int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size) { uint8_t cmd = rc522_uart_pack(reg, WRITE); + int ret; while (size > 0) { // First: send write request - pnd->last_error = uart_send(pnd->driver_data, &cmd, 1, RC522_UART_IO_TIMEOUT); - if (pnd->last_error < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } // Second: wait for a reply uint8_t reply; - pnd->last_error = uart_receive(pnd->driver_data, &reply, 1, NULL, RC522_UART_IO_TIMEOUT); - if (pnd->last_error < 0) { - return pnd->last_error; + if ((ret = uart_receive(DRIVER_DATA(pnd)->port, &reply, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { + return ret; } // Third: compare sent and received. They must match. if (cmd != reply) { - pnd->last_error = NFC_EIO; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_uart_write ack does not match (sent %02X, received %02X)", cmd, reply); + ret = NFC_ECHIP; goto error; } // Fourth: send register data - pnd->last_error = uart_send(pnd->driver_data, data, 1, RC522_UART_IO_TIMEOUT); - if (pnd->last_error < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, data, 1, RC522_UART_IO_TIMEOUT)) < 0) { goto error; } @@ -304,12 +335,20 @@ int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, error: uart_flush_input(DRIVER_DATA(pnd)->port, true); - return pnd->last_error; + return ret; +} + +int rc522_uart_reset_baud_rate(struct nfc_device * pnd) { + uint32_t userBaudRate = DRIVER_DATA(pnd)->baudrate; + return + rc522_uart_test_baudrate(pnd, RC522_UART_BOOT_SPEED) || + rc522_uart_change_baudrate(pnd, userBaudRate); } const struct rc522_io rc522_uart_io = { .read = rc522_uart_read, .write = rc522_uart_write, + .reset_baud_rate = rc522_uart_reset_baud_rate, }; const struct nfc_driver rc522_uart_driver = { From 2823a0e276dc9652a28711a0db65fb988350ae6a Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Wed, 17 Jun 2015 22:14:42 +0200 Subject: [PATCH 09/15] Disable baudrate setting for RC522 for now --- libnfc/drivers/rc522_uart.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index e9ceed5..0085db9 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -180,10 +180,12 @@ int rc522_uart_create(const nfc_context * context, const nfc_connstring connstri } // Change now the baud rate + /* TODO - Check why this doesn't work on my FM17522 and try it with a real MFRC522 if ((ret = rc522_uart_change_baudrate(pnd, userBaudRate)) < 0) { rc522_uart_close(pnd); return ret; } + */ // Now the device is awake and listening at a known baudrate, execute a selftest // Note that some devices (FM17522 for instance) aren't able to run it From 9daec49f3e21918d9d31d099599db6b38d5a2faf Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Thu, 18 Jun 2015 21:39:06 +0200 Subject: [PATCH 10/15] More RC522 stuff --- libnfc/chips/rc522.c | 76 +++++++++++++++++++++++--- libnfc/chips/rc522.h | 4 +- libnfc/drivers/rc522_uart.c | 105 ++++++++++++++---------------------- 3 files changed, 111 insertions(+), 74 deletions(-) diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index aeb4fb1..9e0eaad 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -219,11 +219,33 @@ int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate) { } int rc522_soft_reset(struct nfc_device * pnd) { - return - // 1. Send soft reset - rc522_start_command(pnd, CMD_SOFTRESET) || - CHIP_DATA(pnd)->io->reset_baud_rate(pnd) || - rc522_wait_wakeup(pnd); + int ret; + + // 1. Execute reset command + if ((ret = rc522_start_command(pnd, CMD_SOFTRESET)) < 0) { + return ret; + } + + // 2. If using an UART, reset baud rate to RC522 default speed + if (CHIP_DATA(pnd)->io->reset_baud_rate) { + if ((ret = CHIP_DATA(pnd)->io->reset_baud_rate(pnd)) < 0) { + return ret; + } + } + + // 3. Wait for the RC522 to come back to life, as we shouldn't modify any register till that happens + if ((ret = rc522_wait_wakeup(pnd)) < 0) { + return ret; + } + + // 4. If using an UART, restore baud rate to user's choice + if (CHIP_DATA(pnd)->io->upgrade_baud_rate) { + if ((ret = CHIP_DATA(pnd)->io->upgrade_baud_rate(pnd)) < 0) { + return ret; + } + } + + return NFC_SUCCESS; } int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { @@ -259,8 +281,7 @@ int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { rc522_write_reg(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK); } -int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) -{ +int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) { int ret; if (nm.nmt != NMT_ISO14443A) { @@ -276,6 +297,25 @@ int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc return NFC_ENOTIMPL; } +int rc522_initiator_transceive_bits(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, const uint8_t *pbtTxPar, uint8_t *pbtRx, uint8_t *pbtRxPar) { + return NFC_ENOTIMPL; +} + +int rc522_initiator_transceive_bytes(struct nfc_device * pnd, const uint8_t * txData, const size_t txSize, uint8_t * rxData, const size_t rxMaxSize, int timeout) { + int ret; + + if (txSize > FIFO_SIZE) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to send %d bytes, but FIFO has only %d bytes.", txSize, FIFO_SIZE); + return NFC_EOVFLOW; + } + + if ((ret = rc522_write_bulk(pnd, REG_FIFODataReg, txData, txSize)) < 0) { + return ret; + } + + +} + int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { switch (mode) { case N_INITIATOR: @@ -388,7 +428,9 @@ int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, int rc522_abort(struct nfc_device * pnd) { return + // Halt any running commands rc522_start_command(pnd, CMD_IDLE) || + // Clear FIFO rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer, 0xFF); } @@ -422,6 +464,7 @@ int rc522_self_test(struct nfc_device * pnd) { break; default: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Aborting self test for unknown version %02X.", CHIP_DATA(pnd)->version); return NFC_EDEVNOTSUPP; } @@ -454,6 +497,7 @@ int rc522_self_test(struct nfc_device * pnd) { while (1) { if (!timeout_check(&to)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Self test timeout"); return NFC_ETIMEOUT; } @@ -478,8 +522,26 @@ int rc522_self_test(struct nfc_device * pnd) { } if (memcmp(correct, response, FIFO_SIZE) != 0) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Self test values didn't match"); return NFC_ECHIP; } + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Self test executed successfully!"); return NFC_SUCCESS; } + +int rc522_init(struct nfc_device * pnd) { + int version = rc522_read_reg(pnd, REG_VersionReg); + if (version < 0) { + return version; + } + CHIP_DATA(pnd)->version = version; + + int ret = rc522_self_test(pnd); + if (ret == NFC_EDEVNOTSUPP) { + // TODO: Implement another test, maybe? + ret = rc522_soft_reset(pnd); + } + + return ret; +} diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index d8ee526..8f9ab0e 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -26,13 +26,13 @@ struct rc522_io { int (*read)(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t size); int (*write)(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, size_t size); int (*reset_baud_rate)(struct nfc_device * pnd); + int (*upgrade_baud_rate)(struct nfc_device * pnd); }; int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io); void rc522_data_free(struct nfc_device * pnd); -int rc522_self_test(struct nfc_device * pnd); -int rc522_wait_wakeup(struct nfc_device * pnd); int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate); +int rc522_init(struct nfc_device * pnd); int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index 0085db9..eb86e61 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -49,16 +49,16 @@ // Internal data structs const struct rc522_io rc522_uart_io; struct rc522_uart_data { - serial_port port; - uint32_t baudrate; + serial_port port; + uint32_t baudrate; }; #define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) - +/* int rc522_uart_wakeup(struct nfc_device * pnd) { int ret; - /* High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. */ + // High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), RC522_UART_IO_TIMEOUT); if (ret < 0) { @@ -67,48 +67,7 @@ int rc522_uart_wakeup(struct nfc_device * pnd) { return rc522_wait_wakeup(pnd); } - -bool rc522_uart_test_baudrate(struct nfc_device * pnd, uint32_t baudrate) { - int ret; - - if ((ret = uart_set_speed(DRIVER_DATA(pnd)->port, baudrate)) < 0) { - return false; - } - - ret = rc522_wait_wakeup(pnd); - if (ret != NFC_SUCCESS) { - return false; - } - - DRIVER_DATA(pnd)->baudrate = baudrate; - return true; -} - -int rc522_uart_change_baudrate(struct nfc_device * pnd, uint32_t newBaudRate) { - uint32_t oldBaudRate = DRIVER_DATA(pnd)->baudrate; - int ret; - - if (oldBaudRate == newBaudRate) { - return NFC_SUCCESS; - } - - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Switching baud rate from %dbps to %dbps.", oldBaudRate, newBaudRate); - - if ((ret = rc522_send_baudrate(pnd, newBaudRate)) < 0) { - return ret; - } - - if ((ret = uart_set_speed(DRIVER_DATA(pnd)->port, newBaudRate)) < 0) { - return ret; - } - - if ((ret = rc522_wait_wakeup(pnd)) < 0) { - return ret; - } - - DRIVER_DATA(pnd)->baudrate = newBaudRate; - return NFC_SUCCESS; -} +*/ void rc522_uart_close(nfc_device * pnd) { rc522_powerdown(pnd); @@ -118,6 +77,26 @@ void rc522_uart_close(nfc_device * pnd) { nfc_device_free(pnd); } +bool rc522_uart_test_baudrate(struct nfc_device * pnd, uint32_t baudrate) { + int ret; + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempting to establish a connection at %d bps.", baudrate); + + // Update UART baudrate + if ((ret = uart_set_speed(DRIVER_DATA(pnd)->port, baudrate)) < 0) { + return false; + } + + // Attempt to test and initialize the device + if (rc522_init(pnd) != NFC_SUCCESS) { + return false; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Connection with a RC522 at %d bps established successfully.", baudrate); + + return true; +} + int rc522_uart_create(const nfc_context * context, const nfc_connstring connstring, const char * portPath, uint32_t userBaudRate, struct nfc_device ** pndPtr) { int ret; serial_port sp; @@ -155,6 +134,7 @@ int rc522_uart_create(const nfc_context * context, const nfc_connstring connstri return NFC_ESOFT; } DRIVER_DATA(pnd)->port = sp; + DRIVER_DATA(pnd)->baudrate = userBaudRate; // Alloc and init chip's data if (rc522_data_new(pnd, &rc522_uart_io)) { @@ -179,22 +159,6 @@ int rc522_uart_create(const nfc_context * context, const nfc_connstring connstri return NFC_EIO; } - // Change now the baud rate - /* TODO - Check why this doesn't work on my FM17522 and try it with a real MFRC522 - if ((ret = rc522_uart_change_baudrate(pnd, userBaudRate)) < 0) { - rc522_uart_close(pnd); - return ret; - } - */ - - // Now the device is awake and listening at a known baudrate, execute a selftest - // Note that some devices (FM17522 for instance) aren't able to run it - ret = rc522_self_test(pnd); - if (ret != NFC_SUCCESS && ret != NFC_EDEVNOTSUPP) { - rc522_uart_close(pnd); - return ret; - } - *pndPtr = pnd; return NFC_SUCCESS; } @@ -341,16 +305,27 @@ error: } int rc522_uart_reset_baud_rate(struct nfc_device * pnd) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Restoring baud rate to default of %d bps.", RC522_UART_BOOT_SPEED); + return uart_set_speed(DRIVER_DATA(pnd)->port, RC522_UART_BOOT_SPEED); +} + +int rc522_uart_upgrade_baud_rate(struct nfc_device * pnd) { uint32_t userBaudRate = DRIVER_DATA(pnd)->baudrate; + if (userBaudRate == RC522_UART_BOOT_SPEED) { + return NFC_SUCCESS; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Upgrading baud rate to user-specified %d bps.", userBaudRate); return - rc522_uart_test_baudrate(pnd, RC522_UART_BOOT_SPEED) || - rc522_uart_change_baudrate(pnd, userBaudRate); + uart_set_speed(DRIVER_DATA(pnd)->port, userBaudRate) || + rc522_send_baudrate(pnd, userBaudRate); } const struct rc522_io rc522_uart_io = { .read = rc522_uart_read, .write = rc522_uart_write, - .reset_baud_rate = rc522_uart_reset_baud_rate, + .reset_baud_rate = rc522_uart_reset_baud_rate, + .upgrade_baud_rate = rc522_uart_upgrade_baud_rate, }; const struct nfc_driver rc522_uart_driver = { From 1eaa4dabdf9c47dddb58f08dbed3467dcceabfed Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Fri, 19 Jun 2015 23:32:44 +0200 Subject: [PATCH 11/15] Yay! TX for RC522 is now working! --- libnfc/chips/rc522-internal.h | 15 +++ libnfc/chips/rc522.c | 226 +++++++++++++++++++++++++++++----- libnfc/chips/rc522.h | 3 + libnfc/drivers/rc522_uart.c | 6 +- 4 files changed, 213 insertions(+), 37 deletions(-) diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index 093a5ff..c5dcb1c 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -4,6 +4,8 @@ #define __NFC_CHIPS_RC522_INTERNAL_H__ #define FIFO_SIZE 64 +// This is the default value for water level IRQs +#define DEFAULT_WATER_LEVEL 8 typedef enum { RC522_UNKNOWN = 0x00, @@ -35,6 +37,14 @@ typedef enum { #define REG_DivlEnReg 0x03 #define REG_ComIrqReg 0x04 +#define REG_ComIrqReg_Set1 (1 << 7) +#define REG_ComIrqReg_TxIRq (1 << 6) +#define REG_ComIrqReg_RxIRq (1 << 5) +#define REG_ComIrqReg_IdleIRq (1 << 4) +#define REG_ComIrqReg_HiAlertIRq (1 << 3) +#define REG_ComIrqReg_LoAlertIRq (1 << 2) +#define REG_ComIrqReg_ErrIRq (1 << 1) +#define REG_ComIrqReg_TimerIRq (1 << 0) #define REG_DivIrqReg 0x05 #define REG_DivIrqReg_MfinActIRq (1 << 4) @@ -57,6 +67,11 @@ typedef enum { #define REG_ControlReg 0x0C #define REG_BitFramingReg 0x0D +#define REG_BitFramingReg_StartSend (1 << 7) +#define REG_BitFramingReg_RxAlign_PACK(x) ((x & 7) << 4) +#define REG_BitFramingReg_RxAlign_UNPACK(x) ((x >> 4) & 7) +#define REG_BitFramingReg_TxLastBits_PACK(x) ((x & 7) << 0) +#define REG_BitFramingReg_TxLastBits_UNPACK(x) ((x >> 0) & 7) #define REG_CollReg 0x0E diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index 9e0eaad..1764cc8 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -24,7 +24,10 @@ #include "nfc/nfc.h" #include "nfc-internal.h" #include "timing.h" - + +// This divides by 8 with rounding towards infinity +#define BITS2BYTES(x) ((x >> 3) + ((x & 7) ? 1 : 0)) + #define LOG_CATEGORY "libnfc.chip.rc522" #define LOG_GROUP NFC_LOG_GROUP_CHIP @@ -36,6 +39,7 @@ const nfc_baud_rate rc522_iso14443a_supported_baud_rates[] = { NBR_847, NBR_424, struct rc522_chip_data { const struct rc522_io * io; uint8_t version; + int default_timeout; }; #define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) @@ -49,6 +53,7 @@ int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { CHIP_DATA(pnd)->io = io; CHIP_DATA(pnd)->version = RC522_UNKNOWN; + CHIP_DATA(pnd)->default_timeout = 500; return NFC_SUCCESS; } @@ -99,7 +104,11 @@ int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { return val; } -int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t mask) { +int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val) { + return rc522_write_bulk(pnd, reg, &val, 1); +} + +int rc522_write_reg_mask(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t mask) { if (mask != 0xFF) { int oldval = rc522_read_reg(pnd, reg); if (oldval < 0) { @@ -109,7 +118,7 @@ int rc522_write_reg(struct nfc_device * pnd, uint8_t reg, uint8_t val, uint8_t m val = (val & mask) | (oldval & ~mask); } - return rc522_write_bulk(pnd, reg, &val, 1); + return rc522_write_reg(pnd, reg, val); } int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { @@ -145,7 +154,7 @@ int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) { regval |= REG_CommandReg_RcvOff; } - return rc522_write_reg(pnd, REG_CommandReg, regval, 0xFF); + return rc522_write_reg(pnd, REG_CommandReg, regval); } int rc522_wait_wakeup(struct nfc_device * pnd) { @@ -215,7 +224,7 @@ int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate) { return NFC_EDEVNOTSUPP; } - return rc522_write_reg(pnd, REG_SerialSpeedReg, regval, 0xFF); + return rc522_write_reg(pnd, REG_SerialSpeedReg, regval); } int rc522_soft_reset(struct nfc_device * pnd) { @@ -248,37 +257,42 @@ int rc522_soft_reset(struct nfc_device * pnd) { return NFC_SUCCESS; } -int rc522_set_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { +int rc522_set_rf_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { uint8_t txVal, rxVal; switch (speed) { case NBR_106: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Updating RF baud rate to 106kbps."); txVal = REG_TxModeReg_TxSpeed_106k; rxVal = REG_RxModeReg_RxSpeed_106k; break; case NBR_212: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Updating RF baud rate to 212kbps."); txVal = REG_TxModeReg_TxSpeed_212k; rxVal = REG_RxModeReg_RxSpeed_212k; break; case NBR_424: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Updating RF baud rate to 424kbps."); txVal = REG_TxModeReg_TxSpeed_424k; rxVal = REG_RxModeReg_RxSpeed_424k; break; case NBR_847: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Updating RF baud rate to 847kbps."); txVal = REG_TxModeReg_TxSpeed_847k; rxVal = REG_RxModeReg_RxSpeed_847k; break; default: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to switch RF baud rate to 0x%08X.", speed); return NFC_EINVARG; } return - rc522_write_reg(pnd, REG_TxModeReg, txVal, REG_TxModeReg_TxSpeed_MASK) || - rc522_write_reg(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK); + rc522_write_reg_mask(pnd, REG_TxModeReg, txVal, REG_TxModeReg_TxSpeed_MASK) || + rc522_write_reg_mask(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK); } int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) { @@ -288,7 +302,7 @@ int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc return NFC_EINVARG; } - ret = rc522_set_baud_rate(pnd, nm.nbr); + ret = rc522_set_rf_baud_rate(pnd, nm.nbr); if (ret < 0) { return ret; } @@ -297,26 +311,110 @@ int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc return NFC_ENOTIMPL; } -int rc522_initiator_transceive_bits(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, const uint8_t *pbtTxPar, uint8_t *pbtRx, uint8_t *pbtRxPar) { - return NFC_ENOTIMPL; -} - -int rc522_initiator_transceive_bytes(struct nfc_device * pnd, const uint8_t * txData, const size_t txSize, uint8_t * rxData, const size_t rxMaxSize, int timeout) { +int rc522_transceive(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { + size_t txBytes = BITS2BYTES(txBits); + size_t transmitted = MIN(txBytes, FIFO_SIZE); int ret; - if (txSize > FIFO_SIZE) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to send %d bytes, but FIFO has only %d bytes.", txSize, FIFO_SIZE); - return NFC_EOVFLOW; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: sending %d bits (%d bytes).", txBits, txBytes); + + ret = + // Halt the device (just in case) + rc522_abort(pnd) || + // Clear interesting IRQs + rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_TxIRq || REG_ComIrqReg_RxIRq || REG_ComIrqReg_LoAlertIRq || REG_ComIrqReg_ErrIRq) || + // Write as much data as possible + rc522_write_bulk(pnd, REG_FIFODataReg, txData, transmitted) || + // Start command + rc522_start_command(pnd, CMD_TRANSCEIVE) || + // Start transmission and adjust bit framing + rc522_write_reg(pnd, REG_BitFramingReg, REG_BitFramingReg_StartSend | REG_BitFramingReg_RxAlign_PACK(0) | REG_BitFramingReg_TxLastBits_PACK(txBits)); + if (ret < 0) { + goto abort; } - if ((ret = rc522_write_bulk(pnd, REG_FIFODataReg, txData, txSize)) < 0) { + timeout_t to; + // If "timeout" is zero there's no timeout + if (timeout != 0) { + // If "timeout" is -1 then we'll use the default timeout + if (timeout == -1) { + timeout = CHIP_DATA(pnd)->default_timeout; + } + timeout_init(&to, timeout); + } + while (1) { + if (timeout != 0 && !timeout_check(&to)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: transmission timeout."); + ret = NFC_ETIMEOUT; + goto abort; + } + + int irqs = rc522_read_reg(pnd, REG_ComIrqReg); + if (irqs < 0) { + // If a read error happens attempt to abort transmission and return the error code + ret = irqs; + goto abort; + } + + if (irqs & REG_ComIrqReg_ErrIRq) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: RC522 set ErrIRq flag."); + // If the RC522 detects an error abort the transmission and notify the caller + ret = NFC_ECHIP; + goto abort; + } + + if (irqs & REG_ComIrqReg_TxIRq) { + // Check if the FIFO has underflowed (ie the transmission has ended before we've feeded all the bytes to the FIFO) + if (transmitted < txBytes) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_transceive couldn't feed bytes fast enough. Only %d out of %d bytes have been sent. Aborting transmission.", transmitted, txBytes); + ret = NFC_ESOFT; + goto abort; + } + // Otherwise we're done + break; + } + + if ((irqs & REG_ComIrqReg_LoAlertIRq) && transmitted < txBytes) { + // Okay, now attempt to write as many bytes as possible. This IRQ is generated based on the water level, so we know for sure we can feed at least FIFO_SIZE - DEFAULT_WATER_LEVEL bytes. + size_t chunkSize = MIN(txBytes - transmitted, FIFO_SIZE - DEFAULT_WATER_LEVEL); + if ((ret = rc522_write_bulk(pnd, REG_FIFODataReg, txData + transmitted, chunkSize)) < 0) { + goto abort; + } + transmitted += chunkSize; + + // TODO: Should we clear the flag before or after feeding the data? + if ((ret = rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_LoAlertIRq)) < 0) { + goto abort; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: fed another %d bytes to FIFO.", chunkSize); + } + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: transmission finished."); + return 0; + +abort: + rc522_abort(pnd); + return ret; +} + +int rc522_initiator_transceive_bits(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, const uint8_t * pbtTxPar, uint8_t * rxData, uint8_t * pbtRxPar) { + // TODO: Do something with pbtTxPar and pbtRxPar + return rc522_transceive(pnd, txData, txBits, rxData, ~0, -1); +} + +int rc522_initiator_transceive_bytes(struct nfc_device * pnd, const uint8_t * txData, const size_t txSize, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { + int ret = rc522_transceive(pnd, txData, txSize * 8, rxData, rxMaxBytes, timeout); + if (ret < 0) { return ret; } - - + return ret / 8; } int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { + (void) pnd; + switch (mode) { case N_INITIATOR: *supported_mt = rc522_initiator_modulation; @@ -334,6 +432,8 @@ int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, } int rc522_get_supported_baud_rate(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br) { + (void) pnd; + switch (mode) { case N_INITIATOR: switch (nmt) { @@ -364,8 +464,8 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property } ret = - rc522_write_reg(pnd, REG_TxModeReg, enable ? ~0 : 0, REG_TxModeReg_TxCRCEn) || - rc522_write_reg(pnd, REG_RxModeReg, enable ? ~0 : 0, REG_RxModeReg_RxCRCEn); + rc522_write_reg_mask(pnd, REG_TxModeReg, enable ? ~0 : 0, REG_TxModeReg_TxCRCEn) || + rc522_write_reg_mask(pnd, REG_RxModeReg, enable ? ~0 : 0, REG_RxModeReg_RxCRCEn); if (ret) { return ret; } @@ -378,7 +478,8 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - ret = rc522_write_reg(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable); + // Note it's parity DISABLE (ie active low) + ret = rc522_write_reg_mask(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable); if (ret) { return ret; } @@ -391,10 +492,10 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; case NP_ACTIVATE_FIELD: - return rc522_write_reg(pnd, REG_TxControlReg, enable ? ~0 : 0, REG_TxControlReg_Tx2RFEn | REG_TxControlReg_Tx1RFEn); + return rc522_write_reg_mask(pnd, REG_TxControlReg, enable ? ~0 : 0, REG_TxControlReg_Tx2RFEn | REG_TxControlReg_Tx1RFEn); case NP_ACTIVATE_CRYPTO1: - return rc522_write_reg(pnd, REG_Status2Reg, enable ? ~0 : 0, REG_Status2Reg_MFCrypto1On); + return rc522_write_reg_mask(pnd, REG_Status2Reg, enable ? ~0 : 0, REG_Status2Reg_MFCrypto1On); case NP_FORCE_ISO14443_A: // ISO14443-A is the only mode supported by MFRC522 @@ -405,25 +506,82 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - return rc522_set_baud_rate(pnd, NBR_106); + return rc522_set_rf_baud_rate(pnd, NBR_106); case NP_ACCEPT_MULTIPLE_FRAMES: + // TODO: Figure out what this does and implement it + // HACK: Return NFC_SUCCESS so nfc-anticol runs + return NFC_SUCCESS; + case NP_AUTO_ISO14443_4: + // TODO: Figure out what this does and implement it + // HACK: Return NFC_SUCCESS so nfc-anticol runs + return NFC_SUCCESS; + case NP_ACCEPT_INVALID_FRAMES: + // TODO: Figure out what this does and implement it + // HACK: Return NFC_SUCCESS so nfc-anticol runs + return NFC_SUCCESS; + case NP_INFINITE_SELECT: + // TODO: The RC522 can't do scans on its own, what now? + // HACK: Return NFC_SUCCESS so nfc-anticol runs + return NFC_SUCCESS; + case NP_FORCE_ISO14443_B: + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Attempted to enable ISO14443B"); + if (enable) { + return NFC_EDEVNOTSUPP; + } + return NFC_SUCCESS; + case NP_TIMEOUT_COMMAND: case NP_TIMEOUT_ATR: case NP_TIMEOUT_COM: - return NFC_EINVARG; + break; } return NFC_EINVARG; } int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value) { - // TODO - return NFC_ENOTIMPL; + switch (property) { + case NP_TIMEOUT_COMMAND: + if (value >= 0) { + CHIP_DATA(pnd)->default_timeout = value; + return NFC_SUCCESS; + } + break; + + case NP_TIMEOUT_ATR: + // TODO: Figure out what this does and implement it + return NFC_ENOTIMPL; + + case NP_TIMEOUT_COM: + // TODO: Figure out what this does and implement it + return NFC_ENOTIMPL; + + case NP_HANDLE_CRC: + case NP_HANDLE_PARITY: + case NP_EASY_FRAMING: + case NP_ACTIVATE_FIELD: + case NP_ACTIVATE_CRYPTO1: + case NP_FORCE_ISO14443_A: + case NP_FORCE_SPEED_106: + case NP_ACCEPT_MULTIPLE_FRAMES: + case NP_AUTO_ISO14443_4: + case NP_ACCEPT_INVALID_FRAMES: + case NP_INFINITE_SELECT: + case NP_FORCE_ISO14443_B: + break; + } + + return NFC_EINVARG; +} + +int rc522_initiator_init(struct nfc_device * pnd) { + // TODO: Should we be doing something here? + return NFC_SUCCESS; } int rc522_abort(struct nfc_device * pnd) { @@ -431,11 +589,11 @@ int rc522_abort(struct nfc_device * pnd) { // Halt any running commands rc522_start_command(pnd, CMD_IDLE) || // Clear FIFO - rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer, 0xFF); + rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer); } int rc522_powerdown(struct nfc_device * pnd) { - return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_NOCMDCHANGE, 0xFF); + return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_NOCMDCHANGE); } // NXP MFRC522 datasheet section 16.1.1 @@ -481,9 +639,9 @@ int rc522_self_test(struct nfc_device * pnd) { rc522_write_bulk(pnd, REG_FIFODataReg, zeroes, sizeof(zeroes)) || rc522_start_command(pnd, CMD_MEM) || // 3. Enable the self test by writing 0x09 to the AutoTestReg register - rc522_write_reg(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Enabled, REG_AutoTestReg_SelfTest_MASK) || + rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Enabled, REG_AutoTestReg_SelfTest_MASK) || // 4. Write 0x00h to the FIFO buffer - rc522_write_reg(pnd, REG_FIFODataReg, 0x00, 0xFF) || + rc522_write_reg(pnd, REG_FIFODataReg, 0x00) || // 5. Start the self test with the CalcCRC command rc522_start_command(pnd, CMD_CALCCRC); if (ret) { @@ -516,7 +674,7 @@ int rc522_self_test(struct nfc_device * pnd) { // 7. Read selftest result rc522_read_bulk(pnd, REG_FIFODataReg, response, FIFO_SIZE) || // 8. Disable selftest operation mode - rc522_write_reg(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Disabled, REG_AutoTestReg_SelfTest_MASK); + rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Disabled, REG_AutoTestReg_SelfTest_MASK); if (ret) { return ret; } diff --git a/libnfc/chips/rc522.h b/libnfc/chips/rc522.h index 8f9ab0e..af0c3dc 100644 --- a/libnfc/chips/rc522.h +++ b/libnfc/chips/rc522.h @@ -34,6 +34,9 @@ void rc522_data_free(struct nfc_device * pnd); int rc522_send_baudrate(struct nfc_device * pnd, uint32_t baudrate); int rc522_init(struct nfc_device * pnd); +int rc522_initiator_init(nfc_device * pnd); +int rc522_initiator_transceive_bits(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, const uint8_t * pbtTxPar, uint8_t * rxData, uint8_t * pbtRxPar); +int rc522_initiator_transceive_bytes(struct nfc_device * pnd, const uint8_t * txData, const size_t txSize, uint8_t * rxData, const size_t rxMaxBytes, int timeout); int rc522_get_supported_modulation(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt); int rc522_get_supported_baud_rate(nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate ** const supported_br); int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property, const bool enable); diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index eb86e61..83b615b 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -336,15 +336,15 @@ const struct nfc_driver rc522_uart_driver = { .close = rc522_uart_close, // .strerror = rc522_strerror, -// .initiator_init = rc522_initiator_init, + .initiator_init = rc522_initiator_init, // MFRC522 has no secure element .initiator_init_secure_element = NULL, // .initiator_select_passive_target = rc522_initiator_select_passive_target, // .initiator_poll_target = rc522_initiator_poll_target, .initiator_select_dep_target = NULL, // .initiator_deselect_target = rc522_initiator_deselect_target, -// .initiator_transceive_bytes = rc522_initiator_transceive_bytes, -// .initiator_transceive_bits = rc522_initiator_transceive_bits, + .initiator_transceive_bytes = rc522_initiator_transceive_bytes, + .initiator_transceive_bits = rc522_initiator_transceive_bits, // .initiator_transceive_bytes_timed = rc522_initiator_transceive_bytes_timed, // .initiator_transceive_bits_timed = rc522_initiator_transceive_bits_timed, // .initiator_target_is_present = rc522_initiator_target_is_present, From a9b1b94ffd93b2d1035763a8362628eef8916867 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 20 Jun 2015 16:58:11 +0200 Subject: [PATCH 12/15] Use default timeout in nfc-anticol example (prevents RX from locking after HALT) --- examples/nfc-anticol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/nfc-anticol.c b/examples/nfc-anticol.c index 3e9d533..4630f74 100644 --- a/examples/nfc-anticol.c +++ b/examples/nfc-anticol.c @@ -130,7 +130,7 @@ transmit_bytes(const uint8_t *pbtTx, const size_t szTx) printf("Response after %u cycles\n", cycles); } } else { - if ((res = nfc_initiator_transceive_bytes(pnd, pbtTx, szTx, abtRx, sizeof(abtRx), 0)) < 0) + if ((res = nfc_initiator_transceive_bytes(pnd, pbtTx, szTx, abtRx, sizeof(abtRx), -1)) < 0) return false; } szRx = res; From 6cd025ed002278ae21d9aad59978e8081e2b29e5 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 20 Jun 2015 16:59:09 +0200 Subject: [PATCH 13/15] Added special case for timeouts that don't expire --- libnfc/timing.c | 17 ++++++++++++++--- libnfc/timing.h | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libnfc/timing.c b/libnfc/timing.c index 8b17d71..e937a39 100644 --- a/libnfc/timing.c +++ b/libnfc/timing.c @@ -22,6 +22,9 @@ #define MICROS_PER_SEC 1000000 #define NANOS_PER_SEC 1000000000 +#define MAGIC_EXPIRED ((uint64_t) -1) +#define MAGIC_NEVER ((uint64_t) -2) + // Use Windows' API directly if Win32 for highest possible resolution #if defined(CYGWIN) || defined(_WIN32) # include @@ -61,14 +64,22 @@ void timeout_init(timeout_t * to, unsigned int millis) { *to = time_millis() + millis; } +void timeout_never(timeout_t * to) { + *to = MAGIC_NEVER; +} + bool timeout_check(timeout_t * to) { - if (*to == 0) { - return false; + switch (*to) { + case MAGIC_EXPIRED: + return false; + case MAGIC_NEVER: + return true; } ms_t now = time_millis(); if (now >= *to) { - *to = 0; + // Mark as expired and fail in next check + *to = MAGIC_EXPIRED; } return true; diff --git a/libnfc/timing.h b/libnfc/timing.h index beb9e47..5ee7bab 100644 --- a/libnfc/timing.h +++ b/libnfc/timing.h @@ -38,6 +38,12 @@ typedef ms_t timeout_t; */ void timeout_init(timeout_t * to, unsigned int millis); +/** + * @brief Initializes a timeout which never expires + * @param to Timeout handle + */ +void timeout_never(timeout_t * to); + /** * @brief Checks if the timeout has NOT expired * @param to Timeout handle From 6641097d485e454ee58c9adedaeee75937f7faf7 Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 20 Jun 2015 16:59:50 +0200 Subject: [PATCH 14/15] Replaced short-circuit logic with macros --- libnfc/chips/rc522-internal.h | 2 + libnfc/chips/rc522.c | 307 ++++++++++++++++++++-------------- libnfc/drivers/rc522_uart.c | 54 +++--- 3 files changed, 210 insertions(+), 153 deletions(-) diff --git a/libnfc/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h index c5dcb1c..f7983a4 100644 --- a/libnfc/chips/rc522-internal.h +++ b/libnfc/chips/rc522-internal.h @@ -61,6 +61,8 @@ typedef enum { #define REG_FIFOLevelReg 0x0A #define REG_FIFOLevelReg_FlushBuffer (1 << 7) +#define REG_FIFOLevelReg_Level_PACK(x) ((x & 0x7F) << 0) +#define REG_FIFOLevelReg_Level_UNPACK(x) ((x >> 0) & 0x7F) #define REG_WaterLevelReg 0x0B diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index 1764cc8..ce313bc 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -27,10 +27,15 @@ // This divides by 8 with rounding towards infinity #define BITS2BYTES(x) ((x >> 3) + ((x & 7) ? 1 : 0)) +#define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) +#define CHK(x) ret = (x); if (ret < 0) { return ret; } #define LOG_CATEGORY "libnfc.chip.rc522" #define LOG_GROUP NFC_LOG_GROUP_CHIP +#define TIMEOUT_DEFAULT -1 +#define TIMEOUT_NEVER 0 + const nfc_modulation_type rc522_initiator_modulation[] = { NMT_ISO14443A, 0 }; const nfc_modulation_type rc522_target_modulation[] = { 0 }; @@ -42,7 +47,6 @@ struct rc522_chip_data { int default_timeout; }; -#define CHIP_DATA(x) ((struct rc522_chip_data *) (x)->chip_data) int rc522_data_new(struct nfc_device * pnd, const struct rc522_io * io) { pnd->chip_data = malloc(sizeof(struct rc522_chip_data)); @@ -96,11 +100,7 @@ int rc522_write_bulk(struct nfc_device * pnd, uint8_t reg, const uint8_t * val, int rc522_read_reg(struct nfc_device * pnd, uint8_t reg) { uint8_t val; int ret; - - if ((ret = rc522_read_bulk(pnd, reg, &val, 1)) < 0) { - return ret; - } - + CHK(rc522_read_bulk(pnd, reg, &val, 1)); return val; } @@ -231,27 +231,19 @@ int rc522_soft_reset(struct nfc_device * pnd) { int ret; // 1. Execute reset command - if ((ret = rc522_start_command(pnd, CMD_SOFTRESET)) < 0) { - return ret; - } + CHK(rc522_start_command(pnd, CMD_SOFTRESET)); // 2. If using an UART, reset baud rate to RC522 default speed if (CHIP_DATA(pnd)->io->reset_baud_rate) { - if ((ret = CHIP_DATA(pnd)->io->reset_baud_rate(pnd)) < 0) { - return ret; - } + CHK(CHIP_DATA(pnd)->io->reset_baud_rate(pnd)); } // 3. Wait for the RC522 to come back to life, as we shouldn't modify any register till that happens - if ((ret = rc522_wait_wakeup(pnd)) < 0) { - return ret; - } + CHK(rc522_wait_wakeup(pnd)); // 4. If using an UART, restore baud rate to user's choice if (CHIP_DATA(pnd)->io->upgrade_baud_rate) { - if ((ret = CHIP_DATA(pnd)->io->upgrade_baud_rate(pnd)) < 0) { - return ret; - } + CHK(CHIP_DATA(pnd)->io->upgrade_baud_rate(pnd)); } return NFC_SUCCESS; @@ -259,6 +251,7 @@ int rc522_soft_reset(struct nfc_device * pnd) { int rc522_set_rf_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { uint8_t txVal, rxVal; + int ret; switch (speed) { case NBR_106: @@ -290,9 +283,10 @@ int rc522_set_rf_baud_rate(struct nfc_device * pnd, nfc_baud_rate speed) { return NFC_EINVARG; } - return - rc522_write_reg_mask(pnd, REG_TxModeReg, txVal, REG_TxModeReg_TxSpeed_MASK) || - rc522_write_reg_mask(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK); + CHK(rc522_write_reg_mask(pnd, REG_TxModeReg, txVal, REG_TxModeReg_TxSpeed_MASK)); + CHK(rc522_write_reg_mask(pnd, REG_RxModeReg, rxVal, REG_RxModeReg_RxSpeed_MASK)); + + return NFC_SUCCESS; } int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc_modulation nm, const uint8_t * pbtInitData, const size_t szInitData, nfc_target * pnt, int timeout) { @@ -302,73 +296,67 @@ int rc522_initiator_select_passive_target_ext(struct nfc_device * pnd, const nfc return NFC_EINVARG; } - ret = rc522_set_rf_baud_rate(pnd, nm.nbr); - if (ret < 0) { - return ret; - } + CHK(rc522_set_rf_baud_rate(pnd, nm.nbr)); // TODO return NFC_ENOTIMPL; } -int rc522_transceive(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { +void rc522_timeout_init(struct nfc_device * pnd, timeout_t * to, int timeout) { + if (timeout == TIMEOUT_NEVER) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_timeout_init: creating timeout which doesn't expire."); + timeout_never(to); + } else { + if (timeout == TIMEOUT_DEFAULT) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_timeout_init: creating with default time (%d ms).", CHIP_DATA(pnd)->default_timeout); + timeout = CHIP_DATA(pnd)->default_timeout; + } else { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_timeout_init: creating with custom time of %d ms.", timeout); + } + + timeout_init(to, timeout); + } +} + +int rc522_rf_tx(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, timeout_t * timeout, bool transceive) { size_t txBytes = BITS2BYTES(txBits); size_t transmitted = MIN(txBytes, FIFO_SIZE); int ret; - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: sending %d bits (%d bytes).", txBits, txBytes); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: sending %d bits (%d bytes).", txBits, txBytes); - ret = - // Halt the device (just in case) - rc522_abort(pnd) || - // Clear interesting IRQs - rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_TxIRq || REG_ComIrqReg_RxIRq || REG_ComIrqReg_LoAlertIRq || REG_ComIrqReg_ErrIRq) || - // Write as much data as possible - rc522_write_bulk(pnd, REG_FIFODataReg, txData, transmitted) || - // Start command - rc522_start_command(pnd, CMD_TRANSCEIVE) || - // Start transmission and adjust bit framing - rc522_write_reg(pnd, REG_BitFramingReg, REG_BitFramingReg_StartSend | REG_BitFramingReg_RxAlign_PACK(0) | REG_BitFramingReg_TxLastBits_PACK(txBits)); - if (ret < 0) { - goto abort; + CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_TxIRq | REG_ComIrqReg_RxIRq | REG_ComIrqReg_LoAlertIRq | REG_ComIrqReg_ErrIRq)); + CHK(rc522_write_bulk(pnd, REG_FIFODataReg, txData, transmitted)); + + if (transceive) { + // If transceiving we must first start the command and then configure framing and start transmission + CHK(rc522_start_command(pnd, CMD_TRANSCEIVE)); + CHK(rc522_write_reg(pnd, REG_BitFramingReg, REG_BitFramingReg_StartSend | REG_BitFramingReg_RxAlign_PACK(0) | REG_BitFramingReg_TxLastBits_PACK(txBits))); + } else { + // If only transmitting we must configure framing and then start the transmission + CHK(rc522_write_reg(pnd, REG_BitFramingReg, REG_BitFramingReg_RxAlign_PACK(0) | REG_BitFramingReg_TxLastBits_PACK(txBits))); + CHK(rc522_start_command(pnd, CMD_TRANSMIT)); } - timeout_t to; - // If "timeout" is zero there's no timeout - if (timeout != 0) { - // If "timeout" is -1 then we'll use the default timeout - if (timeout == -1) { - timeout = CHIP_DATA(pnd)->default_timeout; - } - timeout_init(&to, timeout); - } while (1) { - if (timeout != 0 && !timeout_check(&to)) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: transmission timeout."); - ret = NFC_ETIMEOUT; - goto abort; + if (!timeout_check(timeout)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: transmission timeout."); + return NFC_ETIMEOUT; } - int irqs = rc522_read_reg(pnd, REG_ComIrqReg); - if (irqs < 0) { - // If a read error happens attempt to abort transmission and return the error code - ret = irqs; - goto abort; - } + int irqs = CHK(rc522_read_reg(pnd, REG_ComIrqReg)); if (irqs & REG_ComIrqReg_ErrIRq) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: RC522 set ErrIRq flag."); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: RC522 set ErrIRq flag."); // If the RC522 detects an error abort the transmission and notify the caller - ret = NFC_ECHIP; - goto abort; + return NFC_ECHIP; } if (irqs & REG_ComIrqReg_TxIRq) { // Check if the FIFO has underflowed (ie the transmission has ended before we've feeded all the bytes to the FIFO) if (transmitted < txBytes) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_transceive couldn't feed bytes fast enough. Only %d out of %d bytes have been sent. Aborting transmission.", transmitted, txBytes); - ret = NFC_ESOFT; - goto abort; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "rc522_rf_tx: couldn't feed bytes fast enough. Only %d out of %d bytes have been sent. Aborting transmission.", transmitted, txBytes); + return NFC_ESOFT; } // Otherwise we're done break; @@ -377,39 +365,116 @@ int rc522_transceive(struct nfc_device * pnd, const uint8_t * txData, const size if ((irqs & REG_ComIrqReg_LoAlertIRq) && transmitted < txBytes) { // Okay, now attempt to write as many bytes as possible. This IRQ is generated based on the water level, so we know for sure we can feed at least FIFO_SIZE - DEFAULT_WATER_LEVEL bytes. size_t chunkSize = MIN(txBytes - transmitted, FIFO_SIZE - DEFAULT_WATER_LEVEL); - if ((ret = rc522_write_bulk(pnd, REG_FIFODataReg, txData + transmitted, chunkSize)) < 0) { - goto abort; - } + CHK(rc522_write_bulk(pnd, REG_FIFODataReg, txData + transmitted, chunkSize)); transmitted += chunkSize; // TODO: Should we clear the flag before or after feeding the data? - if ((ret = rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_LoAlertIRq)) < 0) { - goto abort; - } + CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_LoAlertIRq)); - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: fed another %d bytes to FIFO.", chunkSize); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: fed another %d bytes to FIFO.", chunkSize); } } - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_transceive: transmission finished."); - return 0; + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: transmission finished."); + return NFC_SUCCESS; +} + +int rc522_rf_rx(struct nfc_device * pnd, uint8_t * rxData, const size_t rxMaxBytes, timeout_t * timeout, bool transceive) { + int ret; + size_t received = 0; + + // Clear this as early as possible + CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_HiAlertIRq)); + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: receiving up to %d bytes.", rxMaxBytes); + + if (!transceive) { + CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_TxIRq | REG_ComIrqReg_RxIRq | REG_ComIrqReg_LoAlertIRq | REG_ComIrqReg_ErrIRq)); + CHK(rc522_start_command(pnd, CMD_RECEIVE)); + } + + while (1) { + if (!timeout_check(timeout)) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: transmission timeout."); + return NFC_ETIMEOUT; + } + + int irqs = CHK(rc522_read_reg(pnd, REG_ComIrqReg)); + + if (irqs & REG_ComIrqReg_ErrIRq) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: RC522 set ErrIRq flag."); + // If the RC522 detects an error abort the transmission and notify the caller + return NFC_ECHIP; + } + + if (irqs & REG_ComIrqReg_RxIRq) { + break; + } + + if (irqs & REG_ComIrqReg_HiAlertIRq) { + size_t chunkSize = FIFO_SIZE - DEFAULT_WATER_LEVEL; + if (rxMaxBytes - received < chunkSize) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: RX buffer overflow (buffer contains %d bytes and may hold up to %d bytes, but needs %d more).", received, rxMaxBytes, chunkSize); + return NFC_EOVFLOW; + } + + CHK(rc522_read_bulk(pnd, REG_FIFODataReg, rxData + received, chunkSize)); + received += chunkSize; + + // TODO: Should we clear the flag before or after feeding the data? + CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_HiAlertIRq)); + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: read another %d bytes from FIFO.", chunkSize); + } + } + + CHK(rc522_read_reg(pnd, REG_FIFOLevelReg)); + size_t remaining = REG_FIFOLevelReg_Level_UNPACK(ret); + + if (rxMaxBytes - received < remaining) { + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: RX buffer overflow (buffer contains %d bytes and may hold up to %d bytes, but needs %d more).", received, rxMaxBytes, remaining); + return NFC_EOVFLOW; + } + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: reading last %d bytes from FIFO.", remaining); + CHK(rc522_read_bulk(pnd, REG_FIFODataReg, rxData + received, remaining)); + received += remaining; + + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_rx: receive finished. Read %d bytes.", received); + + return received; +} + +int rc522_transceive(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { + int ret; + timeout_t to; + rc522_timeout_init(pnd, &to, timeout); + + CHK(rc522_abort(pnd)); + + ret = rc522_rf_tx(pnd, txData, txBits, &to, true); + if (ret < 0) { + rc522_abort(pnd); + return ret; + } + + ret = rc522_rf_rx(pnd, rxData, rxMaxBytes, &to, true); + if (ret < 0) { + rc522_abort(pnd); + } -abort: - rc522_abort(pnd); return ret; } int rc522_initiator_transceive_bits(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, const uint8_t * pbtTxPar, uint8_t * rxData, uint8_t * pbtRxPar) { + int ret; // TODO: Do something with pbtTxPar and pbtRxPar - return rc522_transceive(pnd, txData, txBits, rxData, ~0, -1); + CHK(rc522_transceive(pnd, txData, txBits, rxData, ~0, TIMEOUT_DEFAULT)); + return ret * 8; } int rc522_initiator_transceive_bytes(struct nfc_device * pnd, const uint8_t * txData, const size_t txSize, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { - int ret = rc522_transceive(pnd, txData, txSize * 8, rxData, rxMaxBytes, timeout); - if (ret < 0) { - return ret; - } - return ret / 8; + return rc522_transceive(pnd, txData, txSize * 8, rxData, rxMaxBytes, timeout); } int rc522_get_supported_modulation(struct nfc_device * pnd, const nfc_mode mode, const nfc_modulation_type ** const supported_mt) { @@ -463,12 +528,8 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property return NFC_SUCCESS; } - ret = - rc522_write_reg_mask(pnd, REG_TxModeReg, enable ? ~0 : 0, REG_TxModeReg_TxCRCEn) || - rc522_write_reg_mask(pnd, REG_RxModeReg, enable ? ~0 : 0, REG_RxModeReg_RxCRCEn); - if (ret) { - return ret; - } + CHK(rc522_write_reg_mask(pnd, REG_TxModeReg, enable ? ~0 : 0, REG_TxModeReg_TxCRCEn)); + CHK(rc522_write_reg_mask(pnd, REG_RxModeReg, enable ? ~0 : 0, REG_RxModeReg_RxCRCEn)); pnd->bCrc = enable; return NFC_SUCCESS; @@ -479,10 +540,7 @@ int rc522_set_property_bool(struct nfc_device * pnd, const nfc_property property } // Note it's parity DISABLE (ie active low) - ret = rc522_write_reg_mask(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable); - if (ret) { - return ret; - } + CHK(rc522_write_reg_mask(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable)); pnd->bPar = enable; return NFC_SUCCESS; @@ -585,11 +643,14 @@ int rc522_initiator_init(struct nfc_device * pnd) { } int rc522_abort(struct nfc_device * pnd) { - return - // Halt any running commands - rc522_start_command(pnd, CMD_IDLE) || - // Clear FIFO - rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer); + int ret; + + // Halt any running commands + CHK(rc522_start_command(pnd, CMD_IDLE)); + // Clear FIFO + CHK(rc522_write_reg(pnd, REG_FIFOLevelReg, REG_FIFOLevelReg_FlushBuffer)); + + return NFC_SUCCESS; } int rc522_powerdown(struct nfc_device * pnd) { @@ -631,22 +692,19 @@ int rc522_self_test(struct nfc_device * pnd) { uint8_t zeroes[25]; memset(zeroes, 0x00, sizeof(zeroes)); + int ret; // MFRC522 datasheet section 16.1.1 - int ret = - // 1. Perform a soft reset - rc522_soft_reset(pnd) || - // 2. Clear the internal buffer by writing 25 bytes of 0x00 and execute the Mem command - rc522_write_bulk(pnd, REG_FIFODataReg, zeroes, sizeof(zeroes)) || - rc522_start_command(pnd, CMD_MEM) || - // 3. Enable the self test by writing 0x09 to the AutoTestReg register - rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Enabled, REG_AutoTestReg_SelfTest_MASK) || - // 4. Write 0x00h to the FIFO buffer - rc522_write_reg(pnd, REG_FIFODataReg, 0x00) || - // 5. Start the self test with the CalcCRC command - rc522_start_command(pnd, CMD_CALCCRC); - if (ret) { - return ret; - } + // 1. Perform a soft reset + CHK(rc522_soft_reset(pnd)); + // 2. Clear the internal buffer by writing 25 bytes of 0x00 and execute the Mem command + CHK(rc522_write_bulk(pnd, REG_FIFODataReg, zeroes, sizeof(zeroes))); + CHK(rc522_start_command(pnd, CMD_MEM)); + // 3. Enable the self test by writing 0x09 to the AutoTestReg register + CHK(rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Enabled, REG_AutoTestReg_SelfTest_MASK)); + // 4. Write 0x00h to the FIFO buffer + CHK(rc522_write_reg(pnd, REG_FIFODataReg, 0x00)); + // 5. Start the self test with the CalcCRC command + CHK(rc522_start_command(pnd, CMD_CALCCRC)); // 6. Wait for the RC522 to calculate the selftest values // The official datasheet does not mentions how much time does it take, let's use 50ms @@ -659,9 +717,7 @@ int rc522_self_test(struct nfc_device * pnd) { return NFC_ETIMEOUT; } - if ((ret = rc522_read_reg(pnd, REG_DivIrqReg)) < 0) { - return ret; - } + CHK(rc522_read_reg(pnd, REG_DivIrqReg)); // If the RC522 has finished calculating the CRC proceed if (ret & REG_DivIrqReg_CRCIRq) { @@ -670,14 +726,10 @@ int rc522_self_test(struct nfc_device * pnd) { } uint8_t response[FIFO_SIZE]; - ret = - // 7. Read selftest result - rc522_read_bulk(pnd, REG_FIFODataReg, response, FIFO_SIZE) || - // 8. Disable selftest operation mode - rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Disabled, REG_AutoTestReg_SelfTest_MASK); - if (ret) { - return ret; - } + // 7. Read selftest result + CHK(rc522_read_bulk(pnd, REG_FIFODataReg, response, FIFO_SIZE)); + // 8. Disable selftest operation mode + CHK(rc522_write_reg_mask(pnd, REG_AutoTestReg, REG_AutoTestReg_SelfTest_Disabled, REG_AutoTestReg_SelfTest_MASK)); if (memcmp(correct, response, FIFO_SIZE) != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Self test values didn't match"); @@ -689,13 +741,12 @@ int rc522_self_test(struct nfc_device * pnd) { } int rc522_init(struct nfc_device * pnd) { - int version = rc522_read_reg(pnd, REG_VersionReg); - if (version < 0) { - return version; - } + int ret; + + int version = CHK(rc522_read_reg(pnd, REG_VersionReg)); CHIP_DATA(pnd)->version = version; - int ret = rc522_self_test(pnd); + ret = rc522_self_test(pnd); if (ret == NFC_EDEVNOTSUPP) { // TODO: Implement another test, maybe? ret = rc522_soft_reset(pnd); diff --git a/libnfc/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c index 83b615b..d5fb5d4 100644 --- a/libnfc/drivers/rc522_uart.c +++ b/libnfc/drivers/rc522_uart.c @@ -38,14 +38,17 @@ #include "chips/rc522.h" #include "uart.h" -#define RC522_UART_BOOT_SPEED 9600 -#define RC522_UART_DEFAULT_SPEED 115200 -#define RC522_UART_DRIVER_NAME "rc522_uart" -#define RC522_UART_IO_TIMEOUT 50 - #define LOG_CATEGORY "libnfc.driver.rc522_uart" #define LOG_GROUP NFC_LOG_GROUP_DRIVER +#define BOOT_BAUD_RATE 9600 +#define DEFAULT_BAUD_RATE 115200 +#define DRIVER_NAME "rc522_uart" +#define IO_TIMEOUT 50 + +#define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) +#define CHK(x) ret = (x); if (ret < 0) { return ret; } + // Internal data structs const struct rc522_io rc522_uart_io; struct rc522_uart_data { @@ -53,14 +56,13 @@ struct rc522_uart_data { uint32_t baudrate; }; -#define DRIVER_DATA(pnd) ((struct rc522_uart_data*)(pnd->driver_data)) /* int rc522_uart_wakeup(struct nfc_device * pnd) { int ret; // High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for RC522 being wakeup. const uint8_t rc522_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), RC522_UART_IO_TIMEOUT); + ret = uart_send(DRIVER_DATA(pnd)->port, rc522_wakeup_preamble, sizeof(rc522_wakeup_preamble), IO_TIMEOUT); if (ret < 0) { return ret; } @@ -151,10 +153,10 @@ int rc522_uart_create(const nfc_context * context, const nfc_connstring connstri // Let's try first with boot baud rate if ( - !rc522_uart_test_baudrate(pnd, RC522_UART_BOOT_SPEED) && + !rc522_uart_test_baudrate(pnd, BOOT_BAUD_RATE) && !rc522_uart_test_baudrate(pnd, userBaudRate) ) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Could not connect with RC522 at %d or %d bps.", RC522_UART_BOOT_SPEED, userBaudRate); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Could not connect with RC522 at %d or %d bps.", BOOT_BAUD_RATE, userBaudRate); rc522_uart_close(pnd); return NFC_EIO; } @@ -171,10 +173,10 @@ size_t rc522_uart_scan(const nfc_context * context, nfc_connstring connstrings[] while ((acPort = acPorts[iDevice++])) { nfc_connstring connstring; - snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, RC522_UART_DRIVER_NAME, acPort, RC522_UART_DEFAULT_SPEED); + snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, DRIVER_NAME, acPort, DEFAULT_BAUD_RATE); nfc_device * pnd; - int ret = rc522_uart_create(context, connstring, acPort, RC522_UART_DEFAULT_SPEED, &pnd); + int ret = rc522_uart_create(context, connstring, acPort, DEFAULT_BAUD_RATE, &pnd); if (ret == NFC_ESOFT) { uart_list_free(acPorts); return 0; @@ -203,10 +205,10 @@ struct nfc_device * rc522_uart_open(const nfc_context * context, const nfc_conns char * endptr; struct nfc_device * pnd = NULL; - int decodelvl = connstring_decode(connstring, RC522_UART_DRIVER_NAME, NULL, &port_str, &baud_str); + int decodelvl = connstring_decode(connstring, DRIVER_NAME, NULL, &port_str, &baud_str); switch (decodelvl) { case 2: // Got port but no speed - baudrate = RC522_UART_DEFAULT_SPEED; + baudrate = DEFAULT_BAUD_RATE; break; case 3: // Got port and baud rate @@ -246,11 +248,11 @@ int rc522_uart_read(struct nfc_device * pnd, uint8_t reg, uint8_t * data, size_t int ret; while (size > 0) { - if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, IO_TIMEOUT)) < 0) { goto error; } - if ((ret = uart_receive(DRIVER_DATA(pnd)->port, data, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_receive(DRIVER_DATA(pnd)->port, data, 1, NULL, IO_TIMEOUT)) < 0) { goto error; } @@ -271,13 +273,13 @@ int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, while (size > 0) { // First: send write request - if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, &cmd, 1, IO_TIMEOUT)) < 0) { goto error; } // Second: wait for a reply uint8_t reply; - if ((ret = uart_receive(DRIVER_DATA(pnd)->port, &reply, 1, NULL, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_receive(DRIVER_DATA(pnd)->port, &reply, 1, NULL, IO_TIMEOUT)) < 0) { return ret; } @@ -289,7 +291,7 @@ int rc522_uart_write(struct nfc_device * pnd, uint8_t reg, const uint8_t * data, } // Fourth: send register data - if ((ret = uart_send(DRIVER_DATA(pnd)->port, data, 1, RC522_UART_IO_TIMEOUT)) < 0) { + if ((ret = uart_send(DRIVER_DATA(pnd)->port, data, 1, IO_TIMEOUT)) < 0) { goto error; } @@ -305,20 +307,22 @@ error: } int rc522_uart_reset_baud_rate(struct nfc_device * pnd) { - log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Restoring baud rate to default of %d bps.", RC522_UART_BOOT_SPEED); - return uart_set_speed(DRIVER_DATA(pnd)->port, RC522_UART_BOOT_SPEED); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Restoring baud rate to default of %d bps.", BOOT_BAUD_RATE); + return uart_set_speed(DRIVER_DATA(pnd)->port, BOOT_BAUD_RATE); } int rc522_uart_upgrade_baud_rate(struct nfc_device * pnd) { + int ret; uint32_t userBaudRate = DRIVER_DATA(pnd)->baudrate; - if (userBaudRate == RC522_UART_BOOT_SPEED) { + if (userBaudRate == BOOT_BAUD_RATE) { return NFC_SUCCESS; } log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Upgrading baud rate to user-specified %d bps.", userBaudRate); - return - uart_set_speed(DRIVER_DATA(pnd)->port, userBaudRate) || - rc522_send_baudrate(pnd, userBaudRate); + CHK(uart_set_speed(DRIVER_DATA(pnd)->port, userBaudRate)); + CHK(rc522_send_baudrate(pnd, userBaudRate)); + + return NFC_SUCCESS; } const struct rc522_io rc522_uart_io = { @@ -329,7 +333,7 @@ const struct rc522_io rc522_uart_io = { }; const struct nfc_driver rc522_uart_driver = { - .name = RC522_UART_DRIVER_NAME, + .name = DRIVER_NAME, .scan_type = INTRUSIVE, .scan = rc522_uart_scan, .open = rc522_uart_open, From d0c781c022dff010a483a377786fafc5dfd2f92a Mon Sep 17 00:00:00 2001 From: Marcos Vives Del Sol Date: Sat, 20 Jun 2015 17:27:15 +0200 Subject: [PATCH 15/15] Use also TRANSMIT and RECEIVE commands in MFRC522 --- examples/nfc-anticol.c | 22 +++++++++++----------- libnfc/chips/rc522.c | 27 ++++++++++++++++++--------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/examples/nfc-anticol.c b/examples/nfc-anticol.c index 4630f74..b217239 100644 --- a/examples/nfc-anticol.c +++ b/examples/nfc-anticol.c @@ -113,7 +113,7 @@ transmit_bits(const uint8_t *pbtTx, const size_t szTxBits) static bool -transmit_bytes(const uint8_t *pbtTx, const size_t szTx) +transmit_bytes(const uint8_t *pbtTx, const size_t szTx, bool wantsReply) { uint32_t cycles = 0; // Show transmitted command @@ -124,13 +124,13 @@ transmit_bytes(const uint8_t *pbtTx, const size_t szTx) int res; // Transmit the command bytes if (timed) { - if ((res = nfc_initiator_transceive_bytes_timed(pnd, pbtTx, szTx, abtRx, sizeof(abtRx), &cycles)) < 0) + if ((res = nfc_initiator_transceive_bytes_timed(pnd, pbtTx, szTx, abtRx, wantsReply ? sizeof(abtRx) : 0, &cycles)) < 0) return false; if ((!quiet_output) && (res > 0)) { printf("Response after %u cycles\n", cycles); } } else { - if ((res = nfc_initiator_transceive_bytes(pnd, pbtTx, szTx, abtRx, sizeof(abtRx), -1)) < 0) + if ((res = nfc_initiator_transceive_bytes(pnd, pbtTx, szTx, abtRx, wantsReply ? sizeof(abtRx) : 0, -1)) < 0) return false; } szRx = res; @@ -235,7 +235,7 @@ main(int argc, char *argv[]) memcpy(abtAtqa, abtRx, 2); // Anti-collision - transmit_bytes(abtSelectAll, 2); + transmit_bytes(abtSelectAll, 2, true); // Check answer if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { @@ -248,7 +248,7 @@ main(int argc, char *argv[]) //Prepare and send CL1 Select-Command memcpy(abtSelectTag + 2, abtRx, 5); iso14443a_crc_append(abtSelectTag, 7); - transmit_bytes(abtSelectTag, 9); + transmit_bytes(abtSelectTag, 9, true); abtSak = abtRx[0]; // Test if we are dealing with a CL2 @@ -267,7 +267,7 @@ main(int argc, char *argv[]) abtSelectAll[0] = 0x95; // Anti-collision - transmit_bytes(abtSelectAll, 2); + transmit_bytes(abtSelectAll, 2, true); // Check answer if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { @@ -281,7 +281,7 @@ main(int argc, char *argv[]) abtSelectTag[0] = 0x95; memcpy(abtSelectTag + 2, abtRx, 5); iso14443a_crc_append(abtSelectTag, 7); - transmit_bytes(abtSelectTag, 9); + transmit_bytes(abtSelectTag, 9, true); abtSak = abtRx[0]; // Test if we are dealing with a CL3 @@ -298,7 +298,7 @@ main(int argc, char *argv[]) // Prepare and send CL3 AC-Command abtSelectAll[0] = 0x97; - transmit_bytes(abtSelectAll, 2); + transmit_bytes(abtSelectAll, 2, true); // Check answer if ((abtRx[0] ^ abtRx[1] ^ abtRx[2] ^ abtRx[3] ^ abtRx[4]) != 0) { @@ -312,7 +312,7 @@ main(int argc, char *argv[]) abtSelectTag[0] = 0x97; memcpy(abtSelectTag + 2, abtRx, 5); iso14443a_crc_append(abtSelectTag, 7); - transmit_bytes(abtSelectTag, 9); + transmit_bytes(abtSelectTag, 9, true); abtSak = abtRx[0]; } } @@ -323,7 +323,7 @@ main(int argc, char *argv[]) } if ((abtRx[0] & SAK_FLAG_ATS_SUPPORTED) || force_rats) { iso14443a_crc_append(abtRats, 2); - if (transmit_bytes(abtRats, 4)) { + if (transmit_bytes(abtRats, 4, true)) { memcpy(abtAts, abtRx, szRx); szAts = szRx; } @@ -331,7 +331,7 @@ main(int argc, char *argv[]) // Done, halt the tag now iso14443a_crc_append(abtHalt, 2); - transmit_bytes(abtHalt, 4); + transmit_bytes(abtHalt, 4, false); printf("\nFound tag with\n UID: "); switch (szCL) { diff --git a/libnfc/chips/rc522.c b/libnfc/chips/rc522.c index ce313bc..e9154b9 100644 --- a/libnfc/chips/rc522.c +++ b/libnfc/chips/rc522.c @@ -447,20 +447,29 @@ int rc522_rf_rx(struct nfc_device * pnd, uint8_t * rxData, const size_t rxMaxByt int rc522_transceive(struct nfc_device * pnd, const uint8_t * txData, const size_t txBits, uint8_t * rxData, const size_t rxMaxBytes, int timeout) { int ret; - timeout_t to; - rc522_timeout_init(pnd, &to, timeout); + + bool doTX = txData != NULL && txBits > 0; + bool doRX = rxData != NULL && rxMaxBytes > 0; + bool isTransceive = doTX && doRX; CHK(rc522_abort(pnd)); - ret = rc522_rf_tx(pnd, txData, txBits, &to, true); - if (ret < 0) { - rc522_abort(pnd); - return ret; + timeout_t to; + rc522_timeout_init(pnd, &to, timeout); + + if (doTX) { + ret = rc522_rf_tx(pnd, txData, txBits, &to, isTransceive); + if (ret < 0) { + rc522_abort(pnd); + return ret; + } } - ret = rc522_rf_rx(pnd, rxData, rxMaxBytes, &to, true); - if (ret < 0) { - rc522_abort(pnd); + if (doRX) { + ret = rc522_rf_rx(pnd, rxData, rxMaxBytes, &to, isTransceive); + if (ret < 0) { + rc522_abort(pnd); + } } return ret;