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