From d9fd9155ea65f47e96fe63eb46979262331484d0 Mon Sep 17 00:00:00 2001 From: Eugeny Boger Date: Thu, 28 Feb 2013 23:24:42 +0100 Subject: [PATCH 01/20] Adding a SPI driver for pn532 The driver seems to work well. I tested it on Linux with i.mx233-based board using hardware SPI. I tried to modify the build files as well, but it's probably a little messy. I'm not sure whether it will work on other *nix OSes, so it's probably better to limit the driver to Linux only using build system. --- libnfc/buses/Makefile.am | 4 +- libnfc/buses/spi.c | 44 +++ libnfc/buses/spi.h | 60 ++++ libnfc/buses/spi_posix.c | 300 ++++++++++++++++ libnfc/drivers/Makefile.am | 8 +- libnfc/drivers/pn532_spi.c | 676 +++++++++++++++++++++++++++++++++++++ libnfc/drivers/pn532_spi.h | 35 ++ libnfc/nfc.c | 7 + m4/libnfc_drivers.m4 | 15 +- 9 files changed, 1141 insertions(+), 8 deletions(-) create mode 100644 libnfc/buses/spi.c create mode 100644 libnfc/buses/spi.h create mode 100644 libnfc/buses/spi_posix.c create mode 100644 libnfc/drivers/pn532_spi.c create mode 100644 libnfc/drivers/pn532_spi.h diff --git a/libnfc/buses/Makefile.am b/libnfc/buses/Makefile.am index 2ce0720..f617e58 100644 --- a/libnfc/buses/Makefile.am +++ b/libnfc/buses/Makefile.am @@ -3,8 +3,8 @@ AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS) noinst_LTLIBRARIES = libnfcbuses.la -libnfcbuses_la_SOURCES = uart.c uart.h +libnfcbuses_la_SOURCES = uart.c uart.h spi.c spi.h libnfcbuses_la_CFLAGS = -I$(top_srcdir)/libnfc -EXTRA_DIST = uart_posix.c uart_win32.c +EXTRA_DIST = uart_posix.c uart_win32.c spi_posix.c diff --git a/libnfc/buses/spi.c b/libnfc/buses/spi.c new file mode 100644 index 0000000..2318782 --- /dev/null +++ b/libnfc/buses/spi.c @@ -0,0 +1,44 @@ +/*- + * Public platform independent Near Field Communication (NFC) library + * + * Copyright (C) 2013 Evgeny Boger + * Copyright (C) 2009, 2010 Roel Verdult + * Copyright (C) 2009, 2010 Romuald Conty + * + * 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 wrapper + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif // HAVE_CONFIG_H + +#include "spi.h" + +#include +#include "nfc-internal.h" + +// Test if we are dealing with unix operating systems +#ifndef _WIN32 +// The POSIX SPI port implementation +# include "spi_posix.c" +#else +// The windows SPI port implementation +# error "Not implemented" +#endif /* _WIN32 */ diff --git a/libnfc/buses/spi.h b/libnfc/buses/spi.h new file mode 100644 index 0000000..1a15850 --- /dev/null +++ b/libnfc/buses/spi.h @@ -0,0 +1,60 @@ +/*- + * Public platform independent Near Field Communication (NFC) library + * + * Copyright (C) 2013 Evgeny Boger + * Copyright (C) 2009, 2010 Roel Verdult + * Copyright (C) 2010, 2011 Romain Tarti?re + * Copyright (C) 2009, 2010, 2011 Romuald Conty + * + * 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/buses/spi_posix.c b/libnfc/buses/spi_posix.c new file mode 100644 index 0000000..4c73b68 --- /dev/null +++ b/libnfc/buses/spi_posix.c @@ -0,0 +1,300 @@ +/*- + * Public platform independent Near Field Communication (NFC) library + * + * Copyright (C) 2013 Evgeny Boger + * Copyright (C) 2009, 2010, 2011 Roel Verdult + * Copyright (C) 2009, 2010, 2011, 2012 Romuald Conty + * Copyright (C) 2011 Romain Tarti?re + * + * 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_posix.c + * @brief POSIX SPI driver + */ + +#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 + */ + +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; + while ((pdDirEnt = readdir(pdDir)) != NULL) { +#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/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..dcf247b --- /dev/null +++ b/libnfc/drivers/pn532_spi.c @@ -0,0 +1,676 @@ +/*- + * Public platform independent Near Field Communication (NFC) library + * + * Copyright (C) 2013 Evgeny Boger + * Copyright (C) 2010 Roel Verdult + * Copyright (C) 2011 Romain Tarti?re + * Copyright (C) 2010, 2011, 2012 Romuald Conty + * + * 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 + +#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 + +// 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)) { + // We need to flush input to be sure first reply does not comes from older byte transceive + //~ spi_flush_input(sp); + // 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); + pnd->driver = &pn532_spi_driver; + pnd->driver_data = malloc(sizeof(struct pn532_spi_data)); + DRIVER_DATA(pnd)->port = sp; + + // Alloc and init chip's data + pn53x_data_new(pnd, &pn532_spi_io); + // 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); + pn53x_data_free(pnd); + nfc_device_free(pnd); + spi_close(sp); + 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[128]; + uint32_t speed; +}; + +static int +pn532_connstring_decode(const nfc_connstring connstring, struct pn532_spi_descriptor *desc) +{ + char *cs = malloc(strlen(connstring) + 1); + if (!cs) { + perror("malloc"); + return -1; + } + strcpy(cs, connstring); + const char *driver_name = strtok(cs, ":"); + if (!driver_name) { + // Parse error + free(cs); + return -1; + } + + if (0 != strcmp(driver_name, PN532_SPI_DRIVER_NAME)) { + // Driver name does not match. + free(cs); + return 0; + } + + const char *port = strtok(NULL, ":"); + if (!port) { + // Only driver name was specified (or parsing error) + free(cs); + return 1; + } + strncpy(desc->port, port, sizeof(desc->port) - 1); + desc->port[sizeof(desc->port) - 1] = '\0'; + + const char *speed_s = strtok(NULL, ":"); + if (!speed_s) { + // speed not specified (or parsing error) + free(cs); + return 2; + } + unsigned long speed; + if (sscanf(speed_s, "%lu", &speed) != 1) { + // speed_s is not a number + free(cs); + return 2; + } + desc->speed = speed; + + free(cs); + return 3; +} + +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; + int connstring_decode_level = pn532_connstring_decode(connstring, &ndd); + + 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)) + 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); + snprintf(pnd->name, sizeof(pnd->name), "%s:%s", PN532_SPI_DRIVER_NAME, ndd.port); + + pnd->driver_data = malloc(sizeof(struct pn532_spi_data)); + DRIVER_DATA(pnd)->port = sp; + + // Alloc and init chip's data + pn53x_data_new(pnd, &pn532_spi_io); + // 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, int timeout) +{ + 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 + + 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, timeout)) != 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; + } + + usleep(pn532_spi_poll_interval * 1000); + } + } + + 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 manualy 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 recieve data from PN532 without SPI_DATAREAD + // and then begin full-featured read operation + + // The PN532 do not shift the internal register on the recieve 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/recieve 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; + // Before sending anything, we need to discard from any junk bytes + //~ spi_flush_input(DRIVER_DATA(pnd)->port); + + 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..3fefd89 --- /dev/null +++ b/libnfc/drivers/pn532_spi.h @@ -0,0 +1,35 @@ +/*- + * Public platform independent Near Field Communication (NFC) library + * + * Copyright (C) 2013 Evgeny Boger + * Copyright (C) 2010 Roel Verdult + * Copyright (C) 2011 Romain Tarti?re + * Copyright (C) 2010, 2011, 2012 Romuald Conty + * + * 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 b3ce252..f0d8b50 100644 --- a/libnfc/nfc.c +++ b/libnfc/nfc.c @@ -105,6 +105,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 @@ -134,6 +138,9 @@ nfc_drivers_init() #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 a0ed071..17a412d 100644 --- a/m4/libnfc_drivers.m4 +++ b/m4/libnfc_drivers.m4 @@ -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 @@ -71,6 +72,10 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], driver_pn532_uart_enabled="yes" DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_UART_ENABLED" ;; + pn532_spi) + driver_pn532_spi_enabled="yes" + DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_SPI_ENABLED" + ;; *) AC_MSG_ERROR([Unknow driver: $driver]) ;; @@ -83,6 +88,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],[ @@ -94,4 +100,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" ]) From cd98a4ac871bf066d3b85ab3fabde39ca179c9d9 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 3 Mar 2013 16:14:58 +0100 Subject: [PATCH 02/20] make style --- libnfc/buses/spi_posix.c | 32 +++++++++++++++++--------------- libnfc/drivers/pn532_spi.c | 24 ++++++++++++------------ 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/libnfc/buses/spi_posix.c b/libnfc/buses/spi_posix.c index 4c73b68..ff725c6 100644 --- a/libnfc/buses/spi_posix.c +++ b/libnfc/buses/spi_posix.c @@ -181,26 +181,28 @@ spi_send_receive(spi_port sp, const uint8_t *pbtTx, const size_t szTx, uint8_t * 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, - }; + 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, - }; + 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; } @@ -213,7 +215,7 @@ spi_send_receive(spi_port sp, const uint8_t *pbtTx, const size_t szTx, uint8_t * free(pbtTxLSB); } - if (ret != (int) (szRx + szTx)) { + if (ret != (int)(szRx + szTx)) { return NFC_EIO; } diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index dcf247b..37a202a 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -287,20 +287,20 @@ pn532_spi_wakeup(nfc_device *pnd) uint8_t spi_byte = 0; res = spi_receive(DRIVER_DATA(pnd)->port, &spi_byte, 1, true); if (res != NFC_SUCCESS) { - return res; + 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 - 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 + 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 + 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); + spi_set_speed(DRIVER_DATA(pnd)->port, prev_port_speed); } @@ -320,14 +320,14 @@ pn532_spi_wait_for_data(nfc_device *pnd, int timeout) int timer = 0; int ret; - while ( (ret = pn532_spi_read_spi_status(pnd, timeout)) != pn532_spi_ready) { + while ((ret = pn532_spi_read_spi_status(pnd, timeout)) != pn532_spi_ready) { if (ret < 0) { - return ret; + return ret; } if (DRIVER_DATA(pnd)->abort_flag) { - DRIVER_DATA(pnd)->abort_flag = false; - return NFC_EOPABORTED; + DRIVER_DATA(pnd)->abort_flag = false; + return NFC_EOPABORTED; } if (timeout > 0) { @@ -415,7 +415,7 @@ pn532_spi_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int // long preamble // omit first byte - for (size_t i = 0; i < 3; ++i){ + for (size_t i = 0; i < 3; ++i) { abtRxBuf[i] = abtRxBuf[i + 1]; } @@ -626,7 +626,7 @@ static int pn532_spi_abort_command(nfc_device *pnd) { if (pnd) { - DRIVER_DATA(pnd)->abort_flag = true; + DRIVER_DATA(pnd)->abort_flag = true; } return NFC_SUCCESS; From b0f216b3b2b8f684d03b677305ffa41c859c3fc2 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 3 Mar 2013 16:22:18 +0100 Subject: [PATCH 03/20] spi driver: remove compilation warnings spi_posix.c:141:1: warning: no previous prototype for 'bit_reversal' [-Wmissing-prototypes] => set bit_reversal() static pn532_spi.c:258:48: warning: unused parameter 'timeout' [-Wunused-parameter] => remove timeout in pn532_spi_read_spi_status() params --- libnfc/buses/spi_posix.c | 2 +- libnfc/drivers/pn532_spi.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libnfc/buses/spi_posix.c b/libnfc/buses/spi_posix.c index ff725c6..fdf000a 100644 --- a/libnfc/buses/spi_posix.c +++ b/libnfc/buses/spi_posix.c @@ -137,7 +137,7 @@ spi_close(const spi_port sp) * @return reversed byte */ -uint8_t +static uint8_t bit_reversal(const uint8_t x) { uint8_t ret = x; diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 37a202a..cef5a38 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -255,7 +255,7 @@ pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) } static int -pn532_spi_read_spi_status(nfc_device *pnd, int timeout) +pn532_spi_read_spi_status(nfc_device *pnd) { static const uint8_t pn532_spi_statread_cmd = 0x02; @@ -320,7 +320,7 @@ pn532_spi_wait_for_data(nfc_device *pnd, int timeout) int timer = 0; int ret; - while ((ret = pn532_spi_read_spi_status(pnd, timeout)) != pn532_spi_ready) { + while ((ret = pn532_spi_read_spi_status(pnd)) != pn532_spi_ready) { if (ret < 0) { return ret; } From ff11c8ac872e8dc2accfe352180daaa54d967f97 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 8 Mar 2013 23:30:21 +0100 Subject: [PATCH 04/20] Fix cppcheck warning "Obsolete function 'usleep' called" --- libnfc/drivers/pn532_spi.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index cef5a38..05cb000 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -34,8 +34,6 @@ #include #include #include -#include - #include #include "drivers.h" @@ -51,6 +49,22 @@ #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 { @@ -336,7 +350,7 @@ pn532_spi_wait_for_data(nfc_device *pnd, int timeout) return NFC_ETIMEOUT; } - usleep(pn532_spi_poll_interval * 1000); + msleep(pn532_spi_poll_interval); } } From 8e2effdc53cb559b76ce1bfb5ee497f854b4c893 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 8 Mar 2013 23:43:41 +0100 Subject: [PATCH 05/20] Fix cppcheck warning "scanf without field width limits can crash with huge input data" --- libnfc/drivers/pn532_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 05cb000..5824443 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -186,7 +186,7 @@ pn532_connstring_decode(const nfc_connstring connstring, struct pn532_spi_descri return 2; } unsigned long speed; - if (sscanf(speed_s, "%lu", &speed) != 1) { + if (sscanf(speed_s, "%10lu", &speed) != 1) { // speed_s is not a number free(cs); return 2; From 262555d56859d8f88022c0a9c92e4116ac33fc44 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 8 Mar 2013 23:50:03 +0100 Subject: [PATCH 06/20] Fix cppcheck warning "Non reentrant function 'readdir' called" --- libnfc/buses/spi_posix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libnfc/buses/spi_posix.c b/libnfc/buses/spi_posix.c index fdf000a..e862bdf 100644 --- a/libnfc/buses/spi_posix.c +++ b/libnfc/buses/spi_posix.c @@ -271,7 +271,10 @@ spi_list_ports(void) DIR *pdDir = opendir("/dev"); struct dirent *pdDirEnt; - while ((pdDirEnt = readdir(pdDir)) != NULL) { + 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; From ec99e9033a55b0806138756fb42dcadec474884a Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 10 Mar 2013 00:29:01 +0100 Subject: [PATCH 07/20] pn532_spi: use new connstring_decode() Fix cppcheck warning "Non reentrant function 'strtok' called" --- libnfc/drivers/pn532_spi.c | 81 +++++++++++--------------------------- 1 file changed, 22 insertions(+), 59 deletions(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 5824443..a1b9f23 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -54,11 +54,11 @@ # 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) + 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 @@ -144,59 +144,10 @@ pn532_spi_scan(const nfc_context *context, nfc_connstring connstrings[], const s } struct pn532_spi_descriptor { - char port[128]; + char *port; uint32_t speed; }; -static int -pn532_connstring_decode(const nfc_connstring connstring, struct pn532_spi_descriptor *desc) -{ - char *cs = malloc(strlen(connstring) + 1); - if (!cs) { - perror("malloc"); - return -1; - } - strcpy(cs, connstring); - const char *driver_name = strtok(cs, ":"); - if (!driver_name) { - // Parse error - free(cs); - return -1; - } - - if (0 != strcmp(driver_name, PN532_SPI_DRIVER_NAME)) { - // Driver name does not match. - free(cs); - return 0; - } - - const char *port = strtok(NULL, ":"); - if (!port) { - // Only driver name was specified (or parsing error) - free(cs); - return 1; - } - strncpy(desc->port, port, sizeof(desc->port) - 1); - desc->port[sizeof(desc->port) - 1] = '\0'; - - const char *speed_s = strtok(NULL, ":"); - if (!speed_s) { - // speed not specified (or parsing error) - free(cs); - return 2; - } - unsigned long speed; - if (sscanf(speed_s, "%10lu", &speed) != 1) { - // speed_s is not a number - free(cs); - return 2; - } - desc->speed = speed; - - free(cs); - return 3; -} - static void pn532_spi_close(nfc_device *pnd) { @@ -205,6 +156,7 @@ pn532_spi_close(nfc_device *pnd) // Release SPI port spi_close(DRIVER_DATA(pnd)->port); + free(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); } @@ -213,8 +165,18 @@ static nfc_device * pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) { struct pn532_spi_descriptor ndd; - int connstring_decode_level = pn532_connstring_decode(connstring, &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; } @@ -231,9 +193,10 @@ pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) 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)) + 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); From baa0f9ae36261825fbda4a554379634e23c75f5e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 10 Mar 2013 16:18:22 +0100 Subject: [PATCH 08/20] Unify copyright notices --- libnfc/buses/spi.c | 13 +++++++++---- libnfc/buses/spi.h | 14 +++++++++----- libnfc/drivers/pn532_spi.c | 14 +++++++++----- libnfc/drivers/pn532_spi.h | 14 +++++++++----- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/libnfc/buses/spi.c b/libnfc/buses/spi.c index 2318782..870f9ae 100644 --- a/libnfc/buses/spi.c +++ b/libnfc/buses/spi.c @@ -1,9 +1,14 @@ /*- - * Public platform independent Near Field Communication (NFC) library + * Free/Libre Near Field Communication (NFC) library * - * Copyright (C) 2013 Evgeny Boger - * Copyright (C) 2009, 2010 Roel Verdult - * Copyright (C) 2009, 2010 Romuald Conty + * 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 diff --git a/libnfc/buses/spi.h b/libnfc/buses/spi.h index 1a15850..f27a83c 100644 --- a/libnfc/buses/spi.h +++ b/libnfc/buses/spi.h @@ -1,10 +1,14 @@ /*- - * Public platform independent Near Field Communication (NFC) library + * Free/Libre Near Field Communication (NFC) library * - * Copyright (C) 2013 Evgeny Boger - * Copyright (C) 2009, 2010 Roel Verdult - * Copyright (C) 2010, 2011 Romain Tarti?re - * Copyright (C) 2009, 2010, 2011 Romuald Conty + * 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 diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index a1b9f23..ac666ea 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -1,10 +1,14 @@ /*- - * Public platform independent Near Field Communication (NFC) library + * Free/Libre Near Field Communication (NFC) library * - * Copyright (C) 2013 Evgeny Boger - * Copyright (C) 2010 Roel Verdult - * Copyright (C) 2011 Romain Tarti?re - * Copyright (C) 2010, 2011, 2012 Romuald Conty + * 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 diff --git a/libnfc/drivers/pn532_spi.h b/libnfc/drivers/pn532_spi.h index 3fefd89..b84c589 100644 --- a/libnfc/drivers/pn532_spi.h +++ b/libnfc/drivers/pn532_spi.h @@ -1,10 +1,14 @@ /*- - * Public platform independent Near Field Communication (NFC) library + * Free/Libre Near Field Communication (NFC) library * - * Copyright (C) 2013 Evgeny Boger - * Copyright (C) 2010 Roel Verdult - * Copyright (C) 2011 Romain Tarti?re - * Copyright (C) 2010, 2011, 2012 Romuald Conty + * 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 From a3f4145a2e48517f1f88cc0ac4fb74a90b0fbc86 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 10 Mar 2013 16:33:53 +0100 Subject: [PATCH 09/20] Add pn532_spi author to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) 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 From 87a3dd706781c0378ca7f43ccda3863506a1ed07 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 11:36:41 +0100 Subject: [PATCH 10/20] pn532_spi: fix double free() of spi port info --- libnfc/drivers/pn532_spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index ac666ea..8fe2389 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -160,7 +160,6 @@ pn532_spi_close(nfc_device *pnd) // Release SPI port spi_close(DRIVER_DATA(pnd)->port); - free(DRIVER_DATA(pnd)->port); pn53x_data_free(pnd); nfc_device_free(pnd); } From 794fcdc1adf25252b453a1aca70392ab6f38d290 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 11:51:22 +0100 Subject: [PATCH 11/20] pn532_spi: fix missing free(ndd.port) --- libnfc/drivers/pn532_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 8fe2389..9abb385 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -206,6 +206,7 @@ pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) // We have a connection pnd = nfc_device_new(context, connstring); 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)); DRIVER_DATA(pnd)->port = sp; From 1ab282d43c5f4078880d3eca319b687438cd35e1 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 12:03:09 +0100 Subject: [PATCH 12/20] pn532_spi: missing malloc() checks, spi_close(), nfc_device_close() on some error handling branches --- libnfc/drivers/pn532_spi.c | 39 +++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 9abb385..3fdc68b 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -109,12 +109,28 @@ pn532_spi_scan(const nfc_context *context, nfc_connstring connstrings[], const s 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 - pn53x_data_new(pnd, &pn532_spi_io); + 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 @@ -124,9 +140,9 @@ pn532_spi_scan(const nfc_context *context, nfc_connstring connstrings[], const s // 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); - spi_close(sp); if (res < 0) { continue; } @@ -205,14 +221,31 @@ pn532_spi_open(const nfc_context *context, const nfc_connstring connstring) // 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 - pn53x_data_new(pnd, &pn532_spi_io); + 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 From fc420d58aff427d500abb879a0f9adf85d0c77c4 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 16:58:01 +0100 Subject: [PATCH 13/20] Only include SPI related files when (at least) one SPI driver is enabled --- configure.ac | 5 ++++- libnfc/buses/Makefile.am | 5 +++-- m4/libnfc_drivers.m4 | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) 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/libnfc/buses/Makefile.am b/libnfc/buses/Makefile.am index c1b8056..a875ed7 100644 --- a/libnfc/buses/Makefile.am +++ b/libnfc/buses/Makefile.am @@ -8,11 +8,12 @@ libnfcbuses_la_CFLAGS = -I$(top_srcdir)/libnfc libnfcbuses_la_LIBADD = EXTRA_DIST = -# SPI_ENABLED +if SPI_ENABLED libnfcbuses_la_SOURCES += spi.c spi.h libnfcbuses_la_CFLAGS += libnfcbuses_la_LIBADD += -EXTRA_DIST = spi_posix.c +endif +EXTRA_DIST = spi.c spi.h if UART_ENABLED libnfcbuses_la_SOURCES += uart.c uart.h diff --git a/m4/libnfc_drivers.m4 b/m4/libnfc_drivers.m4 index 62f734a..e27eed1 100644 --- a/m4/libnfc_drivers.m4 +++ b/m4/libnfc_drivers.m4 @@ -76,6 +76,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS], 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" ;; From 12b756e97ad28ca5d507b92b23e7fb66d241be78 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 17:08:21 +0100 Subject: [PATCH 14/20] spi: remove spi_posix.c, exotic platform implementations will have to be done in their respective contrib subdir --- libnfc/buses/spi.c | 285 ++++++++++++++++++++++++++++++++++-- libnfc/buses/spi_posix.c | 305 --------------------------------------- 2 files changed, 276 insertions(+), 314 deletions(-) delete mode 100644 libnfc/buses/spi_posix.c diff --git a/libnfc/buses/spi.c b/libnfc/buses/spi.c index 870f9ae..9d3c38a 100644 --- a/libnfc/buses/spi.c +++ b/libnfc/buses/spi.c @@ -27,7 +27,7 @@ /** * @file spi.c - * @brief SPI driver wrapper + * @brief SPI driver */ #ifdef HAVE_CONFIG_H @@ -36,14 +36,281 @@ #include "spi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include "nfc-internal.h" -// Test if we are dealing with unix operating systems -#ifndef _WIN32 -// The POSIX SPI port implementation -# include "spi_posix.c" -#else -// The windows SPI port implementation -# error "Not implemented" -#endif /* _WIN32 */ +#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_posix.c b/libnfc/buses/spi_posix.c deleted file mode 100644 index e862bdf..0000000 --- a/libnfc/buses/spi_posix.c +++ /dev/null @@ -1,305 +0,0 @@ -/*- - * Public platform independent Near Field Communication (NFC) library - * - * Copyright (C) 2013 Evgeny Boger - * Copyright (C) 2009, 2010, 2011 Roel Verdult - * Copyright (C) 2009, 2010, 2011, 2012 Romuald Conty - * Copyright (C) 2011 Romain Tarti?re - * - * 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_posix.c - * @brief POSIX SPI driver - */ - -#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; -} From 7df8fdd0f680f506ff110b482480e4bf832032d0 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 22:58:59 +0100 Subject: [PATCH 15/20] spi: fix doc typos --- libnfc/drivers/pn532_spi.c | 8 ++++---- m4/libnfc_drivers.m4 | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 3fdc68b..f7a81ad 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -363,7 +363,7 @@ pn532_spi_receive_next_chunk(nfc_device *pnd, uint8_t *pbtData, const size_t szD { // 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 manualy set CS to low between two read operations + // 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. @@ -374,16 +374,16 @@ pn532_spi_receive_next_chunk(nfc_device *pnd, uint8_t *pbtData, const size_t szD // 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 recieve data from PN532 without SPI_DATAREAD + // 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 do not shift the internal register on the recieve operation, which allows us to read the whole response + // 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/recieve s r r r r r s r r s r + // 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 -->| diff --git a/m4/libnfc_drivers.m4 b/m4/libnfc_drivers.m4 index e27eed1..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 From 7c2f8b4cb0620ff587cb9baecdc6905ed32c413b Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 23:25:43 +0100 Subject: [PATCH 16/20] Add PN532 SPI driver to CMake --- cmake/modules/LibnfcDrivers.cmake | 11 +++++++++++ libnfc/CMakeLists.txt | 10 ++++++++++ 2 files changed, 21 insertions(+) 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/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) From f991771128b648bb801f29e30ffa7dd749ed1b86 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 23:29:59 +0100 Subject: [PATCH 17/20] Add connstring example for SPI on RPi --- contrib/libnfc/pn532_spi_on_rpi.conf.sample | 5 +++++ contrib/libnfc/pn532_uart_on_rpi.conf.sample | 2 +- contrib/libnfc/pn532_via_uart2usb.conf.sample | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 contrib/libnfc/pn532_spi_on_rpi.conf.sample 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 From c8e50b68520718f1eff0582a7fa2db799f7f4f38 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 27 Mar 2013 23:33:02 +0100 Subject: [PATCH 18/20] SPI: update NEWS & Changelog --- ChangeLog | 1 + NEWS | 4 ++++ 2 files changed, 5 insertions(+) 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 From f9bd5d79d2813d513b6ec688721a714c21ace560 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 28 Mar 2013 00:11:56 +0100 Subject: [PATCH 19/20] SPI: add 1ms sleep in wakeup() Without this delay I had troubles with a Raspberry-Pi: With debug enabled I could go as high as 7.8MHz but without debug I had to go lower than 500kHz otherwise I got following error: errorlibnfc.driver.pn532_spiUnable to wait for SPI data. (RX) pn53x_check_communication: Timeout It could work occassionally faster but very unreliable. So the delay introduced by printf() was enough to "fix" the problem, therefore this little extra sleep() in wakeup(). --- libnfc/drivers/pn532_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index f7a81ad..1202868 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -307,6 +307,7 @@ pn532_spi_wakeup(nfc_device *pnd) 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"); From 1b45bd4ee170cd2afb136b11e9ed7725d4d30416 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Thu, 28 Mar 2013 00:15:17 +0100 Subject: [PATCH 20/20] SPI: remove dead code --- libnfc/drivers/pn532_spi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libnfc/drivers/pn532_spi.c b/libnfc/drivers/pn532_spi.c index 1202868..639f08d 100644 --- a/libnfc/drivers/pn532_spi.c +++ b/libnfc/drivers/pn532_spi.c @@ -100,8 +100,6 @@ pn532_spi_scan(const nfc_context *context, nfc_connstring connstrings[], const s 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)) { - // We need to flush input to be sure first reply does not comes from older byte transceive - //~ spi_flush_input(sp); // 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); @@ -555,8 +553,6 @@ static int pn532_spi_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 - //~ spi_flush_input(DRIVER_DATA(pnd)->port); switch (CHIP_DATA(pnd)->power_mode) { case LOWVBAT: {