diff --git a/AUTHORS b/AUTHORS index c5fc004..fac6807 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Alex Lian Anugrah Redja Kusuma Audrey Diacre Emanuele Bertoldi +Eugeny Boger Francois Kooman Ludovic Rousseau Nobuhiro Iwamatsu diff --git a/ChangeLog b/ChangeLog index 804d462..a83657e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -25,6 +25,7 @@ Fixes: - nfc-mfclassic: detect MIFARE Plus 2K as 2K instead of 1K Improvements: + - New PN532 over SPI driver, see contrib/libnfc/pn532_spi_on_rpi.conf.sample - Devels HACKING file: introduce clang/scan-build & cppcheck for better code - Better internal dependencies handling (bus <> drivers) - Cleaner handling of portability patches diff --git a/NEWS b/NEWS index f6c2170..51bb4ae 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,9 @@ New in 1.7.0-***: +Drivers: + + * New PN532 over SPI driver, see contrib/libnfc/pn532_spi_on_rpi.conf.sample + API Changes: * Functions diff --git a/cmake/modules/LibnfcDrivers.cmake b/cmake/modules/LibnfcDrivers.cmake index 267d002..a186739 100644 --- a/cmake/modules/LibnfcDrivers.cmake +++ b/cmake/modules/LibnfcDrivers.cmake @@ -2,6 +2,11 @@ SET(LIBNFC_DRIVER_ACR122_PCSC OFF CACHE BOOL "Enable ACR122 support (Depends on SET(LIBNFC_DRIVER_ACR122_USB ON CACHE BOOL "Enable ACR122 support (Direct USB connection)") SET(LIBNFC_DRIVER_ACR122S ON CACHE BOOL "Enable ACR122S support (Use serial port)") SET(LIBNFC_DRIVER_ARYGON ON CACHE BOOL "Enable ARYGON support (Use serial port)") +IF(WIN32) + SET(LIBNFC_DRIVER_PN532_SPI OFF CACHE BOOL "Enable PN532 SPI support (Use SPI bus)") +ELSE(WIN32) + SET(LIBNFC_DRIVER_PN532_SPI ON CACHE BOOL "Enable PN532 SPI support (Use SPI bus)") +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)") @@ -29,6 +34,12 @@ IF(LIBNFC_DRIVER_ARYGON) SET(UART_REQUIRED TRUE) ENDIF(LIBNFC_DRIVER_ARYGON) +IF(LIBNFC_DRIVER_PN532_SPI) + ADD_DEFINITIONS("-DDRIVER_PN532_SPI_ENABLED") + SET(DRIVERS_SOURCES ${DRIVERS_SOURCES} "drivers/pn532_spi") + SET(SPI_REQUIRED TRUE) +ENDIF(LIBNFC_DRIVER_PN532_SPI) + IF(LIBNFC_DRIVER_PN532_UART) ADD_DEFINITIONS("-DDRIVER_PN532_UART_ENABLED") SET(DRIVERS_SOURCES ${DRIVERS_SOURCES} "drivers/pn532_uart") diff --git a/configure.ac b/configure.ac index 6176591..5e7e486 100644 --- a/configure.ac +++ b/configure.ac @@ -118,9 +118,12 @@ fi # Handle --with-drivers option LIBNFC_ARG_WITH_DRIVERS -# Enable UART if +# Enable UART if AM_CONDITIONAL(UART_ENABLED, [test x"$uart_required" = x"yes"]) +# Enable SPI if +AM_CONDITIONAL(SPI_ENABLED, [test x"$spi_required" = x"yes"]) + # Documentation (default: no) AC_ARG_ENABLE([doc],AS_HELP_STRING([--enable-doc],[Enable documentation generation.]),[enable_doc=$enableval],[enable_doc="no"]) diff --git a/contrib/libnfc/pn532_spi_on_rpi.conf.sample b/contrib/libnfc/pn532_spi_on_rpi.conf.sample new file mode 100644 index 0000000..f3065d2 --- /dev/null +++ b/contrib/libnfc/pn532_spi_on_rpi.conf.sample @@ -0,0 +1,5 @@ +## Typical configuration file for PN532 device on R-Pi connected using SPI +## Note: to use SPI port on R-Pi, you have to load kernel module spi-bcm2708: +## Edit /etc/modprobe.d/raspi-blacklist.conf and comment: #blacklist spi-bcm2708 +name = "PN532 board via SPI" +connstring = pn532_spi:/dev/spidev0.0:500000 diff --git a/contrib/libnfc/pn532_uart_on_rpi.conf.sample b/contrib/libnfc/pn532_uart_on_rpi.conf.sample index 9773590..afd591e 100644 --- a/contrib/libnfc/pn532_uart_on_rpi.conf.sample +++ b/contrib/libnfc/pn532_uart_on_rpi.conf.sample @@ -1,5 +1,5 @@ ## Typical configuration file for PN532 device on R-Pi connected using UART ## Note: to use UART port on R-Pi, you have to disable linux serial console: ## http://learn.adafruit.com/adafruit-nfc-rfid-on-raspberry-pi/freeing-uart-on-the-pi -name = "PN532 board" +name = "PN532 board via UART" connstring = pn532_uart:/dev/ttyAMA0 diff --git a/contrib/libnfc/pn532_via_uart2usb.conf.sample b/contrib/libnfc/pn532_via_uart2usb.conf.sample index 50f5e3a..3bdfa96 100644 --- a/contrib/libnfc/pn532_via_uart2usb.conf.sample +++ b/contrib/libnfc/pn532_via_uart2usb.conf.sample @@ -1,3 +1,3 @@ ## Typical configuration file for PN532 board (ie. microbuilder.eu / Adafruit) device -name = "Adafruit PN532 board" +name = "Adafruit PN532 board via UART" connstring = pn532_uart:/dev/ttyUSB0 diff --git a/libnfc/CMakeLists.txt b/libnfc/CMakeLists.txt index caae6df..21ced53 100644 --- a/libnfc/CMakeLists.txt +++ b/libnfc/CMakeLists.txt @@ -25,6 +25,16 @@ IF(UART_REQUIRED) ENDIF(WIN32) ENDIF(UART_REQUIRED) +IF(SPI_REQUIRED) + IF(WIN32) + # Windows is not supported at the moment + #LIST(APPEND BUSES_SOURCES ../contrib/win32/libnfc/buses/spi) + MESSAGE( FATAL_ERROR "SPI not (yet) supported under Windows!" ) + ELSE(WIN32) + LIST(APPEND BUSES_SOURCES buses/spi) + ENDIF(WIN32) +ENDIF(SPI_REQUIRED) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/buses) IF(WIN32) diff --git a/libnfc/buses/Makefile.am b/libnfc/buses/Makefile.am index 2f6a635..a875ed7 100644 --- a/libnfc/buses/Makefile.am +++ b/libnfc/buses/Makefile.am @@ -8,6 +8,13 @@ libnfcbuses_la_CFLAGS = -I$(top_srcdir)/libnfc libnfcbuses_la_LIBADD = EXTRA_DIST = +if SPI_ENABLED +libnfcbuses_la_SOURCES += spi.c spi.h +libnfcbuses_la_CFLAGS += +libnfcbuses_la_LIBADD += +endif +EXTRA_DIST = spi.c spi.h + if UART_ENABLED libnfcbuses_la_SOURCES += uart.c uart.h libnfcbuses_la_CFLAGS += diff --git a/libnfc/buses/spi.c b/libnfc/buses/spi.c new file mode 100644 index 0000000..9d3c38a --- /dev/null +++ b/libnfc/buses/spi.c @@ -0,0 +1,316 @@ +/*- + * 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 + * 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 spi.c + * @brief SPI driver + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif // HAVE_CONFIG_H + +#include "spi.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "nfc-internal.h" + +#define LOG_GROUP NFC_LOG_GROUP_COM +#define LOG_CATEGORY "libnfc.bus.spi" + +# if defined(__APPLE__) +const char *spi_ports_device_radix[] = { "spidev", NULL }; +# elif defined (__FreeBSD__) || defined (__OpenBSD__) +const char *spi_ports_device_radix[] = { "spidev", NULL }; +# elif defined (__linux__) +const char *spi_ports_device_radix[] = { "spidev", NULL }; +# else +# error "Can't determine spi port string for your system" +# endif + + +struct spi_port_unix { + int fd; // Serial port file descriptor + //~ struct termios termios_backup; // Terminal info before using the port + //~ struct termios termios_new; // Terminal info during the transaction +}; + +#define SPI_DATA( X ) ((struct spi_port_unix *) X) + + +spi_port +spi_open(const char *pcPortName) +{ + struct spi_port_unix *sp = malloc(sizeof(struct spi_port_unix)); + + if (sp == 0) + return INVALID_SPI_PORT; + + sp->fd = open(pcPortName, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (sp->fd == -1) { + spi_close(sp); + return INVALID_SPI_PORT; + } + + + return sp; +} + + + +void +spi_set_speed(spi_port sp, const uint32_t uiPortSpeed) +{ + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "SPI port speed requested to be set to %d Hz.", uiPortSpeed); + int ret; + ret = ioctl(SPI_DATA(sp)->fd, SPI_IOC_WR_MAX_SPEED_HZ, &uiPortSpeed); + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "ret %d", ret); + + if (ret == -1) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Error setting SPI speed."); + +} + +void +spi_set_mode(spi_port sp, const uint32_t uiPortMode) +{ + log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "SPI port mode requested to be set to %d.", uiPortMode); + int ret; + ret = ioctl(SPI_DATA(sp)->fd, SPI_IOC_WR_MODE, &uiPortMode); + + if (ret == -1) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Error setting SPI mode."); + +} + +uint32_t +spi_get_speed(spi_port sp) +{ + uint32_t uiPortSpeed = 0; + + int ret; + ret = ioctl(SPI_DATA(sp)->fd, SPI_IOC_RD_MAX_SPEED_HZ, &uiPortSpeed); + + if (ret == -1) log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Error reading SPI speed."); + + return uiPortSpeed; +} + + +void +spi_close(const spi_port sp) +{ + close(SPI_DATA(sp)->fd); + free(sp); +} + + +/** + * @brief Perform bit reversal on one byte \a x + * + * @return reversed byte + */ + +static uint8_t +bit_reversal(const uint8_t x) +{ + uint8_t ret = x; + ret = (((ret & 0xaa) >> 1) | ((ret & 0x55) << 1)); + ret = (((ret & 0xcc) >> 2) | ((ret & 0x33) << 2)); + ret = (((ret & 0xf0) >> 4) | ((ret & 0x0f) << 4)); + return ret; +} + + + + + +/** + * @brief Send \a pbtTx content to SPI then receive data from SPI and copy data to \a pbtRx. CS line stays active between transfers as well as during transfers. + * + * @return 0 on success, otherwise a driver error is returned + */ +int +spi_send_receive(spi_port sp, const uint8_t *pbtTx, const size_t szTx, uint8_t *pbtRx, const size_t szRx, bool lsb_first) +{ + size_t transfers = 0; + struct spi_ioc_transfer tr[2]; + + + uint8_t *pbtTxLSB = 0; + + if (szTx) { + LOG_HEX(LOG_GROUP, "TX", pbtTx, szTx); + if (lsb_first) { + pbtTxLSB = malloc(szTx * sizeof(uint8_t)); + if (!pbtTxLSB) { + return NFC_ESOFT; + } + + size_t i; + for (i = 0; i < szTx; ++i) { + pbtTxLSB[i] = bit_reversal(pbtTx[i]); + } + + pbtTx = pbtTxLSB; + } + + struct spi_ioc_transfer tr_send = { + .tx_buf = (unsigned long) pbtTx, + .rx_buf = 0, + .len = szTx , + .delay_usecs = 0, + .speed_hz = 0, + .bits_per_word = 0, + }; + tr[transfers] = tr_send; + + ++transfers; + } + + if (szRx) { + struct spi_ioc_transfer tr_receive = { + .tx_buf = 0, + .rx_buf = (unsigned long) pbtRx, + .len = szRx, + .delay_usecs = 0, + .speed_hz = 0, + .bits_per_word = 0, + }; + tr[transfers] = tr_receive; + ++transfers; + } + + + + if (transfers) { + int ret = ioctl(SPI_DATA(sp)->fd, SPI_IOC_MESSAGE(transfers), tr); + if (szTx && lsb_first) { + free(pbtTxLSB); + } + + if (ret != (int)(szRx + szTx)) { + return NFC_EIO; + } + + // Reverse received bytes if needed + if (szRx) { + if (lsb_first) { + size_t i; + for (i = 0; i < szRx; ++i) { + pbtRx[i] = bit_reversal(pbtRx[i]); + } + } + + LOG_HEX(LOG_GROUP, "RX", pbtRx, szRx); + } + } + + + return NFC_SUCCESS; +} + + +/** + * @brief Receive data from SPI and copy data to \a pbtRx + * + * @return 0 on success, otherwise driver error code + */ +int +spi_receive(spi_port sp, uint8_t *pbtRx, const size_t szRx, bool lsb_first) +{ + return spi_send_receive(sp, 0, 0, pbtRx, szRx, lsb_first); +} + + +/** + * @brief Send \a pbtTx content to SPI + * + * @return 0 on success, otherwise a driver error is returned + */ +int +spi_send(spi_port sp, const uint8_t *pbtTx, const size_t szTx, bool lsb_first) +{ + return spi_send_receive(sp, pbtTx, szTx, 0, 0, lsb_first); +} + + +char ** +spi_list_ports(void) +{ + char **res = malloc(sizeof(char *)); + size_t szRes = 1; + + res[0] = NULL; + + DIR *pdDir = opendir("/dev"); + struct dirent *pdDirEnt; + struct dirent entry; + struct dirent *result; + while ((readdir_r(pdDir, &entry, &result) == 0) && (result != NULL)) { + pdDirEnt = &entry; +#if !defined(__APPLE__) + if (!isdigit(pdDirEnt->d_name[strlen(pdDirEnt->d_name) - 1])) + continue; +#endif + const char **p = spi_ports_device_radix; + while (*p) { + if (!strncmp(pdDirEnt->d_name, *p, strlen(*p))) { + char **res2 = realloc(res, (szRes + 1) * sizeof(char *)); + if (!res2) + goto oom; + + res = res2; + if (!(res[szRes - 1] = malloc(6 + strlen(pdDirEnt->d_name)))) + goto oom; + + sprintf(res[szRes - 1], "/dev/%s", pdDirEnt->d_name); + + szRes++; + res[szRes - 1] = NULL; + } + p++; + } + } +oom: + closedir(pdDir); + + return res; +} diff --git a/libnfc/buses/spi.h b/libnfc/buses/spi.h new file mode 100644 index 0000000..f27a83c --- /dev/null +++ b/libnfc/buses/spi.h @@ -0,0 +1,64 @@ +/*- + * 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 + * 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 spi.h + * @brief SPI driver header + */ + +#ifndef __NFC_BUS_SPI_H__ +# define __NFC_BUS_SPI_H__ + +# include + +# include +# include +# include + +# include + +# include + +// Define shortcut to types to make code more readable +typedef void *spi_port; +# define INVALID_SPI_PORT (void*)(~1) +# define CLAIMED_SPI_PORT (void*)(~2) + +spi_port spi_open(const char *pcPortName); +void spi_close(const spi_port sp); + +void spi_set_speed(spi_port sp, const uint32_t uiPortSpeed); +void spi_set_mode(spi_port sp, const uint32_t uiPortMode); +uint32_t spi_get_speed(const spi_port sp); + +int spi_receive(spi_port sp, uint8_t *pbtRx, const size_t szRx, bool lsb_first); +int spi_send(spi_port sp, const uint8_t *pbtTx, const size_t szTx, bool lsb_first); +int spi_send_receive(spi_port sp, const uint8_t *pbtTx, const size_t szTx, uint8_t *pbtRx, const size_t szRx, bool lsb_first); + +char **spi_list_ports(void); + +#endif // __NFC_BUS_SPI_H__ diff --git a/libnfc/drivers/Makefile.am b/libnfc/drivers/Makefile.am index 1d24924..aaffaa4 100644 --- a/libnfc/drivers/Makefile.am +++ b/libnfc/drivers/Makefile.am @@ -3,9 +3,9 @@ AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS) noinst_LTLIBRARIES = libnfcdrivers.la -libnfcdrivers_la_SOURCES = +libnfcdrivers_la_SOURCES = libnfcdrivers_la_CFLAGS = @DRIVERS_CFLAGS@ -I$(top_srcdir)/libnfc -I$(top_srcdir)/libnfc/buses -libnfcdrivers_la_LIBADD = +libnfcdrivers_la_LIBADD = if DRIVER_ACR122_PCSC_ENABLED libnfcdrivers_la_SOURCES += acr122_pcsc.c acr122_pcsc.h @@ -31,6 +31,10 @@ if DRIVER_PN532_UART_ENABLED libnfcdrivers_la_SOURCES += pn532_uart.c pn532_uart.h endif +if DRIVER_PN532_SPI_ENABLED +libnfcdrivers_la_SOURCES += pn532_spi.c pn532_spi.h +endif + if PCSC_ENABLED libnfcdrivers_la_CFLAGS += @libpcsclite_CFLAGS@ libnfcdrivers_la_LIBADD += @libpcsclite_LIBS@ diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c new file mode 100644 index 0000000..639f08d --- /dev/null +++ b/libnfc/drivers/pn532_spi.c @@ -0,0 +1,687 @@ +/*- + * 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 + * 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); + 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); + 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); + 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[6]; + res = spi_send_receive(DRIVER_DATA(pnd)->port, &pn532_spi_cmd_dataread, 1, abtRxBuf, 6, 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, +}; + diff --git a/libnfc/drivers/pn532_spi.h b/libnfc/drivers/pn532_spi.h new file mode 100644 index 0000000..b84c589 --- /dev/null +++ b/libnfc/drivers/pn532_spi.h @@ -0,0 +1,39 @@ +/*- + * 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 + * 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.h + * @brief Driver for PN532 connected in SPI + */ + +#ifndef __NFC_DRIVER_PN532_SPI_H__ +#define __NFC_DRIVER_PN532_SPI_H__ + +#include + +extern const struct nfc_driver pn532_spi_driver; + +#endif // ! __NFC_DRIVER_PN532_SPI_H__ diff --git a/libnfc/nfc.c b/libnfc/nfc.c index dcb2273..aa896b6 100644 --- a/libnfc/nfc.c +++ b/libnfc/nfc.c @@ -109,6 +109,10 @@ # include "drivers/pn532_uart.h" #endif /* DRIVER_PN532_UART_ENABLED */ +#if defined (DRIVER_PN532_SPI_ENABLED) +# include "drivers/pn532_spi.h" +#endif /* DRIVER_PN532_SPI_ENABLED */ + #define LOG_CATEGORY "libnfc.general" #define LOG_GROUP NFC_LOG_GROUP_GENERAL @@ -138,6 +142,9 @@ nfc_drivers_init(void) #if defined (DRIVER_PN532_UART_ENABLED) nfc_register_driver(&pn532_uart_driver); #endif /* DRIVER_PN532_UART_ENABLED */ +#if defined (DRIVER_PN532_SPI_ENABLED) + nfc_register_driver(&pn532_spi_driver); +#endif /* DRIVER_PN532_SPI_ENABLED */ #if defined (DRIVER_ARYGON_ENABLED) nfc_register_driver(&arygon_driver); #endif /* DRIVER_ARYGON_ENABLED */ diff --git a/m4/libnfc_drivers.m4 b/m4/libnfc_drivers.m4 index 22cfbe2..f83f825 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_uart' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,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 comma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_spi', 'pn532_uart' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]), [ case "${withval}" in yes | no) dnl ignore calls without any arguments @@ -22,16 +22,16 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], AC_MSG_RESULT(default 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 pn532_spi" ;; 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 pn532_spi" ;; esac - + DRIVERS_CFLAGS="" driver_acr122_pcsc_enabled="no" @@ -40,6 +40,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], driver_pn53x_usb_enabled="no" driver_arygon_enabled="no" driver_pn532_uart_enabled="no" + driver_pn532_spi_enabled="no" for driver in ${DRIVER_BUILD_LIST} do @@ -74,6 +75,11 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], driver_pn532_uart_enabled="yes" DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_UART_ENABLED" ;; + pn532_spi) + spi_required="yes" + driver_pn532_spi_enabled="yes" + DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_SPI_ENABLED" + ;; *) AC_MSG_ERROR([Unknow driver: $driver]) ;; @@ -86,6 +92,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], AM_CONDITIONAL(DRIVER_PN53X_USB_ENABLED, [test x"$driver_pn53x_usb_enabled" = xyes]) AM_CONDITIONAL(DRIVER_ARYGON_ENABLED, [test x"$driver_arygon_enabled" = xyes]) 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]) ]) AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[ @@ -97,4 +104,5 @@ echo " acr122s.......... $driver_acr122s_enabled" echo " arygon........... $driver_arygon_enabled" echo " pn53x_usb........ $driver_pn53x_usb_enabled" echo " pn532_uart....... $driver_pn532_uart_enabled" +echo " pn532_spi....... $driver_pn532_spi_enabled" ])