/*- * 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: * * 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 arygon.c * @brief ARYGON readers driver * * This driver can handle ARYGON readers that use UART as bus. * UART connection can be direct (host<->arygon_uc) or could be provided by internal USB to serial interface (e.g. host<->ftdi_chip<->arygon_uc) */ #ifdef HAVE_CONFIG_H # include "config.h" #endif // HAVE_CONFIG_H #include "arygon.h" #include #include #include #include #include #include #include "drivers.h" #include "nfc-internal.h" #include "chips/pn53x.h" #include "chips/pn53x-internal.h" #include "uart.h" /** @def DEV_ARYGON_PROTOCOL_ARYGON_ASCII * @brief High level language in ASCII format. (Common µC commands and Mifare® commands) */ #define DEV_ARYGON_PROTOCOL_ARYGON_ASCII '0' /** @def DEV_ARYGON_MODE_HL_ASCII * @brief High level language in Binary format With AddressingByte for party line. (Common µC commands and Mifare® commands) */ #define DEV_ARYGON_PROTOCOL_ARYGON_BINARY_WAB '1' /** @def DEV_ARYGON_PROTOCOL_TAMA * @brief Philips protocol (TAMA language) in binary format. */ #define DEV_ARYGON_PROTOCOL_TAMA '2' /** @def DEV_ARYGON_PROTOCOL_TAMA_WAB * @brief Philips protocol (TAMA language) in binary With AddressingByte for party line. */ #define DEV_ARYGON_PROTOCOL_TAMA_WAB '3' #define ARYGON_DEFAULT_SPEED 9600 #define ARYGON_DRIVER_NAME "arygon" #define LOG_CATEGORY "libnfc.driver.arygon" #define LOG_GROUP NFC_LOG_GROUP_DRIVER #define DRIVER_DATA(pnd) ((struct arygon_data*)(pnd->driver_data)) // Internal data structs const struct pn53x_io arygon_tama_io; struct arygon_data { serial_port port; #ifndef WIN32 int iAbortFds[2]; #else volatile bool abort_flag; #endif }; // ARYGON frames static const uint8_t arygon_error_none[] = "FF000000\x0d\x0a"; static const uint8_t arygon_error_unknown_mode[] = "FF060000\x0d\x0a"; // Prototypes int arygon_reset_tama(nfc_device *pnd); void arygon_firmware(nfc_device *pnd, char *str); static size_t arygon_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len) { size_t device_found = 0; serial_port sp; char **acPorts = uart_list_ports(); const char *acPort; int iDevice = 0; while ((acPort = acPorts[iDevice++])) { sp = uart_open(acPort); log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find ARYGON device on serial port: %s at %d baud.", acPort, ARYGON_DEFAULT_SPEED); if ((sp != INVALID_SERIAL_PORT) && (sp != CLAIMED_SERIAL_PORT)) { // We need to flush input to be sure first reply does not comes from older byte transceive uart_flush_input(sp, true); uart_set_speed(sp, ARYGON_DEFAULT_SPEED); nfc_connstring connstring; snprintf(connstring, sizeof(nfc_connstring), "%s:%s:%"PRIu32, ARYGON_DRIVER_NAME, acPort, ARYGON_DEFAULT_SPEED); nfc_device *pnd = nfc_device_new(context, connstring); if (!pnd) { perror("malloc"); uart_close(sp); iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return 0; } pnd->driver = &arygon_driver; pnd->driver_data = malloc(sizeof(struct arygon_data)); if (!pnd->driver_data) { perror("malloc"); uart_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, &arygon_tama_io) == NULL) { perror("malloc"); uart_close(DRIVER_DATA(pnd)->port); nfc_device_free(pnd); iDevice = 0; while ((acPort = acPorts[iDevice++])) { free((void *)acPort); } free(acPorts); return 0; } #ifndef WIN32 // pipe-based abort mechanism if (pipe(DRIVER_DATA(pnd)->iAbortFds) < 0) { 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); return 0; } #else DRIVER_DATA(pnd)->abort_flag = false; #endif int res = arygon_reset_tama(pnd); uart_close(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); if (res < 0) { continue; } // ARYGON reader is found 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 arygon_descriptor { char *port; uint32_t speed; }; static void arygon_close_step2(nfc_device *pnd) { // Release UART port uart_close(DRIVER_DATA(pnd)->port); #ifndef WIN32 // Release file descriptors used for abort mechanism close(DRIVER_DATA(pnd)->iAbortFds[0]); close(DRIVER_DATA(pnd)->iAbortFds[1]); #endif pn53x_data_free(pnd); nfc_device_free(pnd); } static void arygon_close(nfc_device *pnd) { pn53x_idle(pnd); arygon_close_step2(pnd); } static nfc_device * arygon_open(const nfc_context *context, const nfc_connstring connstring) { struct arygon_descriptor ndd; char *speed_s; int connstring_decode_level = connstring_decode(connstring, ARYGON_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 = ARYGON_DEFAULT_SPEED; } serial_port sp; nfc_device *pnd = NULL; log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open: %s at %d baud.", ndd.port, ndd.speed); sp = uart_open(ndd.port); if (sp == INVALID_SERIAL_PORT) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Invalid serial port: %s", ndd.port); if (sp == CLAIMED_SERIAL_PORT) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Serial port already claimed: %s", ndd.port); if ((sp == CLAIMED_SERIAL_PORT) || (sp == INVALID_SERIAL_PORT)) { free(ndd.port); return NULL; } // We need to flush input to be sure first reply does not comes from older byte transceive uart_flush_input(sp, true); uart_set_speed(sp, ndd.speed); // We have a connection pnd = nfc_device_new(context, connstring); if (!pnd) { perror("malloc"); free(ndd.port); uart_close(sp); return NULL; } snprintf(pnd->name, sizeof(pnd->name), "%s:%s", ARYGON_DRIVER_NAME, ndd.port); free(ndd.port); pnd->driver_data = malloc(sizeof(struct arygon_data)); if (!pnd->driver_data) { perror("malloc"); uart_close(sp); nfc_device_free(pnd); return NULL; } DRIVER_DATA(pnd)->port = sp; // Alloc and init chip's data if (pn53x_data_new(pnd, &arygon_tama_io) == NULL) { perror("malloc"); uart_close(DRIVER_DATA(pnd)->port); nfc_device_free(pnd); return NULL; } // The PN53x chip opened to ARYGON MCU doesn't seems to be in LowVBat mode CHIP_DATA(pnd)->power_mode = NORMAL; // empirical tuning CHIP_DATA(pnd)->timer_correction = 46; pnd->driver = &arygon_driver; #ifndef WIN32 // pipe-based abort mechanism if (pipe(DRIVER_DATA(pnd)->iAbortFds) < 0) { uart_close(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); return NULL; } #else DRIVER_DATA(pnd)->abort_flag = false; #endif // Check communication using "Reset TAMA" command if (arygon_reset_tama(pnd) < 0) { arygon_close_step2(pnd); return NULL; } char arygon_firmware_version[10]; arygon_firmware(pnd, arygon_firmware_version); char *pcName; pcName = strdup(pnd->name); snprintf(pnd->name, sizeof(pnd->name), "%s %s", pcName, arygon_firmware_version); free(pcName); pn53x_init(pnd); return pnd; } #define ARYGON_TX_BUFFER_LEN (PN53x_NORMAL_FRAME__DATA_MAX_LEN + PN53x_NORMAL_FRAME__OVERHEAD + 1) #define ARYGON_RX_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD) static int arygon_tama_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout) { int res = 0; // Before sending anything, we need to discard from any junk bytes uart_flush_input(DRIVER_DATA(pnd)->port, false); uint8_t abtFrame[ARYGON_TX_BUFFER_LEN] = { DEV_ARYGON_PROTOCOL_TAMA, 0x00, 0x00, 0xff }; // Every packet must start with "0x32 0x00 0x00 0xff" size_t szFrame = 0; if (szData > PN53x_NORMAL_FRAME__DATA_MAX_LEN) { // ARYGON Reader with PN532 equipped does not support extended frame (bug in ARYGON firmware?) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "ARYGON device does not support more than %d bytes as payload (requested: %" PRIdPTR ")", PN53x_NORMAL_FRAME__DATA_MAX_LEN, szData); pnd->last_error = NFC_EDEVNOTSUPP; return pnd->last_error; } if ((res = pn53x_build_frame(abtFrame + 1, &szFrame, pbtData, szData)) < 0) { pnd->last_error = res; return pnd->last_error; } if ((res = uart_send(DRIVER_DATA(pnd)->port, abtFrame, szFrame + 1, timeout)) != 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; } uint8_t abtRxBuf[PN53x_ACK_FRAME__LEN]; if ((res = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, sizeof(abtRxBuf), 0, timeout)) != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%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 if (0 == memcmp(arygon_error_unknown_mode, abtRxBuf, sizeof(abtRxBuf))) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Bad frame format."); // We have already read 6 bytes and arygon_error_unknown_mode is 10 bytes long // so we have to read 4 remaining bytes to be synchronized at the next receiving pass. pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 4, 0, timeout); return pnd->last_error; } else { return pnd->last_error; } return NFC_SUCCESS; } static int arygon_abort(nfc_device *pnd) { // Send a valid TAMA packet to wakup the PN53x (we will not have an answer, according to Arygon manual) uint8_t dummy[] = { 0x32, 0x00, 0x00, 0xff, 0x09, 0xf7, 0xd4, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0xbe, 0x00 }; uart_send(DRIVER_DATA(pnd)->port, dummy, sizeof(dummy), 0); // Using Arygon device we can't send ACK frame to abort the running command return pn53x_check_communication(pnd); } static int arygon_tama_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout) { uint8_t abtRxBuf[5]; size_t len; void *abort_p = NULL; #ifndef WIN32 abort_p = &(DRIVER_DATA(pnd)->iAbortFds[1]); #else abort_p = (void *) & (DRIVER_DATA(pnd)->abort_flag); #endif pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 5, abort_p, timeout); if (abort_p && (NFC_EOPABORTED == pnd->last_error)) { arygon_abort(pnd); /* last_error got reset by arygon_abort() */ pnd->last_error = NFC_EOPABORTED; return pnd->last_error; } if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); return pnd->last_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; return pnd->last_error; } if ((0x01 == abtRxBuf[3]) && (0xff == abtRxBuf[4])) { // 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; return pnd->last_error; } else if ((0xff == abtRxBuf[3]) && (0xff == abtRxBuf[4])) { // Extended frame // ARYGON devices does not support extended frame sending abort(); } else { // Normal frame 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; return pnd->last_error; } // abtRxBuf[3] (LEN) include TFI + (CC+1) len = abtRxBuf[3] - 2; } 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; return pnd->last_error; } // TFI + PD0 (CC+1) pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); return pnd->last_error; } if (abtRxBuf[0] != 0xD5) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch"); pnd->last_error = NFC_EIO; return pnd->last_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; return pnd->last_error; } if (len) { pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, pbtData, len, 0, timeout); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); return pnd->last_error; } } pnd->last_error = uart_receive(DRIVER_DATA(pnd)->port, abtRxBuf, 2, 0, timeout); if (pnd->last_error != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to receive data. (RX)"); return pnd->last_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; return pnd->last_error; } if (0x00 != abtRxBuf[1]) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame postamble mismatch"); pnd->last_error = NFC_EIO; return pnd->last_error; } // The PN53x command is done and we successfully received the reply return len; } void arygon_firmware(nfc_device *pnd, char *str) { const uint8_t arygon_firmware_version_cmd[] = { DEV_ARYGON_PROTOCOL_ARYGON_ASCII, 'a', 'v' }; uint8_t abtRx[16]; size_t szRx = sizeof(abtRx); int res = uart_send(DRIVER_DATA(pnd)->port, arygon_firmware_version_cmd, sizeof(arygon_firmware_version_cmd), 0); if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Unable to send ARYGON firmware command."); return; } res = uart_receive(DRIVER_DATA(pnd)->port, abtRx, szRx, 0, 0); if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "Unable to retrieve ARYGON firmware version."); return; } if (0 == memcmp(abtRx, arygon_error_none, 6)) { uint8_t *p = abtRx + 6; unsigned int szData; sscanf((const char *)p, "%02x%9s", &szData, p); if (szData > 9) szData = 9; memcpy(str, p, szData); *(str + szData) = '\0'; } } int arygon_reset_tama(nfc_device *pnd) { const uint8_t arygon_reset_tama_cmd[] = { DEV_ARYGON_PROTOCOL_ARYGON_ASCII, 'a', 'r' }; uint8_t abtRx[10]; // Attempted response is 10 bytes long size_t szRx = sizeof(abtRx); int res; uart_send(DRIVER_DATA(pnd)->port, arygon_reset_tama_cmd, sizeof(arygon_reset_tama_cmd), 500); // Two reply are possible from ARYGON device: arygon_error_none (ie. in case the byte is well-sent) // or arygon_error_unknown_mode (ie. in case of the first byte was bad-transmitted) res = uart_receive(DRIVER_DATA(pnd)->port, abtRx, szRx, 0, 1000); if (res != 0) { log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "No reply to 'reset TAMA' command."); pnd->last_error = res; return pnd->last_error; } if (0 != memcmp(abtRx, arygon_error_none, sizeof(arygon_error_none) - 1)) { pnd->last_error = NFC_EIO; return pnd->last_error; } return NFC_SUCCESS; } static int arygon_abort_command(nfc_device *pnd) { if (pnd) { #ifndef WIN32 close(DRIVER_DATA(pnd)->iAbortFds[0]); if (pipe(DRIVER_DATA(pnd)->iAbortFds) < 0) { return NFC_ESOFT; } #else DRIVER_DATA(pnd)->abort_flag = true; #endif } return NFC_SUCCESS; } const struct pn53x_io arygon_tama_io = { .send = arygon_tama_send, .receive = arygon_tama_receive, }; const struct nfc_driver arygon_driver = { .name = ARYGON_DRIVER_NAME, .scan_type = INTRUSIVE, .scan = arygon_scan, .open = arygon_open, .close = arygon_close, .strerror = pn53x_strerror, .initiator_init = pn53x_initiator_init, .initiator_init_secure_element = NULL, // No secure-element support .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 = arygon_abort_command, .idle = pn53x_idle, /* Even if PN532, PowerDown is not recommended on those devices */ .powerdown = NULL, };