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] 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" ])