/*- * Free/Libre Near Field Communication (NFC) library * * Libnfc historical contributors: * Copyright (C) 2009 Roel Verdult * Copyright (C) 2009-2013 Romuald Conty * Copyright (C) 2010-2012 Romain Tartière * Copyright (C) 2010-2013 Philippe Teuwen * Copyright (C) 2012-2013 Ludovic Rousseau * See AUTHORS file for a more comprehensive list of contributors. * Additional contributors of this file: * Copyright (C) 2013 Evgeny Boger * * 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 pn532_spi.c * @brief PN532 driver using SPI bus */ #ifdef HAVE_CONFIG_H # include "config.h" #endif // HAVE_CONFIG_H #include "pn532_spi.h" #include #include #include #include #include "drivers.h" #include "nfc-internal.h" #include "chips/pn53x.h" #include "chips/pn53x-internal.h" #include "spi.h" #define PN532_SPI_DEFAULT_SPEED 1000000 // 1 MHz #define PN532_SPI_DRIVER_NAME "pn532_spi" #define PN532_SPI_MODE SPI_MODE_0 #define LOG_CATEGORY "libnfc.driver.pn532_spi" #define LOG_GROUP NFC_LOG_GROUP_DRIVER #ifndef _WIN32 // Needed by sleep() under Unix # include # include # define msleep(x) do { \ struct timespec xsleep; \ xsleep.tv_sec = x / 1000; \ xsleep.tv_nsec = (x - xsleep.tv_sec * 1000) * 1000 * 1000; \ nanosleep(&xsleep, NULL); \ } while (0) #else // Needed by Sleep() under Windows # include # define msleep Sleep #endif // Internal data structs const struct pn53x_io pn532_spi_io; struct pn532_spi_data { spi_port port; volatile bool abort_flag; }; static const uint8_t pn532_spi_cmd_dataread = 0x03; static const uint8_t pn532_spi_cmd_datawrite = 0x01; // Prototypes int pn532_spi_ack(nfc_device *pnd); int pn532_spi_wakeup(nfc_device *pnd); #define DRIVER_DATA(pnd) ((struct pn532_spi_data*)(pnd->driver_data)) static size_t pn532_spi_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) { size_t device_found = 0; spi_port sp; char **acPorts = spi_list_ports(); const char *acPort; int iDevice = 0; while ((acPort = acPorts[iDevice++])) { sp = spi_open(acPort); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find PN532 device on SPI port: %s at %d Hz.", acPort, PN532_SPI_DEFAULT_SPEED); if ((sp != INVALID_SPI_PORT) && (sp != CLAIMED_SPI_PORT)) { // Serial port claimed but we need to check if a PN532_SPI is opened. spi_set_speed(sp, PN532_SPI_DEFAULT_SPEED); spi_set_mode(sp, PN532_SPI_MODE); nfc_connstring connstring; snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, PN532_SPI_DRIVER_NAME, acPort, PN532_SPI_DEFAULT_SPEED); nfc_device *pnd = nfc_device_new(context, connstring); if (!pnd) { perror("malloc"); spi_close(sp); iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return 0; } pnd->driver = &pn532_spi_driver; pnd->driver_data = malloc(sizeof(struct pn532_spi_data)); if (!pnd->driver_data) { perror("malloc"); spi_close(sp); nfc_device_free(pnd); iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return 0; } DRIVER_DATA(pnd)->port = sp; // Alloc and init chip's data if (pn53x_data_new(pnd, &pn532_spi_io) == NULL) { perror("malloc"); spi_close(DRIVER_DATA(pnd)->port); nfc_device_free(pnd); iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return 0; } // SAMConfiguration command if needed to wakeup the chip and pn53x_SAMConfiguration check if the chip is a PN532 CHIP_DATA(pnd)->type = PN532; // This device starts in LowVBat power mode CHIP_DATA(pnd)->power_mode = LOWVBAT; DRIVER_DATA(pnd)->abort_flag = false; // Check communication using "Diagnose" command, with "Communication test" (0x00) int res = pn53x_check_communication(pnd); spi_close(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); if (res < 0) { continue; } memcpy(connstrings[device_found], connstring, sizeof(nfc_connstring)); device_found++; // Test if we reach the maximum "wanted" devices if (device_found >= connstrings_len) break; } } iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return device_found; } struct pn532_spi_descriptor { char *port; uint32_t speed; }; static void pn532_spi_close(nfc_device *pnd) { pn53x_idle(pnd); // Release SPI port spi_close(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); } static nfc_device * pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) { struct pn532_spi_descriptor ndd; char *speed_s; int connstring_decode_level = connstring_decode(connstring, PN532_SPI_DRIVER_NAME, NULL, &ndd.port, &speed_s); if (connstring_decode_level == 3) { ndd.speed = 0; if (sscanf(speed_s, "%10"PRIu32, &ndd.speed) != 1) { // speed_s is not a number free(ndd.port); free(speed_s); return NULL; } free(speed_s); } if (connstring_decode_level < 2) { return NULL; } if (connstring_decode_level < 3) { ndd.speed = PN532_SPI_DEFAULT_SPEED; } spi_port sp; nfc_device *pnd = NULL; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s at %d Hz.", ndd.port, ndd.speed); sp = spi_open(ndd.port); if (sp == INVALID_SPI_PORT) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid SPI port: %s", ndd.port); if (sp == CLAIMED_SPI_PORT) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "SPI port already claimed: %s", ndd.port); if ((sp == CLAIMED_SPI_PORT) || (sp == INVALID_SPI_PORT)) { free(ndd.port); return NULL; } spi_set_speed(sp, ndd.speed); spi_set_mode(sp, PN532_SPI_MODE); // We have a connection pnd = nfc_device_new(context, connstring); if (!pnd) { perror("malloc"); free(ndd.port); spi_close(sp); return NULL; } snprintf(pnd->name, sizeof(pnd->name), "%s:%s", PN532_SPI_DRIVER_NAME, ndd.port); free(ndd.port); pnd->driver_data = malloc(sizeof(struct pn532_spi_data)); if (!pnd->driver_data) { perror("malloc"); spi_close(sp); nfc_device_free(pnd); return NULL; } DRIVER_DATA(pnd)->port = sp; // Alloc and init chip's data if (pn53x_data_new(pnd, &pn532_spi_io) == NULL) { perror("malloc"); spi_close(DRIVER_DATA(pnd)->port); nfc_device_free(pnd); return NULL; } // SAMConfiguration command if needed to wakeup the chip and pn53x_SAMConfiguration check if the chip is a PN532 CHIP_DATA(pnd)->type = PN532; // This device starts in LowVBat mode CHIP_DATA(pnd)->power_mode = LOWVBAT; // empirical tuning CHIP_DATA(pnd)->timer_correction = 48; pnd->driver = &pn532_spi_driver; DRIVER_DATA(pnd)->abort_flag = false; // Check communication using "Diagnose" command, with "Communication test" (0x00) if (pn53x_check_communication(pnd) < 0) { nfc_perror(pnd, "pn53x_check_communication"); pn532_spi_close(pnd); return NULL; } pn53x_init(pnd); return pnd; } static int pn532_spi_read_spi_status(nfc_device *pnd) { static const uint8_t pn532_spi_statread_cmd = 0x02; uint8_t spi_status = 0; int res = spi_send_receive(DRIVER_DATA(pnd)->port, &pn532_spi_statread_cmd, 1, &spi_status, 1, true); if (res != NFC_SUCCESS) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Unable to read SPI status"); return res; } return spi_status; } int pn532_spi_wakeup(nfc_device *pnd) { /* SPI wakeup is basically activating chipselect for several ms. * To do so, we are sending harmless command at very low speed */ int res; const uint32_t prev_port_speed = spi_get_speed(DRIVER_DATA(pnd)->port); // Try to get byte from the SPI line. If PN532 is powered down, the byte will be 0xff (MISO line is high) uint8_t spi_byte = 0; res = spi_receive(DRIVER_DATA(pnd)->port, &spi_byte, 1, true); if (res != NFC_SUCCESS) { return res; } log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Got %x byte from SPI line before wakeup", spi_byte); CHIP_DATA(pnd)->power_mode = NORMAL; // PN532 will be awake soon msleep(1); if (spi_byte == 0xff) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Wakeup is needed"); spi_set_speed(DRIVER_DATA(pnd)->port, 5000); // set slow speed res = pn532_SAMConfiguration(pnd, PSM_NORMAL, 1000); // wakeup by sending SAMConfiguration, which works just fine spi_set_speed(DRIVER_DATA(pnd)->port, prev_port_speed); } return res; } #define PN532_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD) static int pn532_spi_wait_for_data(nfc_device *pnd, int timeout) { static const uint8_t pn532_spi_ready = 0x01; static const int pn532_spi_poll_interval = 10; //ms int timer = 0; int ret; while ((ret = pn532_spi_read_spi_status(pnd)) != pn532_spi_ready) { if (ret < 0) { return ret; } if (DRIVER_DATA(pnd)->abort_flag) { DRIVER_DATA(pnd)->abort_flag = false; return NFC_EOPABORTED; } if (timeout > 0) { timer += pn532_spi_poll_interval; if (timer > timeout) { return NFC_ETIMEOUT; } msleep(pn532_spi_poll_interval); } } return NFC_SUCCESS; } static int pn532_spi_receive_next_chunk(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen) { // According to datasheet, the entire read operation should be done at once // However, it seems impossible to do since the length of the frame is stored in the frame // itself and it's impossible to manually set CS to low between two read operations // It's possible to read the response frame in a series of read operations, provided // each read operation is preceded by SPI_DATAREAD byte from the host. // Unfortunately, the PN532 sends first byte of the second and successive response chunks // at the same time as host sends SPI_DATAREAD byte // Many hardware SPI implementations are half-duplex, so it's became impossible to read this // first response byte // The following hack is used here: we first try to receive data from PN532 without SPI_DATAREAD // and then begin full-featured read operation // The PN532 does not shift the internal register on the receive operation, which allows us to read the whole response // The example transfer log is as follows: // CS ..._/---\___________________________/---\________/------\_____________/-----\_________/---\____________/---... // MOSI (host=>pn532) ... 0x03 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x03 0x00 // MISO (pn532<=host) ... 0x01 0x00 0xff 0x02 0xfe 0xd5 0xd5 0x15 0x16 0x16 0x00 // linux send/receive s r r r r r s r r s r // |<-- data -->| |<-data->| |<-data->| |<-data->| |<-data->| // |<-- first chunk -->| |<-- second chunk -->| |<-- third chunk -->| // The response frame is 0x00 0xff 0x02 0xfe 0xd5 0x15 0x16 0x00 int res = spi_receive(DRIVER_DATA(pnd)->port, pbtData, 1, true); if (res != NFC_SUCCESS) { return res; } res = spi_send_receive(DRIVER_DATA(pnd)->port, &pn532_spi_cmd_dataread, 1, pbtData + 1, szDataLen - 1, true); return res; } static int pn532_spi_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout) { uint8_t abtRxBuf[5]; size_t len; pnd->last_error = pn532_spi_wait_for_data(pnd, timeout); if (NFC_EOPABORTED == pnd->last_error) { return pn532_spi_ack(pnd); } if (pnd->last_error != NFC_SUCCESS) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to wait for SPI data. (RX)"); goto error; } pnd->last_error = spi_send_receive(DRIVER_DATA(pnd)->port, &pn532_spi_cmd_dataread, 1, abtRxBuf, 4, true); if (pnd->last_error < 0) { goto error; } const uint8_t pn53x_long_preamble[3] = { 0x00, 0x00, 0xff }; if (0 == (memcmp(abtRxBuf, pn53x_long_preamble, 3))) { // long preamble // omit first byte for (size_t i = 0; i < 3; ++i) { abtRxBuf[i] = abtRxBuf[i + 1]; } // need one more byte pnd->last_error = pn532_spi_receive_next_chunk(pnd, abtRxBuf + 3, 1); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive one more byte for long preamble frame. (RX)"); goto error; } } const uint8_t pn53x_preamble[2] = { 0x00, 0xff }; if (0 != (memcmp(abtRxBuf, pn53x_preamble, 2))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", " preamble+start code mismatch"); pnd->last_error = NFC_EIO; goto error; } if ((0x01 == abtRxBuf[2]) && (0xff == abtRxBuf[3])) { // Error frame pn532_spi_receive_next_chunk(pnd, abtRxBuf, 3); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Application level error detected"); pnd->last_error = NFC_EIO; goto error; } else if ((0xff == abtRxBuf[2]) && (0xff == abtRxBuf[3])) { // Extended frame pnd->last_error = pn532_spi_receive_next_chunk(pnd, abtRxBuf, 3); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } // (abtRxBuf[0] << 8) + abtRxBuf[1] (LEN) include TFI + (CC+1) 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; goto error; } } else { // Normal frame if (256 != (abtRxBuf[2] + abtRxBuf[3])) { // TODO: Retry log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch"); pnd->last_error = NFC_EIO; goto error; } // abtRxBuf[3] (LEN) include TFI + (CC+1) len = abtRxBuf[2] - 2; } if (len > szDataLen) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to receive data: buffer too small. (szDataLen: %zu, len: %zu)", szDataLen, len); pnd->last_error = NFC_EIO; goto error; } // TFI + PD0 (CC+1) pnd->last_error = pn532_spi_receive_next_chunk(pnd, abtRxBuf, 2); if (pnd->last_error != 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; 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; goto error; } if (len) { pnd->last_error = pn532_spi_receive_next_chunk(pnd, pbtData, len); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } } pnd->last_error = pn532_spi_receive_next_chunk(pnd, abtRxBuf, 2); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); goto error; } uint8_t btDCS = (256 - 0xD5); btDCS -= CHIP_DATA(pnd)->last_command + 1; for (size_t szPos = 0; szPos < len; szPos++) { btDCS -= pbtData[szPos]; } if (btDCS != abtRxBuf[0]) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Data checksum mismatch"); pnd->last_error = 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; goto error; } // The PN53x command is done and we successfully received the reply return len; error: return pnd->last_error; } static int pn532_spi_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout) { int res = 0; switch (CHIP_DATA(pnd)->power_mode) { case LOWVBAT: { /** PN532C106 wakeup. */ if ((res = pn532_spi_wakeup(pnd)) < 0) { return res; } // According to PN532 application note, C106 appendix: to go out Low Vbat mode and enter in normal mode we need to send a SAMConfiguration command if ((res = pn532_SAMConfiguration(pnd, PSM_NORMAL, 1000)) < 0) { return res; } } break; case POWERDOWN: { if ((res = pn532_spi_wakeup(pnd)) < 0) { return res; } } break; case NORMAL: // Nothing to do :) break; }; uint8_t abtFrame[PN532_BUFFER_LEN + 1] = { pn532_spi_cmd_datawrite, 0x00, 0x00, 0xff }; // SPI data transfer starts with DATAWRITE (0x01) byte, Every packet must start with "00 00 ff" size_t szFrame = 0; if ((res = pn53x_build_frame(abtFrame + 1, &szFrame, pbtData, szData)) < 0) { pnd->last_error = res; return pnd->last_error; } res = spi_send(DRIVER_DATA(pnd)->port, abtFrame, szFrame, true); 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; } res = pn532_spi_wait_for_data(pnd, timeout); if (res != NFC_SUCCESS) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to wait for SPI data. (RX)"); pnd->last_error = res; return pnd->last_error; } uint8_t abtRxBuf[PN53x_ACK_FRAME__LEN]; res = spi_send_receive(DRIVER_DATA(pnd)->port, &pn532_spi_cmd_dataread, 1, abtRxBuf, sizeof(abtRxBuf), true); 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; } if (pn53x_check_ack_frame(pnd, abtRxBuf, sizeof(abtRxBuf)) == 0) { // The PN53x is running the sent command } else { return pnd->last_error; } return NFC_SUCCESS; } int pn532_spi_ack(nfc_device *pnd) { const size_t ack_frame_len = (sizeof(pn53x_ack_frame) / sizeof(pn53x_ack_frame[0])); uint8_t ack_tx_buf [1 + ack_frame_len]; ack_tx_buf[0] = pn532_spi_cmd_datawrite; memcpy(ack_tx_buf + 1, pn53x_ack_frame, ack_frame_len); int res = spi_send(DRIVER_DATA(pnd)->port, ack_tx_buf, ack_frame_len + 1, true); return res; } static int pn532_spi_abort_command(nfc_device *pnd) { if (pnd) { DRIVER_DATA(pnd)->abort_flag = true; } return NFC_SUCCESS; } const struct pn53x_io pn532_spi_io = { .send = pn532_spi_send, .receive = pn532_spi_receive, }; const struct nfc_driver pn532_spi_driver = { .name = PN532_SPI_DRIVER_NAME, .scan_type = INTRUSIVE, .scan = pn532_spi_scan, .open = pn532_spi_open, .close = pn532_spi_close, .strerror = pn53x_strerror, .initiator_init = pn53x_initiator_init, .initiator_init_secure_element = pn532_initiator_init_secure_element, .initiator_select_passive_target = pn53x_initiator_select_passive_target, .initiator_poll_target = pn53x_initiator_poll_target, .initiator_select_dep_target = pn53x_initiator_select_dep_target, .initiator_deselect_target = pn53x_initiator_deselect_target, .initiator_transceive_bytes = pn53x_initiator_transceive_bytes, .initiator_transceive_bits = pn53x_initiator_transceive_bits, .initiator_transceive_bytes_timed = pn53x_initiator_transceive_bytes_timed, .initiator_transceive_bits_timed = pn53x_initiator_transceive_bits_timed, .initiator_target_is_present = pn53x_initiator_target_is_present, .target_init = pn53x_target_init, .target_send_bytes = pn53x_target_send_bytes, .target_receive_bytes = pn53x_target_receive_bytes, .target_send_bits = pn53x_target_send_bits, .target_receive_bits = pn53x_target_receive_bits, .device_set_property_bool = pn53x_set_property_bool, .device_set_property_int = pn53x_set_property_int, .get_supported_modulation = pn53x_get_supported_modulation, .get_supported_baud_rate = pn53x_get_supported_baud_rate, .device_get_information_about = pn53x_get_information_about, .abort_command = pn532_spi_abort_command, .idle = pn53x_idle, .powerdown = pn53x_PowerDown, };