Merge branch 'pn532_spi'
* pn532_spi: SPI: remove dead code SPI: add 1ms sleep in wakeup() SPI: update NEWS & Changelog Add connstring example for SPI on RPi Add PN532 SPI driver to CMake spi: fix doc typos spi: remove spi_posix.c, exotic platform implementations will have to be done in their respective contrib subdir Only include SPI related files when (at least) one SPI driver is enabled pn532_spi: missing malloc() checks, spi_close(), nfc_device_close() on some error handling branches pn532_spi: fix missing free(ndd.port) pn532_spi: fix double free() of spi port info Add pn532_spi author to AUTHORS Unify copyright notices pn532_spi: use new connstring_decode() Fix cppcheck warning "Non reentrant function 'readdir' called" Fix cppcheck warning "scanf without field width limits can crash with huge input data" Fix cppcheck warning "Obsolete function 'usleep' called" spi driver: remove compilation warnings make style Adding a SPI driver for pn532
This commit is contained in:
commit
0625544cc3
17 changed files with 1177 additions and 10 deletions
1
AUTHORS
1
AUTHORS
|
@ -5,6 +5,7 @@ Alex Lian <alian@alum.mit.edu>
|
|||
Anugrah Redja Kusuma <anugrah.redja@gmail.com>
|
||||
Audrey Diacre <adiacre@il4p.fr>
|
||||
Emanuele Bertoldi <emanuele.bertoldi@gmail.com>
|
||||
Eugeny Boger <eugenyboger@gmail.com>
|
||||
Francois Kooman <fkooman@tuxed.net>
|
||||
Ludovic Rousseau <ludovic.rousseau@gmail.com>
|
||||
Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
|
||||
|
|
|
@ -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
|
||||
|
|
4
NEWS
4
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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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"])
|
||||
|
||||
|
|
5
contrib/libnfc/pn532_spi_on_rpi.conf.sample
Normal file
5
contrib/libnfc/pn532_spi_on_rpi.conf.sample
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 +=
|
||||
|
|
316
libnfc/buses/spi.c
Normal file
316
libnfc/buses/spi.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file spi.c
|
||||
* @brief SPI driver
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "spi.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
#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;
|
||||
}
|
64
libnfc/buses/spi.h
Normal file
64
libnfc/buses/spi.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file spi.h
|
||||
* @brief SPI driver header
|
||||
*/
|
||||
|
||||
#ifndef __NFC_BUS_SPI_H__
|
||||
# define __NFC_BUS_SPI_H__
|
||||
|
||||
# include <sys/time.h>
|
||||
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
|
||||
# include <linux/spi/spidev.h>
|
||||
|
||||
# include <nfc/nfc-types.h>
|
||||
|
||||
// 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__
|
|
@ -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@
|
||||
|
|
687
libnfc/drivers/pn532_spi.c
Normal file
687
libnfc/drivers/pn532_spi.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
#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 <unistd.h>
|
||||
# include <time.h>
|
||||
# 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 <winbase.h>
|
||||
# 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,
|
||||
};
|
||||
|
39
libnfc/drivers/pn532_spi.h
Normal file
39
libnfc/drivers/pn532_spi.h
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pn532_spi.h
|
||||
* @brief Driver for PN532 connected in SPI
|
||||
*/
|
||||
|
||||
#ifndef __NFC_DRIVER_PN532_SPI_H__
|
||||
#define __NFC_DRIVER_PN532_SPI_H__
|
||||
|
||||
#include <nfc/nfc-types.h>
|
||||
|
||||
extern const struct nfc_driver pn532_spi_driver;
|
||||
|
||||
#endif // ! __NFC_DRIVER_PN532_SPI_H__
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
])
|
||||
|
|
Loading…
Reference in a new issue