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/cmake/modules/LibnfcDrivers.cmake b/cmake/modules/LibnfcDrivers.cmake
index a79c229..69a7042 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}/libnfc/drivers)
diff --git a/examples/nfc-anticol.c b/examples/nfc-anticol.c
index 3e9d533..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), 0)) < 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/Makefile.am b/libnfc/Makefile.am
index 8ab4191..b1bf245 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/buses/uart.c b/libnfc/buses/uart.c
index ffe64aa..4378d58 100644
--- a/libnfc/buses/uart.c
+++ b/libnfc/buses/uart.c
@@ -143,10 +143,11 @@ uart_open(const char *pcPortName)
uart_close_ext(sp, true);
return INVALID_SERIAL_PORT;
}
+
return sp;
}
-void
+int
uart_flush_input(serial_port sp, bool wait)
{
// flush commands may seem to be without effect
@@ -158,34 +159,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);
@@ -226,15 +230,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
@@ -431,3 +437,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..712ef49 100644
--- a/libnfc/buses/uart.h
+++ b/libnfc/buses/uart.h
@@ -49,14 +49,15 @@ 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);
char **uart_list_ports(void);
+void uart_list_free(char **acPorts);
#endif // __NFC_BUS_UART_H__
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/pn53x.c b/libnfc/chips/pn53x.c
index 34cf4ad..a85211c 100644
--- a/libnfc/chips/pn53x.c
+++ b/libnfc/chips/pn53x.c
@@ -335,8 +335,6 @@ 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;
}
@@ -1063,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) {
@@ -1150,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)
@@ -1174,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) {
@@ -1208,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
@@ -1226,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:
@@ -1258,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 {
@@ -1400,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
@@ -1418,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 +1529,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 +1624,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 +2032,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 +2082,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 +2101,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 +2129,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 +2229,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 +2291,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 +2369,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 +2391,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 +2473,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 +2622,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 +2636,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 +2687,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 +2701,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 +2778,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 +2876,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 +3001,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/chips/rc522-internal.h b/libnfc/chips/rc522-internal.h
new file mode 100644
index 0000000..f7983a4
--- /dev/null
+++ b/libnfc/chips/rc522-internal.h
@@ -0,0 +1,145 @@
+
+
+#ifndef __NFC_CHIPS_RC522_INTERNAL_H__
+#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,
+ FM17522 = 0x88,
+ MFRC522_V1 = 0x91,
+ MFRC522_V2 = 0x92
+} rc522_type;
+
+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 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
+
+#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)
+#define REG_DivIrqReg_CRCIRq (1 << 2)
+
+#define REG_ErrorReg 0x06
+
+#define REG_Status1Reg 0x07
+
+#define REG_Status2Reg 0x08
+#define REG_Status2Reg_MFCrypto1On (1 << 3)
+
+#define REG_FIFODataReg 0x09
+
+#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
+
+#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
+
+#define REG_ModeReg 0x11
+
+#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 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 REG_TxControlReg 0x14
+#define REG_TxControlReg_Tx2RFEn (1 << 1)
+#define REG_TxControlReg_Tx1RFEn (1 << 0)
+
+#define REG_TxASKReg 0x15
+
+#define REG_TxSelReg 0x16
+
+#define REG_RxSelReg 0x17
+
+#define REG_RxThresholdReg 0x18
+
+#define REG_DemodReg 0x19
+
+#define REG_MfTxReg 0x1C
+
+#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
new file mode 100644
index 0000000..e9154b9
--- /dev/null
+++ b/libnfc/chips/rc522.c
@@ -0,0 +1,765 @@
+/*-
+ * 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"
+#include "timing.h"
+
+// 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 };
+
+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;
+ uint8_t version;
+ int default_timeout;
+};
+
+
+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;
+ CHIP_DATA(pnd)->version = RC522_UNKNOWN;
+ CHIP_DATA(pnd)->default_timeout = 500;
+ 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 (err: %d)", reg, ret);
+ 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;
+ CHK(rc522_read_bulk(pnd, reg, &val, 1));
+ return val;
+}
+
+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) {
+ return oldval;
+ }
+
+ val = (val & mask) | (oldval & ~mask);
+ }
+
+ return rc522_write_reg(pnd, reg, val);
+}
+
+int rc522_start_command(struct nfc_device * pnd, rc522_cmd cmd) {
+ bool needsRX;
+
+ // 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:
+ needsRX = false;
+ 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);
+ return NFC_ESOFT;
+ }
+
+ uint8_t regval = cmd;
+ if (!needsRX) {
+ regval |= REG_CommandReg_RcvOff;
+ }
+
+ return rc522_write_reg(pnd, REG_CommandReg, regval);
+}
+
+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);
+
+ 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) {
+ return NFC_SUCCESS;
+ }
+ }
+
+ 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);
+}
+
+int rc522_soft_reset(struct nfc_device * pnd) {
+ int ret;
+
+ // 1. Execute reset command
+ 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) {
+ 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
+ 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) {
+ CHK(CHIP_DATA(pnd)->io->upgrade_baud_rate(pnd));
+ }
+
+ return NFC_SUCCESS;
+}
+
+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:
+ 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;
+ }
+
+ 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) {
+ int ret;
+
+ if (nm.nmt != NMT_ISO14443A) {
+ return NFC_EINVARG;
+ }
+
+ CHK(rc522_set_rf_baud_rate(pnd, nm.nbr));
+
+ // TODO
+ return NFC_ENOTIMPL;
+}
+
+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_rf_tx: sending %d bits (%d bytes).", txBits, txBytes);
+
+ 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));
+ }
+
+ while (1) {
+ if (!timeout_check(timeout)) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "rc522_rf_tx: 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_tx: RC522 set ErrIRq flag.");
+ // If the RC522 detects an error abort the transmission and notify the caller
+ 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_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;
+ }
+
+ 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);
+ CHK(rc522_write_bulk(pnd, REG_FIFODataReg, txData + transmitted, chunkSize));
+ transmitted += chunkSize;
+
+ // TODO: Should we clear the flag before or after feeding the data?
+ CHK(rc522_write_reg(pnd, REG_ComIrqReg, REG_ComIrqReg_LoAlertIRq));
+
+ 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_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;
+
+ bool doTX = txData != NULL && txBits > 0;
+ bool doRX = rxData != NULL && rxMaxBytes > 0;
+ bool isTransceive = doTX && doRX;
+
+ CHK(rc522_abort(pnd));
+
+ 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;
+ }
+ }
+
+ if (doRX) {
+ ret = rc522_rf_rx(pnd, rxData, rxMaxBytes, &to, isTransceive);
+ if (ret < 0) {
+ 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
+ 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) {
+ 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) {
+ (void) pnd;
+
+ 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) {
+ (void) pnd;
+
+ 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;
+ }
+
+ 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;
+
+ case NP_HANDLE_PARITY:
+ if (pnd->bPar == enable) {
+ return NFC_SUCCESS;
+ }
+
+ // Note it's parity DISABLE (ie active low)
+ CHK(rc522_write_reg_mask(pnd, REG_MfRxReg, enable ? 0 : ~0, REG_MfRxReg_ParityDisable));
+
+ pnd->bPar = enable;
+ return NFC_SUCCESS;
+
+ case NP_EASY_FRAMING:
+ pnd->bEasyFraming = enable;
+ return NFC_SUCCESS;
+
+ case NP_ACTIVATE_FIELD:
+ 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_mask(pnd, REG_Status2Reg, enable ? ~0 : 0, 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_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:
+ break;
+ }
+
+ return NFC_EINVARG;
+}
+
+int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value) {
+ 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) {
+ 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) {
+ return rc522_write_reg(pnd, REG_CommandReg, REG_CommandReg_RcvOff | REG_CommandReg_PowerDown | CMD_NOCMDCHANGE);
+}
+
+// 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
+};
+
+int rc522_self_test(struct nfc_device * pnd) {
+ const uint8_t * correct;
+ switch (CHIP_DATA(pnd)->version) {
+ case MFRC522_V1:
+ correct = MFRC522_V1_SELFTEST;
+ break;
+
+ case MFRC522_V2:
+ correct = MFRC522_V2_SELFTEST;
+ 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;
+ }
+
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Executing self test");
+
+ uint8_t zeroes[25];
+ memset(zeroes, 0x00, sizeof(zeroes));
+
+ int ret;
+ // MFRC522 datasheet section 16.1.1
+ // 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
+ timeout_t to;
+ timeout_init(&to, 50);
+
+ while (1) {
+ if (!timeout_check(&to)) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Self test timeout");
+ return NFC_ETIMEOUT;
+ }
+
+ CHK(rc522_read_reg(pnd, REG_DivIrqReg));
+
+ // If the RC522 has finished calculating the CRC proceed
+ if (ret & REG_DivIrqReg_CRCIRq) {
+ break;
+ }
+ }
+
+ uint8_t response[FIFO_SIZE];
+ // 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");
+ 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 ret;
+
+ int version = CHK(rc522_read_reg(pnd, REG_VersionReg));
+ CHIP_DATA(pnd)->version = version;
+
+ 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
new file mode 100644
index 0000000..af0c3dc
--- /dev/null
+++ b/libnfc/chips/rc522.h
@@ -0,0 +1,47 @@
+/*-
+ * 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 (*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_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);
+int rc522_set_property_int(struct nfc_device * pnd, const nfc_property property, const int value);
+int rc522_abort(struct nfc_device * pnd);
+int rc522_powerdown(struct nfc_device * pnd);
+
+#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/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/drivers/rc522_uart.c b/libnfc/drivers/rc522_uart.c
new file mode 100644
index 0000000..d5fb5d4
--- /dev/null
+++ b/libnfc/drivers/rc522_uart.c
@@ -0,0 +1,373 @@
+/*-
+ * 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
+
+#include
+
+#include "drivers.h"
+#include "nfc-internal.h"
+#include "chips/rc522.h"
+#include "uart.h"
+
+#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 {
+ serial_port port;
+ uint32_t baudrate;
+};
+
+/*
+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), IO_TIMEOUT);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return rc522_wait_wakeup(pnd);
+}
+*/
+
+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);
+}
+
+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;
+
+ 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;
+ DRIVER_DATA(pnd)->baudrate = userBaudRate;
+
+ // 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, 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.", BOOT_BAUD_RATE, userBaudRate);
+ rc522_uart_close(pnd);
+ return NFC_EIO;
+ }
+
+ *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;
+ char ** acPorts = uart_list_ports();
+ const char * acPort;
+ size_t iDevice = 0;
+
+ while ((acPort = acPorts[iDevice++])) {
+ nfc_connstring connstring;
+ 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, DEFAULT_BAUD_RATE, &pnd);
+ if (ret == NFC_ESOFT) {
+ uart_list_free(acPorts);
+ return 0;
+ }
+ if (ret != NFC_SUCCESS) {
+ continue;
+ }
+ rc522_uart_close(pnd);
+
+ 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 = NULL;
+ char * baud_str = NULL;
+ uint32_t baudrate;
+ char * endptr;
+ struct nfc_device * pnd = NULL;
+
+ int decodelvl = connstring_decode(connstring, DRIVER_NAME, NULL, &port_str, &baud_str);
+ switch (decodelvl) {
+ case 2: // Got port but no speed
+ baudrate = DEFAULT_BAUD_RATE;
+ break;
+
+ case 3: // Got port and baud rate
+ // TODO: set baud rate AFTER initialization
+ baudrate = (uint32_t) strtol(baud_str, &endptr, 10);
+ if (*endptr != '\0') {
+ free(port_str);
+ free(baud_str);
+ return NULL;
+ }
+
+ free(baud_str);
+ break;
+
+ default: // Got unparseable gibberish
+ free(port_str);
+ free(baud_str);
+ return NULL;
+ }
+
+ rc522_uart_create(context, connstring, port_str, baudrate, &pnd);
+ free(port_str);
+ return pnd;
+}
+
+#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) {
+ uint8_t cmd = rc522_uart_pack(reg, READ);
+ int ret;
+
+ while (size > 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, IO_TIMEOUT)) < 0) {
+ goto error;
+ }
+
+ size--;
+ data++;
+ }
+
+ return NFC_SUCCESS;
+
+error:
+ uart_flush_input(DRIVER_DATA(pnd)->port, true);
+ 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
+ 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, IO_TIMEOUT)) < 0) {
+ return ret;
+ }
+
+ // Third: compare sent and received. They must match.
+ if (cmd != reply) {
+ 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
+ if ((ret = uart_send(DRIVER_DATA(pnd)->port, data, 1, IO_TIMEOUT)) < 0) {
+ goto error;
+ }
+
+ size--;
+ data++;
+ }
+
+ return NFC_SUCCESS;
+
+error:
+ uart_flush_input(DRIVER_DATA(pnd)->port, true);
+ return ret;
+}
+
+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.", 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 == 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);
+ 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 = {
+ .read = rc522_uart_read,
+ .write = rc522_uart_write,
+ .reset_baud_rate = rc522_uart_reset_baud_rate,
+ .upgrade_baud_rate = rc522_uart_upgrade_baud_rate,
+};
+
+const struct nfc_driver rc522_uart_driver = {
+ .name = 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_abort,
+// .idle = rc522_idle,
+ .powerdown = rc522_powerdown,
+};
+
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/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))
diff --git a/libnfc/nfc.c b/libnfc/nfc.c
index 70312b8..e7bca32 100644
--- a/libnfc/nfc.c
+++ b/libnfc/nfc.c
@@ -119,6 +119,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
@@ -157,6 +160,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..e937a39
--- /dev/null
+++ b/libnfc/timing.c
@@ -0,0 +1,86 @@
+/*-
+ * 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
+
+#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
+
+ 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;
+}
+
+void timeout_never(timeout_t * to) {
+ *to = MAGIC_NEVER;
+}
+
+bool timeout_check(timeout_t * to) {
+ switch (*to) {
+ case MAGIC_EXPIRED:
+ return false;
+ case MAGIC_NEVER:
+ return true;
+ }
+
+ ms_t now = time_millis();
+ if (now >= *to) {
+ // Mark as expired and fail in next check
+ *to = MAGIC_EXPIRED;
+ }
+
+ return true;
+}
diff --git a/libnfc/timing.h b/libnfc/timing.h
new file mode 100644
index 0000000..5ee7bab
--- /dev/null
+++ b/libnfc/timing.h
@@ -0,0 +1,54 @@
+/*-
+ * 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 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
+ * @return True if the timeout has NOT expired
+ */
+bool timeout_check(timeout_t * to);
+
+#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"
])