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.
This commit is contained in:
parent
a0ebd8ba15
commit
d9fd9155ea
9 changed files with 1141 additions and 8 deletions
|
@ -3,8 +3,8 @@
|
||||||
AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS)
|
AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libnfcbuses.la
|
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
|
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
|
||||||
|
|
||||||
|
|
44
libnfc/buses/spi.c
Normal file
44
libnfc/buses/spi.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file spi.c
|
||||||
|
* @brief SPI driver wrapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif // HAVE_CONFIG_H
|
||||||
|
|
||||||
|
#include "spi.h"
|
||||||
|
|
||||||
|
#include <nfc/nfc.h>
|
||||||
|
#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 */
|
60
libnfc/buses/spi.h
Normal file
60
libnfc/buses/spi.h
Normal file
|
@ -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 <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__
|
300
libnfc/buses/spi_posix.c
Normal file
300
libnfc/buses/spi_posix.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file spi_posix.c
|
||||||
|
* @brief POSIX SPI driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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-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;
|
||||||
|
}
|
|
@ -3,9 +3,9 @@ AM_CPPFLAGS = $(all_includes) $(LIBNFC_CFLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libnfcdrivers.la
|
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_CFLAGS = @DRIVERS_CFLAGS@ -I$(top_srcdir)/libnfc -I$(top_srcdir)/libnfc/buses
|
||||||
libnfcdrivers_la_LIBADD =
|
libnfcdrivers_la_LIBADD =
|
||||||
|
|
||||||
if DRIVER_ACR122_PCSC_ENABLED
|
if DRIVER_ACR122_PCSC_ENABLED
|
||||||
libnfcdrivers_la_SOURCES += acr122_pcsc.c acr122_pcsc.h
|
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
|
libnfcdrivers_la_SOURCES += pn532_uart.c pn532_uart.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if DRIVER_PN532_SPI_ENABLED
|
||||||
|
libnfcdrivers_la_SOURCES += pn532_spi.c pn532_spi.h
|
||||||
|
endif
|
||||||
|
|
||||||
if PCSC_ENABLED
|
if PCSC_ENABLED
|
||||||
libnfcdrivers_la_CFLAGS += @libpcsclite_CFLAGS@
|
libnfcdrivers_la_CFLAGS += @libpcsclite_CFLAGS@
|
||||||
libnfcdrivers_la_LIBADD += @libpcsclite_LIBS@
|
libnfcdrivers_la_LIBADD += @libpcsclite_LIBS@
|
||||||
|
|
676
libnfc/drivers/pn532_spi.c
Normal file
676
libnfc/drivers/pn532_spi.c
Normal file
|
@ -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 <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 <unistd.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
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
};
|
||||||
|
|
35
libnfc/drivers/pn532_spi.h
Normal file
35
libnfc/drivers/pn532_spi.h
Normal file
|
@ -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 <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__
|
|
@ -105,6 +105,10 @@
|
||||||
# include "drivers/pn532_uart.h"
|
# include "drivers/pn532_uart.h"
|
||||||
#endif /* DRIVER_PN532_UART_ENABLED */
|
#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_CATEGORY "libnfc.general"
|
||||||
#define LOG_GROUP NFC_LOG_GROUP_GENERAL
|
#define LOG_GROUP NFC_LOG_GROUP_GENERAL
|
||||||
|
@ -134,6 +138,9 @@ nfc_drivers_init()
|
||||||
#if defined (DRIVER_PN532_UART_ENABLED)
|
#if defined (DRIVER_PN532_UART_ENABLED)
|
||||||
nfc_register_driver(&pn532_uart_driver);
|
nfc_register_driver(&pn532_uart_driver);
|
||||||
#endif /* DRIVER_PN532_UART_ENABLED */
|
#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)
|
#if defined (DRIVER_ARYGON_ENABLED)
|
||||||
nfc_register_driver(&arygon_driver);
|
nfc_register_driver(&arygon_driver);
|
||||||
#endif /* DRIVER_ARYGON_ENABLED */
|
#endif /* DRIVER_ARYGON_ENABLED */
|
||||||
|
|
|
@ -22,16 +22,16 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
||||||
AC_MSG_RESULT(default drivers)
|
AC_MSG_RESULT(default drivers)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
case "${DRIVER_BUILD_LIST}" in
|
case "${DRIVER_BUILD_LIST}" in
|
||||||
default)
|
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)
|
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
|
esac
|
||||||
|
|
||||||
DRIVERS_CFLAGS=""
|
DRIVERS_CFLAGS=""
|
||||||
|
|
||||||
driver_acr122_pcsc_enabled="no"
|
driver_acr122_pcsc_enabled="no"
|
||||||
|
@ -40,6 +40,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
||||||
driver_pn53x_usb_enabled="no"
|
driver_pn53x_usb_enabled="no"
|
||||||
driver_arygon_enabled="no"
|
driver_arygon_enabled="no"
|
||||||
driver_pn532_uart_enabled="no"
|
driver_pn532_uart_enabled="no"
|
||||||
|
driver_pn532_spi_enabled="no"
|
||||||
|
|
||||||
for driver in ${DRIVER_BUILD_LIST}
|
for driver in ${DRIVER_BUILD_LIST}
|
||||||
do
|
do
|
||||||
|
@ -71,6 +72,10 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
||||||
driver_pn532_uart_enabled="yes"
|
driver_pn532_uart_enabled="yes"
|
||||||
DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_UART_ENABLED"
|
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])
|
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_PN53X_USB_ENABLED, [test x"$driver_pn53x_usb_enabled" = xyes])
|
||||||
AM_CONDITIONAL(DRIVER_ARYGON_ENABLED, [test x"$driver_arygon_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_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],[
|
AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[
|
||||||
|
@ -94,4 +100,5 @@ echo " acr122s.......... $driver_acr122s_enabled"
|
||||||
echo " arygon........... $driver_arygon_enabled"
|
echo " arygon........... $driver_arygon_enabled"
|
||||||
echo " pn53x_usb........ $driver_pn53x_usb_enabled"
|
echo " pn53x_usb........ $driver_pn53x_usb_enabled"
|
||||||
echo " pn532_uart....... $driver_pn532_uart_enabled"
|
echo " pn532_uart....... $driver_pn532_uart_enabled"
|
||||||
|
echo " pn532_spi....... $driver_pn532_spi_enabled"
|
||||||
])
|
])
|
||||||
|
|
Loading…
Reference in a new issue