Add I2C protocol support for PN532.
This commit is contained in:
parent
6038aca7d1
commit
1b11450312
9 changed files with 920 additions and 1 deletions
|
@ -46,6 +46,7 @@ AC_HEADER_STDC
|
|||
AC_HEADER_STDBOOL
|
||||
AC_CHECK_HEADERS([fcntl.h limits.h stdio.h stdlib.h stdint.h stddef.h stdbool.h sys/ioctl.h sys/param.h sys/time.h termios.h])
|
||||
AC_CHECK_HEADERS([linux/spi/spidev.h], [spi_available="yes"])
|
||||
AC_CHECK_HEADERS([linux/i2c-dev.h], [i2c_available="yes"])
|
||||
AC_CHECK_FUNCS([memmove memset select strdup strerror strstr strtol usleep],
|
||||
[AC_DEFINE([_XOPEN_SOURCE], [600], [Enable POSIX extensions if present])])
|
||||
|
||||
|
@ -122,6 +123,9 @@ AM_CONDITIONAL(UART_ENABLED, [test x"$uart_required" = x"yes"])
|
|||
# Enable SPI if
|
||||
AM_CONDITIONAL(SPI_ENABLED, [test x"$spi_required" = x"yes"])
|
||||
|
||||
# Enable I2C if
|
||||
AM_CONDITIONAL(I2C_ENABLED, [test x"$i2c_required" = x"yes"])
|
||||
|
||||
# Documentation (default: no)
|
||||
AC_ARG_ENABLE([doc],AS_HELP_STRING([--enable-doc],[Enable documentation generation.]),[enable_doc=$enableval],[enable_doc="no"])
|
||||
|
||||
|
|
|
@ -28,3 +28,10 @@ if LIBUSB_ENABLED
|
|||
libnfcbuses_la_LIBADD += @libusb_LIBS@
|
||||
endif
|
||||
EXTRA_DIST += usbbus.c usbbus.h
|
||||
|
||||
if I2C_ENABLED
|
||||
libnfcbuses_la_SOURCES += i2c.c i2c.h
|
||||
libnfcbuses_la_CFLAGS +=
|
||||
libnfcbuses_la_LIBADD +=
|
||||
endif
|
||||
EXTRA_DIST += i2c.c i2c.h
|
||||
|
|
212
libnfc/buses/i2c.c
Normal file
212
libnfc/buses/i2c.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*-
|
||||
* 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:
|
||||
*
|
||||
* 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 i2c.c
|
||||
* @brief I2C driver (implemented / tested for Linux only currently)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
#include "i2c.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.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 <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
#include "nfc-internal.h"
|
||||
|
||||
#define LINUX_I2C_DRIVER_NAME "linux_i2c"
|
||||
|
||||
#define LOG_GROUP NFC_LOG_GROUP_COM
|
||||
#define LOG_CATEGORY "libnfc.bus.i2c"
|
||||
|
||||
# if defined (__linux__)
|
||||
const char *i2c_ports_device_radix[] =
|
||||
{ "i2c-", NULL };
|
||||
# else
|
||||
# error "Can't determine I2C devices standard names for your system"
|
||||
# endif
|
||||
|
||||
|
||||
struct i2c_device
|
||||
{
|
||||
int fd; // I2C device file descriptor
|
||||
};
|
||||
|
||||
#define I2C_DATA( X ) ((struct i2c_device *) X)
|
||||
|
||||
i2c_device
|
||||
i2c_open(const char *pcI2C_busName, uint32_t devAddr)
|
||||
{
|
||||
struct i2c_device *id = malloc(sizeof(struct i2c_device));
|
||||
|
||||
if (id == 0)
|
||||
return INVALID_I2C_BUS ;
|
||||
|
||||
id->fd = open(pcI2C_busName, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||
if (id->fd == -1) {
|
||||
perror("Cannot open I2C bus");
|
||||
i2c_close(id);
|
||||
return INVALID_I2C_BUS ;
|
||||
}
|
||||
|
||||
if (ioctl(id->fd, I2C_SLAVE, devAddr) < 0) {
|
||||
perror("Cannot select I2C device");
|
||||
i2c_close(id);
|
||||
return INVALID_I2C_ADDRESS ;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
i2c_close(const i2c_device id)
|
||||
{
|
||||
if (I2C_DATA(id) ->fd >= 0) {
|
||||
close(I2C_DATA(id) ->fd);
|
||||
}
|
||||
free(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a frame from the I2C device and copy data to \a pbtRx
|
||||
*
|
||||
* @param timeout Time out for data read (in milliseconds). 0 for not timeout.
|
||||
* @return 0 on success, otherwise driver error code
|
||||
*/
|
||||
int
|
||||
i2c_read(i2c_device id, uint8_t *pbtRx, const size_t szRx, void *abort_p,
|
||||
int timeout)
|
||||
{
|
||||
int iAbortFd = abort_p ? *((int *) abort_p) : 0;
|
||||
|
||||
int res;
|
||||
int done = 0;
|
||||
|
||||
struct timeval start_tv, cur_tv;
|
||||
long long duration;
|
||||
|
||||
ssize_t recCount = read(I2C_DATA(id) ->fd, pbtRx, szRx);
|
||||
|
||||
if (recCount < 0) {
|
||||
res = NFC_EIO;
|
||||
} else {
|
||||
if (recCount < (ssize_t)szRx) {
|
||||
res = NFC_EINVARG;
|
||||
} else {
|
||||
res = recCount;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a frame to I2C device containing \a pbtTx content
|
||||
*
|
||||
* @param timeout Time out for data read (in milliseconds). 0 for not timeout.
|
||||
* @return 0 on success, otherwise a driver error is returned
|
||||
*/
|
||||
int
|
||||
i2c_write(i2c_device id, const uint8_t *pbtTx, const size_t szTx, int timeout)
|
||||
{
|
||||
LOG_HEX(LOG_GROUP, "TX", pbtTx, szTx);
|
||||
|
||||
ssize_t writeCount;
|
||||
writeCount = write(I2C_DATA(id) ->fd, pbtTx, szTx);
|
||||
|
||||
if ((const ssize_t) szTx == writeCount) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG,
|
||||
"wrote %d bytes successfully.", szTx);
|
||||
return NFC_SUCCESS;
|
||||
} else {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR,
|
||||
"Error: wrote only %d bytes (%d expected).", writeCount, (int) szTx);
|
||||
return NFC_EIO;
|
||||
}
|
||||
}
|
||||
|
||||
char **
|
||||
i2c_list_ports(void)
|
||||
{
|
||||
char **res = malloc(sizeof(char *));
|
||||
if (!res) {
|
||||
perror("malloc");
|
||||
return res;
|
||||
}
|
||||
size_t szRes = 1;
|
||||
|
||||
res[0] = NULL;
|
||||
DIR *dir;
|
||||
if ((dir = opendir("/dev")) == NULL ) {
|
||||
perror("opendir error: /dev");
|
||||
return res;
|
||||
}
|
||||
struct dirent entry;
|
||||
struct dirent *result;
|
||||
|
||||
while ((readdir_r(dir, &entry, &result) == 0) && (result != NULL )) {
|
||||
const char **p = i2c_ports_device_radix;
|
||||
while (*p) {
|
||||
if (!strncmp(entry.d_name, *p, strlen(*p))) {
|
||||
char **res2 = realloc(res, (szRes + 1) * sizeof(char *));
|
||||
if (!res2) {
|
||||
perror("malloc");
|
||||
goto oom;
|
||||
}
|
||||
res = res2;
|
||||
if (!(res[szRes - 1] = malloc(6 + strlen(entry.d_name)))) {
|
||||
perror("malloc");
|
||||
goto oom;
|
||||
}
|
||||
sprintf(res[szRes - 1], "/dev/%s", entry.d_name);
|
||||
|
||||
szRes++;
|
||||
res[szRes - 1] = NULL;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
oom:
|
||||
closedir(dir);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
60
libnfc/buses/i2c.h
Normal file
60
libnfc/buses/i2c.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*-
|
||||
* 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:
|
||||
*
|
||||
* 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 i2c.h
|
||||
* @brief I2C driver header
|
||||
*/
|
||||
|
||||
#ifndef __NFC_BUS_I2C_H__
|
||||
# define __NFC_BUS_I2C_H__
|
||||
|
||||
# include <sys/time.h>
|
||||
|
||||
# include <stdio.h>
|
||||
# include <string.h>
|
||||
# include <stdlib.h>
|
||||
|
||||
# include <linux/i2c-dev.h>
|
||||
# include <nfc/nfc-types.h>
|
||||
|
||||
// extern const struct i2c_driver linux_i2c_driver;
|
||||
|
||||
typedef void *i2c_device;
|
||||
# define INVALID_I2C_BUS (void*)(~1)
|
||||
# define INVALID_I2C_ADDRESS (void*)(~2)
|
||||
|
||||
i2c_device i2c_open(const char *pcI2C_busName, uint32_t devAddr);
|
||||
|
||||
void i2c_close(const i2c_device id);
|
||||
|
||||
int i2c_read(i2c_device id, uint8_t *pbtRx, const size_t szRx, void *abort_p, int timeout);
|
||||
|
||||
int i2c_write(i2c_device id, const uint8_t *pbtTx, const size_t szTx, int timeout);
|
||||
|
||||
char ** i2c_list_ports(void);
|
||||
|
||||
#endif // __NFC_BUS_I2C_H__
|
|
@ -35,6 +35,10 @@ if DRIVER_PN532_SPI_ENABLED
|
|||
libnfcdrivers_la_SOURCES += pn532_spi.c pn532_spi.h
|
||||
endif
|
||||
|
||||
if DRIVER_PN532_I2C_ENABLED
|
||||
libnfcdrivers_la_SOURCES += pn532_i2c.c pn532_i2c.h
|
||||
endif
|
||||
|
||||
if PCSC_ENABLED
|
||||
libnfcdrivers_la_CFLAGS += @libpcsclite_CFLAGS@
|
||||
libnfcdrivers_la_LIBADD += @libpcsclite_LIBS@
|
||||
|
|
560
libnfc/drivers/pn532_i2c.c
Normal file
560
libnfc/drivers/pn532_i2c.c
Normal file
|
@ -0,0 +1,560 @@
|
|||
/*-
|
||||
* 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:
|
||||
*
|
||||
* 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_i2c.c
|
||||
* @brief PN532 driver using I2C bus.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include "pn532_i2c.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
#include "drivers.h"
|
||||
#include "nfc-internal.h"
|
||||
#include "chips/pn53x.h"
|
||||
#include "chips/pn53x-internal.h"
|
||||
#include "buses/i2c.h"
|
||||
|
||||
#define PN532_I2C_DRIVER_NAME "pn532_i2c"
|
||||
|
||||
#define LOG_CATEGORY "libnfc.driver.pn532_i2c"
|
||||
#define LOG_GROUP NFC_LOG_GROUP_DRIVER
|
||||
|
||||
// I2C address of the PN532 chip.
|
||||
#define PN532_I2C_ADDR 0x24
|
||||
|
||||
// Internal data structs
|
||||
const struct pn53x_io pn532_i2c_io;
|
||||
|
||||
struct pn532_i2c_data {
|
||||
i2c_device dev;
|
||||
volatile bool abort_flag;
|
||||
};
|
||||
|
||||
/* Delay for the loop waiting for READY frame (in ms) */
|
||||
#define PN532_RDY_LOOP_DELAY 90
|
||||
|
||||
const struct timespec rdyDelay = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = PN532_RDY_LOOP_DELAY * 1000 * 1000
|
||||
};
|
||||
|
||||
/* Private Functions Prototypes */
|
||||
|
||||
static nfc_device *pn532_i2c_open(const nfc_context *context, const nfc_connstring connstring);
|
||||
|
||||
static void pn532_i2c_close(nfc_device *pnd);
|
||||
|
||||
static int pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout);
|
||||
|
||||
static int pn532_i2c_ack(nfc_device *pnd);
|
||||
|
||||
static int pn532_i2c_abort_command(nfc_device *pnd);
|
||||
|
||||
static int pn532_i2c_wakeup(nfc_device *pnd);
|
||||
|
||||
static int pn532_i2c_wait_rdyframe(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout);
|
||||
|
||||
static size_t pn532_i2c_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len);
|
||||
|
||||
|
||||
#define DRIVER_DATA(pnd) ((struct pn532_i2c_data*)(pnd->driver_data))
|
||||
|
||||
|
||||
static size_t
|
||||
pn532_i2c_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len)
|
||||
{
|
||||
size_t device_found = 0;
|
||||
i2c_device id;
|
||||
char **i2cPorts = i2c_list_ports();
|
||||
const char *i2cPort;
|
||||
int iDevice = 0;
|
||||
|
||||
while ((i2cPort = i2cPorts[iDevice++])) {
|
||||
id = i2c_open(i2cPort, PN532_I2C_ADDR);
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Trying to find PN532 device on I2C bus %s.", i2cPort);
|
||||
|
||||
if ((id != INVALID_I2C_ADDRESS) && (id != INVALID_I2C_BUS)) {
|
||||
|
||||
nfc_connstring connstring;
|
||||
snprintf(connstring, sizeof(nfc_connstring), "%s:%s", PN532_I2C_DRIVER_NAME, i2cPort);
|
||||
nfc_device *pnd = nfc_device_new(context, connstring);
|
||||
if (!pnd) {
|
||||
perror("malloc");
|
||||
i2c_close(id);
|
||||
return 0;
|
||||
}
|
||||
pnd->driver = &pn532_i2c_driver;
|
||||
pnd->driver_data = malloc(sizeof(struct pn532_i2c_data));
|
||||
if (!pnd->driver_data) {
|
||||
perror("malloc");
|
||||
i2c_close(id);
|
||||
nfc_device_free(pnd);
|
||||
return 0;
|
||||
}
|
||||
DRIVER_DATA(pnd)->dev = id;
|
||||
|
||||
// Alloc and init chip's data
|
||||
if (pn53x_data_new(pnd, &pn532_i2c_io) == NULL) {
|
||||
perror("malloc");
|
||||
i2c_close(DRIVER_DATA(pnd)->dev);
|
||||
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);
|
||||
i2c_close(DRIVER_DATA(pnd)->dev);
|
||||
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 ((i2cPort = i2cPorts[iDevice++])) {
|
||||
free((void *)i2cPort);
|
||||
}
|
||||
free(i2cPorts);
|
||||
return device_found;
|
||||
}
|
||||
|
||||
static void
|
||||
pn532_i2c_close(nfc_device *pnd)
|
||||
{
|
||||
pn53x_idle(pnd);
|
||||
i2c_close(DRIVER_DATA(pnd)->dev);
|
||||
|
||||
pn53x_data_free(pnd);
|
||||
nfc_device_free(pnd);
|
||||
}
|
||||
|
||||
static nfc_device *
|
||||
pn532_i2c_open(const nfc_context *context, const nfc_connstring connstring)
|
||||
{
|
||||
char *i2c_devname;
|
||||
i2c_device i2c_dev;
|
||||
nfc_device *pnd;
|
||||
|
||||
// pn532_i2c:<i2c_device>
|
||||
int connstring_decode_level = connstring_decode(connstring, PN532_I2C_DRIVER_NAME, NULL, &i2c_devname, NULL);
|
||||
|
||||
switch(connstring_decode_level) {
|
||||
case 2:
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 0:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i2c_dev = i2c_open(i2c_devname, PN532_I2C_ADDR);
|
||||
|
||||
if (i2c_dev == INVALID_I2C_BUS || i2c_dev == INVALID_I2C_ADDRESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pnd = nfc_device_new(context, connstring);
|
||||
if (!pnd) {
|
||||
perror("malloc");
|
||||
i2c_close(i2c_dev);
|
||||
return NULL;
|
||||
}
|
||||
snprintf(pnd->name, sizeof(pnd->name), "%s:%s", PN532_I2C_DRIVER_NAME, i2c_devname);
|
||||
|
||||
pnd->driver_data = malloc(sizeof(struct pn532_i2c_data));
|
||||
if (!pnd->driver_data) {
|
||||
perror("malloc");
|
||||
i2c_close(i2c_dev);
|
||||
nfc_device_free(pnd);
|
||||
return NULL;
|
||||
}
|
||||
DRIVER_DATA(pnd)->dev = i2c_dev;
|
||||
|
||||
// Alloc and init chip's data
|
||||
if (pn53x_data_new(pnd, &pn532_i2c_io) == NULL) {
|
||||
perror("malloc");
|
||||
i2c_close(i2c_dev);
|
||||
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_i2c_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_i2c_close(pnd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pn53x_init(pnd);
|
||||
return pnd;
|
||||
}
|
||||
|
||||
static int
|
||||
pn532_i2c_wakeup(nfc_device *pnd)
|
||||
{
|
||||
/* No specific. PN532 holds SCL during wakeup time */
|
||||
CHIP_DATA(pnd)->power_mode = NORMAL; // PN532 should now be awake
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
#define PN532_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD)
|
||||
static int
|
||||
pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int timeout)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
// Discard any existing data ?
|
||||
|
||||
switch (CHIP_DATA(pnd)->power_mode) {
|
||||
case LOWVBAT: {
|
||||
/** PN532C106 wakeup. */
|
||||
if ((res = pn532_i2c_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_i2c_wakeup(pnd)) < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NORMAL:
|
||||
// Nothing to do :)
|
||||
break;
|
||||
};
|
||||
|
||||
uint8_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff"
|
||||
size_t szFrame = 0;
|
||||
|
||||
if ((res = pn53x_build_frame(abtFrame, &szFrame, pbtData, szData)) < 0) {
|
||||
pnd->last_error = res;
|
||||
return pnd->last_error;
|
||||
}
|
||||
|
||||
res = i2c_write(DRIVER_DATA(pnd)->dev, abtFrame, szFrame, timeout);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t abtRxBuf[6];
|
||||
|
||||
// Wait for the ACK frame
|
||||
res = pn532_i2c_wait_rdyframe(pnd, abtRxBuf, 6, timeout);
|
||||
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, res) == 0) {
|
||||
// The PN53x is running the sent command
|
||||
} else {
|
||||
return pnd->last_error;
|
||||
}
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
// pbtData pointer on buffer used to store data
|
||||
// szDataLen Length of buffer
|
||||
//
|
||||
// Return: length (in bytes) of the actual data
|
||||
static int
|
||||
pn532_i2c_wait_rdyframe(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout)
|
||||
{
|
||||
bool done = false;
|
||||
int res;
|
||||
|
||||
struct timeval start_tv, cur_tv;
|
||||
long long duration;
|
||||
|
||||
bool *abort_p = NULL;
|
||||
|
||||
/* Actual response frame includes a RDY byte */
|
||||
uint8_t *i2cRx = malloc(szDataLen + 1);
|
||||
|
||||
if (NULL == i2cRx) {
|
||||
perror("malloc");
|
||||
return NFC_ESOFT;
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
gettimeofday(&start_tv, NULL );
|
||||
}
|
||||
|
||||
do {
|
||||
/* Wait a little bit before reading */
|
||||
nanosleep(&rdyDelay, (struct timespec *) NULL );
|
||||
|
||||
int recCount = i2c_read(DRIVER_DATA(pnd)->dev, i2cRx, szDataLen + 1, abort_p, timeout);
|
||||
|
||||
if (DRIVER_DATA(pnd)->abort_flag) {
|
||||
/* Reset abort flag */
|
||||
DRIVER_DATA(pnd)->abort_flag = false;
|
||||
return NFC_EOPABORTED;
|
||||
}
|
||||
|
||||
if (recCount <= 0) {
|
||||
done = true;
|
||||
res = NFC_EIO;
|
||||
} else {
|
||||
const uint8_t rdy = i2cRx[0];
|
||||
if (rdy & 1) {
|
||||
int copyLength;
|
||||
|
||||
done = true;
|
||||
res = recCount - 1;
|
||||
copyLength = MIN (res, (int)szDataLen);
|
||||
memcpy(pbtData, &(i2cRx[1]), copyLength);
|
||||
} else {
|
||||
/* Not ready yet. Check for elapsed timeout. */
|
||||
|
||||
if (timeout > 0) {
|
||||
gettimeofday(&cur_tv, NULL );
|
||||
duration = (cur_tv.tv_sec - start_tv.tv_sec) * 1000000L
|
||||
+ (cur_tv.tv_usec - start_tv.tv_usec);
|
||||
|
||||
if (duration / 1000 > timeout) {
|
||||
res = NFC_ETIMEOUT;
|
||||
done = 1;
|
||||
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG,
|
||||
"timeout reached with no RDY frame.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
pn532_i2c_receive(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLen, int timeout)
|
||||
{
|
||||
uint8_t frameBuf[PN53X_MAX_FRAME_LENGTH];
|
||||
int frameLength;
|
||||
int TFI_idx;
|
||||
size_t len;
|
||||
|
||||
frameLength = pn532_i2c_wait_rdyframe(pnd, frameBuf, sizeof(frameBuf), timeout);
|
||||
if (frameLength < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
const uint8_t pn53x_preamble[3] = { 0x00, 0x00, 0xff };
|
||||
|
||||
if (0 != (memcmp(frameBuf, pn53x_preamble, 3))) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Frame preamble+start code mismatch");
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((0x01 == frameBuf[3]) && (0xff == frameBuf[4])) {
|
||||
uint8_t errorCode = frameBuf[5];
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Application level error detected (%d)", errorCode);
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
} else if ((0xff == frameBuf[3]) && (0xff == frameBuf[4])) {
|
||||
// Extended frame
|
||||
len = (frameBuf[5] << 8) + frameBuf[6];
|
||||
|
||||
// Verify length checksum
|
||||
if (((frameBuf[5] + frameBuf[6] + frameBuf[7]) % 256) != 0) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Length checksum mismatch");
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
TFI_idx = 8;
|
||||
} else {
|
||||
// Normal frame
|
||||
|
||||
len = frameBuf[3];
|
||||
|
||||
// Verify length checksum
|
||||
if ((uint8_t) (frameBuf[3] + frameBuf[4])) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Length checksum mismatch");
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
TFI_idx = 5;
|
||||
}
|
||||
|
||||
if ((len-2) > szDataLen) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Unable to receive data: buffer too small. (szDataLen: %" PRIuPTR ", len: %" PRIuPTR ")", szDataLen, len);
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint8_t TFI = frameBuf[TFI_idx];
|
||||
if (TFI != 0xD5) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "TFI Mismatch");
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (frameBuf[TFI_idx +1] != CHIP_DATA(pnd)->last_command + 1) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Command Code verification failed. (got %d, expected %d)",
|
||||
frameBuf[TFI_idx +1], CHIP_DATA(pnd)->last_command + 1);
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint8_t DCS = frameBuf[TFI_idx + len];
|
||||
uint8_t btDCS = DCS;
|
||||
|
||||
// Compute data checksum
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
btDCS += frameBuf[TFI_idx +i];
|
||||
}
|
||||
|
||||
if (btDCS != 0) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Data checksum mismatch (DCS = %02x, btDCS = %d)", DCS, btDCS);
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (0x00 != frameBuf[TFI_idx + len + 1]) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "Frame postamble mismatch (got %d)", frameBuf[frameLength -1]);
|
||||
pnd->last_error = NFC_EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
memcpy(pbtData, &frameBuf[TFI_idx + 2], len - 2);
|
||||
|
||||
/* The PN53x command is done and we successfully received the reply */
|
||||
return len - 2;
|
||||
error:
|
||||
return pnd->last_error;
|
||||
}
|
||||
|
||||
int
|
||||
pn532_i2c_ack(nfc_device *pnd)
|
||||
{
|
||||
if (POWERDOWN == CHIP_DATA(pnd)->power_mode) {
|
||||
int res = 0;
|
||||
if ((res = pn532_i2c_wakeup(pnd)) < 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return i2c_write(DRIVER_DATA(pnd)->dev, pn53x_ack_frame, sizeof(pn53x_ack_frame), 0);
|
||||
}
|
||||
|
||||
static int
|
||||
pn532_i2c_abort_command(nfc_device *pnd)
|
||||
{
|
||||
if (pnd) {
|
||||
DRIVER_DATA(pnd)->abort_flag = true; }
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
const struct pn53x_io pn532_i2c_io = {
|
||||
.send = pn532_i2c_send,
|
||||
.receive = pn532_i2c_receive,
|
||||
};
|
||||
|
||||
const struct nfc_driver pn532_i2c_driver = {
|
||||
.name = PN532_I2C_DRIVER_NAME,
|
||||
.scan_type = INTRUSIVE,
|
||||
.scan = pn532_i2c_scan,
|
||||
.open = pn532_i2c_open,
|
||||
.close = pn532_i2c_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_i2c_abort_command,
|
||||
.idle = pn53x_idle,
|
||||
.powerdown = pn53x_PowerDown,
|
||||
};
|
||||
|
41
libnfc/drivers/pn532_i2c.h
Normal file
41
libnfc/drivers/pn532_i2c.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*-
|
||||
* 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:
|
||||
*
|
||||
* 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_i2c.h
|
||||
* @brief Driver for PN532 connected through I2C bus
|
||||
*/
|
||||
|
||||
#ifndef __NFC_DRIVER_PN532_I2C_H__
|
||||
#define __NFC_DRIVER_PN532_I2C_H__
|
||||
|
||||
#include <nfc/nfc-types.h>
|
||||
|
||||
# define PN53X_MAX_FRAME_LENGTH 265
|
||||
|
||||
/* Reference to the I2C driver structure */
|
||||
extern const struct nfc_driver pn532_i2c_driver;
|
||||
|
||||
#endif // ! __NFC_DRIVER_I2C_H__
|
13
libnfc/nfc.c
13
libnfc/nfc.c
|
@ -113,6 +113,10 @@
|
|||
# include "drivers/pn532_spi.h"
|
||||
#endif /* DRIVER_PN532_SPI_ENABLED */
|
||||
|
||||
#if defined (DRIVER_PN532_I2C_ENABLED)
|
||||
# include "drivers/pn532_i2c.h"
|
||||
#endif /* DRIVER_PN532_I2C_ENABLED */
|
||||
|
||||
|
||||
#define LOG_CATEGORY "libnfc.general"
|
||||
#define LOG_GROUP NFC_LOG_GROUP_GENERAL
|
||||
|
@ -145,11 +149,15 @@ nfc_drivers_init(void)
|
|||
#if defined (DRIVER_PN532_SPI_ENABLED)
|
||||
nfc_register_driver(&pn532_spi_driver);
|
||||
#endif /* DRIVER_PN532_SPI_ENABLED */
|
||||
#if defined (DRIVER_PN532_I2C_ENABLED)
|
||||
nfc_register_driver(&pn532_i2c_driver);
|
||||
#endif /* DRIVER_PN532_I2C_ENABLED */
|
||||
#if defined (DRIVER_ARYGON_ENABLED)
|
||||
nfc_register_driver(&arygon_driver);
|
||||
#endif /* DRIVER_ARYGON_ENABLED */
|
||||
}
|
||||
|
||||
|
||||
/** @ingroup lib
|
||||
* @brief Register an NFC device driver with libnfc.
|
||||
* This function registers a driver with libnfc, the caller is responsible of managing the lifetime of the
|
||||
|
@ -189,6 +197,11 @@ nfc_init(nfc_context **context)
|
|||
}
|
||||
if (!nfc_drivers)
|
||||
nfc_drivers_init();
|
||||
|
||||
//#if defined (I2C_DRIVERS_ENABLED)
|
||||
// if (!i2c_drivers)
|
||||
// i2c_drivers_init();
|
||||
//#endif
|
||||
}
|
||||
|
||||
/** @ingroup lib
|
||||
|
|
|
@ -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 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.]),
|
||||
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_spi', 'pn532_uart', 'pn532_i2c' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_spi,pn532_i2c,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]),
|
||||
[ case "${withval}" in
|
||||
yes | no)
|
||||
dnl ignore calls without any arguments
|
||||
|
@ -30,6 +30,10 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
then
|
||||
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi"
|
||||
fi
|
||||
if test x"$i2c_available" = x"yes"
|
||||
then
|
||||
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_i2c"
|
||||
fi
|
||||
;;
|
||||
all)
|
||||
DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart"
|
||||
|
@ -37,6 +41,10 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
then
|
||||
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi"
|
||||
fi
|
||||
if test x"$i2c_available" = x"yes"
|
||||
then
|
||||
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_i2c"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -49,6 +57,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
driver_arygon_enabled="no"
|
||||
driver_pn532_uart_enabled="no"
|
||||
driver_pn532_spi_enabled="no"
|
||||
driver_pn532_i2c_enabled="no"
|
||||
|
||||
for driver in ${DRIVER_BUILD_LIST}
|
||||
do
|
||||
|
@ -88,6 +97,11 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
driver_pn532_spi_enabled="yes"
|
||||
DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_SPI_ENABLED"
|
||||
;;
|
||||
pn532_i2c)
|
||||
i2c_required="yes"
|
||||
driver_pn532_i2c_enabled="yes"
|
||||
DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PN532_I2C_ENABLED -DI2C_DRIVERS_ENABLED"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR([Unknow driver: $driver])
|
||||
;;
|
||||
|
@ -101,6 +115,9 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
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])
|
||||
AM_CONDITIONAL(DRIVER_PN532_I2C_ENABLED, [test x"$driver_pn532_i2c_enabled" = xyes])
|
||||
|
||||
AM_CONDITIONAL(I2C_DRIVERS_ENABLED, [test x"$i2c_required" = xyes])
|
||||
])
|
||||
|
||||
AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[
|
||||
|
@ -113,4 +130,5 @@ 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"
|
||||
echo " pn532_i2c........ $driver_pn532_i2c_enabled"
|
||||
])
|
||||
|
|
Loading…
Reference in a new issue