Broke whole the libnfc :-)

use a new way to handle drivers
use absolute include path instead of relative ones
move some nfc_device_t members in a better place
nfc_device_t now embeddeds driver data and chip data pointers (useful to be more generic)
use more readable variables instead of strange coding convention
move PRINT_HEX macro into nfc-internal.h
silent warnings with more strict CFLAGS
chips/pn53x: use the powerful C99 writing to construct PN53x commands
chips/pn53x: remove almost all memcpy()
chips/pn53x: WriteRegister, ReadRegister and SetParameters command wrappers are correctly named
chips/pn53x: introduce chip state (SLEEP, NORMAL or EXECUTE)
chips/pn53x: add SAMConfiguration command wrapper (need to be improved)
chips/pn53x: remove almost all const arrays
chips/pn53x: use human readable defines for commands instead of hex values
chips/pn53x: in debug mode, the PN53x command is shown in human-readable string, awesome isn't it? ;-)
drivers: split transceive() into send() and receive() to be able to handle more cases (differed replies, abort commands, etc) later
drivers: use a const structure of functions instead of -dirty- callbacks array
drivers/pn532_uart: major improvement of UART handling
drivers/pn532_uart: check PN53x frames when received
buses/uart: receive() is now based on expected bytes instead of calculated timeouts..
buses/uart: use a smart way to determine available ports on POSIX systems (tested on Linux and FreeBSD)
This commit is contained in:
Romuald Conty 2011-03-02 15:00:44 +00:00
parent f1d909ae74
commit 5af845cdfc
20 changed files with 937 additions and 775 deletions

View file

@ -56,10 +56,6 @@ nfc_initiator_mifare_cmd (nfc_device_t * pnd, const mifare_cmd mc, const uint8_t
byte_t abtCmd[265];
bool bEasyFraming;
// Make sure we are dealing with a active device
if (!pnd->bActive)
return false;
abtCmd[0] = mc; // The MIFARE Classic command
abtCmd[1] = ui8Block; // The block address (1K=0x00..0x39, 4K=0x00..0xff)

View file

@ -40,10 +40,10 @@
#include "nfc-utils.h"
#include "chips/pn53x.h"
#include "chips/pn53x-internal.h"
#define MAX_DEVICE_COUNT 16
int
main (int argc, const char *argv[])
{
@ -54,7 +54,7 @@ main (int argc, const char *argv[])
const char *acLibnfcVersion;
bool result;
byte_t abtRx[PN53x_EXTENDED_FRAME_MAX_LEN];
byte_t abtRx[PN53x_EXTENDED_FRAME__DATA_MAX_LEN];
size_t szRx;
const byte_t pncmd_diagnose_communication_line_test[] = { 0xD4, 0x00, 0x00, 0x06, 'l', 'i', 'b', 'n', 'f', 'c' };
const byte_t pncmd_diagnose_rom_test[] = { 0xD4, 0x00, 0x01 };
@ -88,19 +88,19 @@ main (int argc, const char *argv[])
printf ("NFC device [%s] connected.\n", pnd->acName);
result = pn53x_transceive (pnd, pncmd_diagnose_communication_line_test, sizeof (pncmd_diagnose_communication_line_test), abtRx, &szRx);
result = pn53x_transceive (pnd, pncmd_diagnose_communication_line_test, sizeof (pncmd_diagnose_communication_line_test), abtRx, &szRx, false);
if (result) {
result = (memcmp (pncmd_diagnose_communication_line_test + 2, abtRx, sizeof (pncmd_diagnose_communication_line_test) - 2) == 0);
}
printf (" Communication line test: %s\n", result ? "OK" : "Failed");
result = pn53x_transceive (pnd, pncmd_diagnose_rom_test, sizeof (pncmd_diagnose_rom_test), abtRx, &szRx);
result = pn53x_transceive (pnd, pncmd_diagnose_rom_test, sizeof (pncmd_diagnose_rom_test), abtRx, &szRx, false);
if (result) {
result = ((szRx == 1) && (abtRx[0] == 0x00));
}
printf (" ROM test: %s\n", result ? "OK" : "Failed");
result = pn53x_transceive (pnd, pncmd_diagnose_ram_test, sizeof (pncmd_diagnose_ram_test), abtRx, &szRx);
result = pn53x_transceive (pnd, pncmd_diagnose_ram_test, sizeof (pncmd_diagnose_ram_test), abtRx, &szRx, false);
if (result) {
result = ((szRx == 1) && (abtRx[0] == 0x00));
}

View file

@ -93,7 +93,7 @@ sam_connection (nfc_device_t * pnd, int mode)
break;
}
if (!pn53x_transceive (pnd, pncmd_sam_config, szCmd, abtRx, &szRx)) {
if (!pn53x_transceive (pnd, pncmd_sam_config, szCmd, abtRx, &szRx, false)) {
nfc_perror(pnd, "pn53x_transceive");
ERR ("%s %d", "Unable to execute SAMConfiguration command with mode byte:", mode);
return false;

View file

@ -167,7 +167,7 @@ int main(int argc, const char* argv[])
printf("Tx: ");
print_hex((byte_t*)abtTx+1,szTx-1);
if (!pn53x_transceive (pnd, abtTx, szTx, abtRx, &szRx)) {
if (!pn53x_transceive (pnd, abtTx, szTx, abtRx, &szRx, false)) {
free(cmd);
nfc_perror (pnd, "Rx");
continue;

View file

@ -3,6 +3,7 @@
*
* Copyright (C) 2009, Roel Verdult
* Copyright (C) 2010, Romain Tartière, Romuald Conty
* Copyright (C) 2011, Romain Tartière, 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
@ -16,8 +17,9 @@
*
* 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 nfc-types.h
* @brief Define NFC types
*/
@ -25,12 +27,6 @@
#ifndef __NFC_TYPES_H__
# define __NFC_TYPES_H__
/**
* @file types.h
* @brief libnfc-defined types
*
* Define libnfc specific types: typedef, enum, struct, etc.
*/
# include <stddef.h>
# include <stdint.h>
# include <stdbool.h>
@ -38,32 +34,19 @@
typedef uint8_t byte_t;
typedef enum {
NC_PN531 = 0x10,
NC_PN532 = 0x20,
NC_PN533 = 0x30,
} nfc_chip_t;
struct driver_callbacks; // Prototype the callback struct
typedef void *nfc_device_spec_t; // Device connection specification
# define DEVICE_NAME_LENGTH 256
/**
* @struct nfc_device_t
* @brief NFC device information
*/
typedef struct {
/** Callback functions for handling device specific wrapping */
const struct driver_callbacks *pdc;
/** Driver's functions for handling device specific wrapping */
const struct nfc_driver_t *driver;
void* driver_data;
void* chip_data;
/** Device name string, including device wrapper firmware */
char acName[DEVICE_NAME_LENGTH];
/** PN53X chip type, this is useful for some "bug" work-arounds */
nfc_chip_t nc;
/** Pointer to the device connection specification */
nfc_device_spec_t nds;
/** This represents if the PN53X device was initialized succesful */
bool bActive;
/** Is the crc automaticly added, checked and removed from the frames */
bool bCrc;
/** Does the PN53x chip handles parity bits, all parities are handled as data */
@ -87,8 +70,10 @@ typedef struct {
* +----------- Driver-level general error (common to all drivers)
*/
int iLastError;
/** Last sent command */
int iLastCommand;
} nfc_device_t;
// TODO: Move chip's specifics in a chips structure (e.g. iLastCommand, ui8Parameters, ui8TxBits)
/**
* @struct nfc_device_desc_t
@ -118,29 +103,6 @@ struct chip_callbacks {
const char *(*strerror) (const nfc_device_t * pnd);
};
/**
* @struct driver_callbacks
* @brief Generic structure to handle NFC device functions.
*/
struct driver_callbacks {
/** Driver name */
const char *acDriver;
/** Chip specific callback functions */
const struct chip_callbacks *pcc;
/** Pick devices callback */
nfc_device_desc_t *(*pick_device) (void);
/** List devices callback */
bool (*list_devices) (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound);
/** Connect callback */
nfc_device_t *(*connect) (const nfc_device_desc_t * pndd);
/** Init callback */
void (*init) (nfc_device_t * pnd);
/** Transceive callback */
bool (*transceive) (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, byte_t * pbtRx, size_t * pszRx);
/** Disconnect callback */
void (*disconnect) (nfc_device_t * pnd);
};
// Compiler directive, set struct alignment to 1 byte_t for compatibility
# pragma pack(1)

View file

@ -3,7 +3,7 @@ SUBDIRS = chips buses drivers .
# set the include path found by configure
INCLUDES = $(all_includes) $(LIBNFC_CFLAGS)
noinst_HEADERS = chips.h buses.h drivers.h mirror-subr.h
noinst_HEADERS = chips.h buses.h drivers.h mirror-subr.h nfc-internal.h
lib_LTLIBRARIES = libnfc.la
libnfc_la_SOURCES = nfc.c iso14443-subr.c mirror-subr.c
libnfc_la_LDFLAGS = -no-undefined -version-info 1:0:0

View file

@ -1,7 +1,9 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2009, Roel Verdult, Romuald Conty
* Copyright (C) 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2011, Romuald Conty, 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
@ -54,17 +56,7 @@
// Try to guess what we should use.
# if defined (_WIN32)
# define DEFAULT_SERIAL_PORTS { "COM1", "COM2", "COM3", "COM4", NULL }
# elif defined(__APPLE__)
// XXX: find UART connection string for PN53X device on Mac OS X when multiples devices are used
# define DEFAULT_SERIAL_PORTS { "/dev/tty.SLAB_USBtoUART", NULL }
# elif defined (__FreeBSD__) || defined (__OpenBSD__)
// XXX: Not tested
# define DEFAULT_SERIAL_PORTS { "/dev/cuau0", "/dev/cuau1", "/dev/cuau2", "/dev/cuau3", NULL }
# elif defined (__linux__)
# define DEFAULT_SERIAL_PORTS { "/dev/ttyUSB0", "/dev/ttyUSB1", "/dev/ttyUSB2", "/dev/ttyUSB3", "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", NULL }
# else
# error "Can't determine serial string for your system"
# endif
# endif
// Define shortcut to types to make code more readable
typedef void *serial_port;
@ -77,7 +69,9 @@ void uart_close (const serial_port sp);
void uart_set_speed (serial_port sp, const uint32_t uiPortSpeed);
uint32_t uart_get_speed (const serial_port sp);
int uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx);
int uart_receive (serial_port sp, byte_t * pbtRx, const size_t szRx);
int uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx);
char **uart_list_ports (void);
#endif // __NFC_BUS_UART_H__

View file

@ -2,6 +2,8 @@
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2011, Romuald Conty, 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
@ -23,9 +25,29 @@
* @brief POSIX UART driver
*/
/* vim: set ts=2 sw=2 et: */
# include <sys/types.h>
# include <sys/select.h>
# include <sys/param.h>
# include <ctype.h>
# include <termios.h>
# include <stdio.h>
# include <dirent.h>
# include "nfc-internal.h"
# if defined(__APPLE__)
// FIXME: find UART connection string for PN53X device on Mac OS X when multiples devices are used
char *serial_ports_device_radix[] = { "tty.SLAB_USBtoUART", NULL };
# elif defined (__FreeBSD__) || defined (__OpenBSD__)
char *serial_ports_device_radix[] = { "cuaU", "cuau", NULL };
# elif defined (__linux__)
char *serial_ports_device_radix[] = { "ttyUSB", "ttyS", NULL };
# else
# error "Can't determine serial string for your system"
# endif
typedef struct termios term_info;
typedef struct {
int fd; // Serial port file descriptor
@ -33,12 +55,6 @@ typedef struct {
term_info tiNew; // Terminal info during the transaction
} serial_port_unix;
// timeval struct that define timeout delay for serial port:
// first is constant and currently related to PN53x response delay
static const unsigned long int uiTimeoutStatic = 15000; // 15 ms to allow device to respond
// second is a per-byte timeout (sets when setting baudrate)
static unsigned long int uiTimeoutPerByte = 0;
// Work-around to claim uart interface using the c_iflag (software input processing) from the termios struct
# define CCLAIMED 0x80000000
@ -85,25 +101,10 @@ uart_open (const char *pcPortName)
return sp;
}
/**
* @note This define convert a Baud rate in a per-byte duration (in µs)
* Bauds are "symbols per second", so each symbol is bit here.
* We want to convert Bd to bytes/s in first time,
* 1 serial-transmitted byte is (in 8N1):
* - 1 start bit,
* - 8 data bits,
* - 1 stop bit.
*
* In 8N1 mode, byte-rate = baud-rate / 10
*/
#define UART_BAUDRATE_T0_BYTE_DURATION(X) ((1000000 * 10)/ X)
void
uart_set_speed (serial_port sp, const uint32_t uiPortSpeed)
{
// Set per-byte timeout
uiTimeoutPerByte = UART_BAUDRATE_T0_BYTE_DURATION(uiPortSpeed);
DBG ("Serial port speed requested to be set to %d bauds (%lu µs).", uiPortSpeed, uiTimeoutPerByte);
DBG ("Serial port speed requested to be set to %d bauds.", uiPortSpeed);
const serial_port_unix *spu = (serial_port_unix *) sp;
// Portability note: on some systems, B9600 != 9600 so we have to do
@ -203,28 +204,26 @@ uart_close (const serial_port sp)
free (sp);
}
static const struct timeval tvTimeout = {
.tv_sec = 1,
.tv_usec = 0
};
/**
* @brief Receive data from UART and copy data to \a pbtRx
*
* @return 0 on success, otherwise driver error code
*/
int
uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
uart_receive (serial_port sp, byte_t * pbtRx, const size_t szRx)
{
int res;
int byteCount;
fd_set rfds;
int iExpectedByteCount = (int)*pszRx;
DBG ("iExpectedByteCount == %d", iExpectedByteCount);
struct timeval tvTimeout = {
.tv_sec = 0,
.tv_usec = uiTimeoutStatic + (uiTimeoutPerByte * iExpectedByteCount),
};
struct timeval tv = tvTimeout;
// Reset the output count
*pszRx = 0;
int received_bytes_count = 0;
int available_bytes_count = 0;
const int expected_bytes_count = (int)szRx;
int res;
fd_set rfds;
do {
// Reset file descriptor
FD_ZERO (&rfds);
@ -238,35 +237,25 @@ uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
}
// Read time-out
if (res == 0) {
if (*pszRx == 0) {
// Error, we received no data
// DBG ("RX time-out (%lu µs), buffer empty.", tvTimeout.tv_usec);
return DETIMEOUT;
} else {
// We received some data, but nothing more is available
return 0;
}
DBG ("Timeout!");
return DETIMEOUT;
}
// Retrieve the count of the incoming bytes
res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &byteCount);
if (res < 0) {
res = ioctl (((serial_port_unix *) sp)->fd, FIONREAD, &available_bytes_count);
if (res != 0) {
return DEIO;
}
// There is something available, read the data
res = read (((serial_port_unix *) sp)->fd, pbtRx + (*pszRx), MIN(byteCount, iExpectedByteCount));
iExpectedByteCount -= MIN (byteCount, iExpectedByteCount);
// DBG ("expected bytes: %zu, received bytes: %d, available bytes: %d", szRx, received_bytes_count, available_bytes_count);
res = read (((serial_port_unix *) sp)->fd, pbtRx + received_bytes_count, MIN(available_bytes_count, (expected_bytes_count - received_bytes_count)));
// Stop if the OS has some troubles reading the data
if (res <= 0) {
return DEIO;
}
received_bytes_count += res;
*pszRx += res;
// Reload timeout with a low value to prevent from waiting too long on slow devices (16x is enought to took at least 1 byte)
tv.tv_usec = uiTimeoutPerByte * MIN( iExpectedByteCount, 16 );
// DBG("Timeout reloaded at: %d µs", tv.tv_usec);
} while (byteCount && (iExpectedByteCount > 0));
DBG ("byteCount == %d, iExpectedByteCount == %d", byteCount, iExpectedByteCount);
} while (expected_bytes_count > received_bytes_count);
PRINT_HEX ("RX", pbtRx, szRx);
return 0;
}
@ -278,13 +267,16 @@ uart_receive (serial_port sp, byte_t * pbtRx, size_t * pszRx)
int
uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
{
PRINT_HEX ("TX", pbtTx, szTx);
#if 0
if ((int) szTx == write (((serial_port_unix *) sp)->fd, pbtTx, szTx))
return 0;
else
return DEIO;
#endif
int32_t res;
size_t szPos = 0;
fd_set rfds;
struct timeval tvTimeout = {
.tv_sec = 0,
.tv_usec = uiTimeoutStatic + (uiTimeoutPerByte * szTx),
};
struct timeval tv = tvTimeout;
while (szPos < szTx) {
@ -312,10 +304,45 @@ uart_send (serial_port sp, const byte_t * pbtTx, const size_t szTx)
}
szPos += res;
// Reload timeout with a low value to prevent from waiting too long on slow devices (16x is enought to took at least 1 byte)
tv.tv_usec = uiTimeoutStatic + uiTimeoutPerByte * MIN( szTx - szPos, 16 );
}
return 0;
}
char **
uart_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 (!isdigit (pdDirEnt->d_name[strlen (pdDirEnt->d_name) - 1]))
continue;
char **p = serial_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;
}

View file

@ -2,8 +2,7 @@
# set the include path found by configure
INCLUDES= $(all_includes) $(LIBNFC_CFLAGS)
noinst_HEADERS = pn53x.h
noinst_HEADERS = pn53x.h pn53x-internal.h
noinst_LTLIBRARIES = libnfcchips.la
libnfcchips_la_SOURCES = pn53x.c
libnfcchips_la_CFLAGS = -I$(top_srcdir)/libnfc

View file

@ -0,0 +1,190 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2011, Romuald Conty, 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 pn53x-internal.h
* @brief PN531, PN532 and PN533 defines and compatibility
*/
#ifndef __PN53X_INTERNAL_H__
#define __PN53X_INTERNAL_H__
#include "pn53x.h"
// Miscellaneous
#define Diagnose 0x00
#define GetFirmwareVersion 0x02
#define GetGeneralStatus 0x04
#define ReadRegister 0x06
#define WriteRegister 0x08
#define ReadGPIO 0x0C
#define WriteGPIO 0x0E
#define SetSerialBaudRate 0x10
#define SetParameters 0x12
#define SAMConfiguration 0x14
#define PowerDown 0x16
#define AlparCommandForTDA 0x18
// RF communication
#define RFConfiguration 0x32
#define RFRegulationTest 0x58
// Initiator
#define InJumpForDEP 0x56
#define InJumpForPSL 0x46
#define InListPassiveTarget 0x4A
#define InATR 0x50
#define InPSL 0x4E
#define InDataExchange 0x40
#define InCommunicateThru 0x42
#define InQuartetByteExchange 0x38
#define InDeselect 0x44
#define InRelease 0x52
#define InSelect 0x54
#define InActivateDeactivatePaypass 0x48
#define InAutoPoll 0x60
// Target
#define TgInitAsTarget 0x8C
#define TgSetGeneralBytes 0x92
#define TgGetData 0x86
#define TgSetData 0x8E
#define TgSetDataSecure 0x96
#define TgSetMetaData 0x94
#define TgSetMetaDataSecure 0x98
#define TgGetInitiatorCommand 0x88
#define TgResponseToInitiator 0x90
#define TgGetTargetStatus 0x8A
/** @note PN53x's normal frame:
*
* .-- Start
* | .-- Packet lenght
* | | .-- Lenght checksum
* | | | .-- Direction (D4 Host to PN, D5 PN to Host)
* | | | | .-- Code
* | | | | | .-- Packet checksum
* | | | | | | .-- Postamble
* V | | | | | |
* ----- V V V V V V
* 00 FF 02 FE D4 02 2A 00
*/
/** @note PN53x's extended frame:
*
* .-- Start
* | .-- Fixed to FF to enable extended frame
* | | .-- Packet lenght
* | | | .-- Lenght checksum
* | | | | .-- Direction (D4 Host to PN, D5 PN to Host)
* | | | | | .-- Code
* | | | | | | .-- Packet checksum
* | | | | | | | .-- Postamble
* V V V | | | | |
* ----- ----- ----- V V V V V
* 00 FF FF FF 00 02 FE D4 02 2A 00
*/
/**
* Start bytes, packet lenght, lenght checksum, direction, packet checksum and postamble are overhead
*/
// The TFI is considered part of the overhead
# define PN53x_NORMAL_FRAME__DATA_MAX_LEN 254
# define PN53x_NORMAL_FRAME__OVERHEAD 8
# define PN53x_EXTENDED_FRAME__DATA_MAX_LEN 264
# define PN53x_EXTENDED_FRAME__OVERHEAD 11
typedef struct {
uint8_t ui8Code;
uint8_t ui8CompatFlags;
#ifdef DEBUG
const char * abtCommandText;
#endif
} pn53x_command;
/*
#define PN531 0x01
#define PN532 0x02
#define PN533 0X04
*/
#ifndef DEBUG
# define PNCMD( X, Y ) { X , Y }
# define PNCMD_DBG( X ) do { \
} while(0)
#else
# define PNCMD( X, Y ) { X , Y, #X }
# define PNCMD_DBG( X ) do { \
for (size_t i=0; i<(sizeof(pn53x_commands)/sizeof(pn53x_command)); i++) { \
if ( X == pn53x_commands[i].ui8Code ) { \
DBG( "%s", pn53x_commands[i].abtCommandText ); \
break; \
} \
} \
} while(0)
#endif
static const pn53x_command pn53x_commands[] = {
// Miscellaneous
PNCMD( Diagnose, PN531|PN532|PN533 ),
PNCMD( GetFirmwareVersion, PN531|PN532|PN533 ),
PNCMD( GetGeneralStatus, PN531|PN532|PN533 ),
PNCMD( ReadRegister, PN531|PN532|PN533 ),
PNCMD( WriteRegister, PN531|PN532|PN533 ),
PNCMD( ReadGPIO, PN531|PN532|PN533 ),
PNCMD( WriteGPIO, PN531|PN532|PN533 ),
PNCMD( SetSerialBaudRate, PN531|PN532|PN533 ),
PNCMD( SetParameters, PN531|PN532|PN533 ),
PNCMD( SAMConfiguration, PN531|PN532 ),
PNCMD( PowerDown, PN531|PN532 ),
PNCMD( AlparCommandForTDA, PN533 ),
// RF communication
PNCMD( RFConfiguration, PN531|PN532|PN533 ),
PNCMD( RFRegulationTest, PN531|PN532|PN533 ),
// Initiator
PNCMD( InJumpForDEP, PN531|PN532|PN533 ),
PNCMD( InJumpForPSL, PN531|PN532|PN533 ),
PNCMD( InListPassiveTarget, PN531|PN532|PN533 ),
PNCMD( InATR, PN531|PN532|PN533 ),
PNCMD( InPSL, PN531|PN532|PN533 ),
PNCMD( InDataExchange, PN531|PN532|PN533 ),
PNCMD( InCommunicateThru, PN531|PN532|PN533 ),
PNCMD( InQuartetByteExchange, PN533 ),
PNCMD( InDeselect, PN531|PN532|PN533 ),
PNCMD( InRelease, PN531|PN532|PN533 ),
PNCMD( InSelect, PN531|PN532|PN533 ),
PNCMD( InAutoPoll, PN532 ),
PNCMD( InActivateDeactivatePaypass, PN533 ),
// Target
PNCMD( TgInitAsTarget, PN531|PN532|PN533 ),
PNCMD( TgSetGeneralBytes, PN531|PN532|PN533 ),
PNCMD( TgGetData, PN531|PN532|PN533 ),
PNCMD( TgSetData, PN531|PN532|PN533 ),
PNCMD( TgSetDataSecure, PN533 ),
PNCMD( TgSetMetaData, PN531|PN532|PN533 ),
PNCMD( TgSetMetaDataSecure, PN533 ),
PNCMD( TgGetInitiatorCommand, PN531|PN532|PN533 ),
PNCMD( TgResponseToInitiator, PN531|PN532|PN533 ),
PNCMD( TgGetTargetStatus, PN531|PN532|PN533 ),
};
#endif /* __PN53X_INTERNAL_H__ */

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,9 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2009, Roel Verdult, Romuald Conty
* Copyright (C) 2010, Roel Verdult, Romuald Conty, Romain Tartière
* Copyright (C) 2011, Romuald Conty, 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
@ -27,11 +29,6 @@
# include <nfc/nfc-types.h>
# define PN53x_NORMAL_FRAME_MAX_LEN 255
# define PN53x_NORMAL_FRAME_OVERHEAD 7
# define PN53x_EXTENDED_FRAME_MAX_LEN 264
# define PN53x_EXTENDED_FRAME_OVERHEAD 10
// Registers and symbols masks used to covers parts within a register
# define REG_CIU_TX_MODE 0x6302
# define SYMBOL_TX_CRC_ENABLE 0x80
@ -98,6 +95,24 @@
# define DEISERRFRAME 0x0300/* Error frame */
# define DENOTSUP 0x0400/* Not supported */
typedef enum {
PN531 = 0x01,
PN532 = 0x02,
PN533 = 0x04
} pn53x_type;
typedef enum {
SLEEP = 0x00, // Need to be wake up to process commands
NORMAL = 0x01, // Ready to process command
EXECUTE = 0x02, // Need to cancel the running command to process new one
} pn53x_state;
struct pn53x_data {
pn53x_type type;
pn53x_state state;
};
/* PN53x specific types */
/**
* @enum pn53x_modulation_t
@ -185,18 +200,17 @@ bool pn53x_check_ack_frame_callback (nfc_device_t * pnd, const byte_t * pbtRx
const size_t szRxFrameLen);
bool pn53x_check_error_frame_callback (nfc_device_t * pnd, const byte_t * pbtRxFrame,
const size_t szRxFrameLen);
bool pn53x_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, byte_t * pbtRx,
size_t * pszRx);
bool pn53x_get_reg (nfc_device_t * pnd, uint16_t ui16Reg, uint8_t * ui8Value);
bool pn53x_set_reg (nfc_device_t * pnd, uint16_t ui16Reg, uint8_t ui8SymbolMask, uint8_t ui8Value);
bool pn53x_set_parameter (nfc_device_t * pnd, const uint8_t ui8Value, const bool bEnable);
bool pn53x_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, byte_t * pbtRx, size_t *pszRx, bool toto);
bool pn53x_read_register (nfc_device_t * pnd, uint16_t ui16Reg, uint8_t * ui8Value);
bool pn53x_write_register (nfc_device_t * pnd, uint16_t ui16Reg, uint8_t ui8SymbolMask, uint8_t ui8Value);
bool pn53x_set_parameters (nfc_device_t * pnd, const uint8_t ui8Value, const bool bEnable);
bool pn53x_set_tx_bits (nfc_device_t * pnd, const uint8_t ui8Bits);
bool pn53x_wrap_frame (const byte_t * pbtTx, const size_t szTxBits, const byte_t * pbtTxPar, byte_t * pbtFrame,
size_t * pszFrameBits);
bool pn53x_unwrap_frame (const byte_t * pbtFrame, const size_t szFrameBits, byte_t * pbtRx, size_t * pszRxBits,
byte_t * pbtRxPar);
bool pn53x_decode_target_data (const byte_t * pbtRawData, size_t szRawData,
nfc_chip_t nc, nfc_modulation_type_t nmt,
pn53x_type chip_type, nfc_modulation_type_t nmt,
nfc_target_info_t * pnti);
bool pn53x_get_firmware_version (nfc_device_t * pnd, char abtFirmwareText[18]);
bool pn53x_configure (nfc_device_t * pnd, const nfc_device_option_t ndo, const bool bEnable);
@ -234,6 +248,7 @@ static const struct chip_callbacks pn53x_callbacks_list = {
// C wrappers for PN53x commands
bool pn53x_SetParameters (nfc_device_t * pnd, const uint8_t ui8Value);
bool pn53x_SAMConfiguration (nfc_device_t * pnd, const uint8_t ui8Mode);
bool pn53x_InListPassiveTarget (nfc_device_t * pnd, const pn53x_modulation_t pmInitModulation,
const byte_t szMaxTargets, const byte_t * pbtInitiatorData,
const size_t szInitiatorDataLen, byte_t * pbtTargetsData, size_t * pszTargetsData);

View file

@ -2,6 +2,7 @@
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, Roel Verdult
* Copyright (C) 2011, Romuald Conty, 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
@ -27,8 +28,6 @@
# include <nfc/nfc-types.h>
# include "chips/pn53x.h"
# if defined (DRIVER_ACR122_ENABLED)
# include "drivers/acr122.h"
# endif /* DRIVER_ACR122_ENABLED */
@ -54,46 +53,7 @@
# endif /* DRIVER_PN532_UART_ENABLED */
# define DRIVERS_MAX_DEVICES 16
# define MAX_FRAME_LEN 264
static const struct driver_callbacks drivers_callbacks_list[] = {
// Driver Name Chip callbacks Pick Device List Devices Connect Transceive Disconnect
# if defined (DRIVER_PN531_USB_ENABLED)
{PN531_USB_DRIVER_NAME, &pn53x_callbacks_list, pn531_usb_pick_device, pn531_usb_list_devices, pn531_usb_connect,
NULL, pn53x_usb_transceive, pn53x_usb_disconnect},
# endif /* DRIVER_PN531_USB_ENABLED */
# if defined (DRIVER_PN533_USB_ENABLED)
{PN533_USB_DRIVER_NAME, &pn53x_callbacks_list, pn533_usb_pick_device, pn533_usb_list_devices, pn533_usb_connect,
pn533_usb_init, pn53x_usb_transceive, pn53x_usb_disconnect},
# endif /* DRIVER_PN533_USB_ENABLED */
# if defined (DRIVER_ACR122_ENABLED)
{ACR122_DRIVER_NAME, &pn53x_callbacks_list, acr122_pick_device, acr122_list_devices, acr122_connect,
NULL, acr122_transceive, acr122_disconnect},
# endif /* DRIVER_ACR122_ENABLED */
# if defined (DRIVER_ARYGON_ENABLED)
{ARYGON_DRIVER_NAME, &pn53x_callbacks_list, arygon_pick_device, arygon_list_devices, arygon_connect,
NULL, arygon_transceive, arygon_disconnect},
# endif /* DRIVER_ARYGON_ENABLED */
# if defined (DRIVER_PN532_UART_ENABLED)
{PN532_UART_DRIVER_NAME, &pn53x_callbacks_list, pn532_uart_pick_device, pn532_uart_list_devices, pn532_uart_connect,
NULL, pn532_uart_transceive, pn532_uart_disconnect},
# endif /* DRIVER_PN532_UART_ENABLED */
};
# ifdef DEBUG
/*
* TODO Move this helper macro for dumping drivers messages.
* Here is not the best place for such a macro, however, I
* can't see any convenient place ATM.
*/
# define PRINT_HEX(pcTag, pbtData, szBytes) do { \
size_t __szPos; \
printf(" %s: ", pcTag); \
for (__szPos=0; __szPos < (size_t)(szBytes); __szPos++) { \
printf("%02x ",pbtData[__szPos]); \
} \
printf("\n"); \
} while (0);
# endif
extern const struct nfc_driver_t *nfc_drivers[];
#endif // __NFC_DRIVERS_H__

View file

@ -119,7 +119,7 @@ arygon_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t *
*pszDeviceFound = 0;
serial_port sp;
const char *pcPorts[] = DEFAULT_SERIAL_PORTS;
char **pcPorts = uart_list_ports ();
const char *pcPort;
int iDevice = 0;
@ -155,6 +155,7 @@ arygon_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t *
# endif
/* DEBUG */
}
free (pcPorts);
#endif /* SERIAL_AUTOPROBE_ENABLED */
return true;
}

View file

@ -1,7 +1,8 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, Roel Verdult
* Copyright (C) 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2011, Romuald Conty, 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
@ -22,11 +23,13 @@
* @brief PN532 driver using UART bus (UART, RS232, etc.)
*/
/* vim: set ts=2 sw=2 et: */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif // HAVE_CONFIG_H
#include "../drivers.h"
#include "libnfc/drivers.h"
#include <stdio.h>
#include <string.h>
@ -36,44 +39,26 @@
#include <nfc/nfc.h>
#include <nfc/nfc-messages.h>
// Bus
#include "uart.h"
#include "libnfc/nfc-internal.h"
#include "libnfc/chips/pn53x.h"
#include "libnfc/chips/pn53x-internal.h"
#define SERIAL_DEFAULT_PORT_SPEED 115200
// TODO Move this one level up for libnfc-1.6
static const byte_t ack_frame[] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 };
void pn532_uart_ack (const nfc_device_spec_t nds);
void pn532_uart_wakeup (const nfc_device_spec_t nds);
bool pn532_uart_check_communication (const nfc_device_spec_t nds, bool * success);
void pn532_uart_ack (nfc_device_t * pnd);
// void pn532_uart_wakeup (const nfc_device_spec_t nds);
bool pn532_uart_check_communication (nfc_device_t *pnd);
nfc_device_desc_t *
pn532_uart_pick_device (void)
{
nfc_device_desc_t *pndd;
if ((pndd = malloc (sizeof (*pndd)))) {
size_t szN;
if (!pn532_uart_list_devices (pndd, 1, &szN)) {
DBG ("%s", "pn532_uart_list_devices failed");
free (pndd);
return NULL;
}
if (szN == 0) {
DBG ("%s", "No device found");
free (pndd);
return NULL;
}
}
return pndd;
}
struct pn532_uart_data {
serial_port port;
};
bool
pn532_uart_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound)
pn532_uart_probe (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound)
{
/** @note: Due to UART bus we can't know if its really a pn532 without
* sending some PN53x commands. But using this way to probe devices, we can
@ -88,7 +73,7 @@ pn532_uart_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size
*pszDeviceFound = 0;
serial_port sp;
const char *pcPorts[] = DEFAULT_SERIAL_PORTS;
char **pcPorts = uart_list_ports ();
const char *pcPort;
int iDevice = 0;
@ -97,15 +82,24 @@ pn532_uart_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size
DBG ("Trying to find PN532 device on serial port: %s at %d bauds.", pcPort, SERIAL_DEFAULT_PORT_SPEED);
if ((sp != INVALID_SERIAL_PORT) && (sp != CLAIMED_SERIAL_PORT)) {
bool bComOk;
// Serial port claimed but we need to check if a PN532_UART is connected.
uart_set_speed (sp, SERIAL_DEFAULT_PORT_SPEED);
nfc_device_t nd;
nd.driver = &pn532_uart_driver;
nd.driver_data = malloc(sizeof(struct pn532_uart_data));
((struct pn532_uart_data*)(nd.driver_data))->port = sp;
nd.chip_data = malloc(sizeof(struct pn53x_data));
((struct pn53x_data*)(nd.chip_data))->type = PN532;
((struct pn53x_data*)(nd.chip_data))->state = SLEEP;
// PN532 could be powered down, we need to wake it up before line testing.
pn532_uart_wakeup ((nfc_device_spec_t) sp);
// TODO pn532_uart_wakeup ((nfc_device_spec_t) sp);
// Check communication using "Diagnose" command, with "Communication test" (0x00)
if (!pn532_uart_check_communication ((nfc_device_spec_t) sp, &bComOk))
continue;
if (!bComOk)
bool res = pn532_uart_check_communication (&nd);
free(nd.driver_data);
free(nd.chip_data);
if(!res)
continue;
uart_close (sp);
@ -129,6 +123,7 @@ pn532_uart_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size
# endif
/* DEBUG */
}
free (pcPorts);
#endif /* SERIAL_AUTOPROBE_ENABLED */
return true;
}
@ -138,7 +133,6 @@ pn532_uart_connect (const nfc_device_desc_t * pndd)
{
serial_port sp;
nfc_device_t *pnd = NULL;
bool bComOk;
DBG ("Attempt to connect to: %s at %d bauds.", pndd->pcPort, pndd->uiSpeed);
sp = uart_open (pndd->pcPort);
@ -152,24 +146,23 @@ pn532_uart_connect (const nfc_device_desc_t * pndd)
uart_set_speed (sp, pndd->uiSpeed);
// PN532 could be powered down, we need to wake it up before line testing.
pn532_uart_wakeup ((nfc_device_spec_t) sp);
// Check communication using "Diagnose" command, with "Communication test" (0x00)
if (!pn532_uart_check_communication ((nfc_device_spec_t) sp, &bComOk))
return NULL;
if (!bComOk)
return NULL;
DBG ("Successfully connected to: %s", pndd->pcPort);
// We have a connection
pnd = malloc (sizeof (nfc_device_t));
strncpy (pnd->acName, pndd->acDevice, DEVICE_NAME_LENGTH - 1);
pnd->acName[DEVICE_NAME_LENGTH - 1] = '\0';
pnd->nc = NC_PN532;
pnd->nds = (nfc_device_spec_t) sp;
pnd->bActive = true;
pnd->driver_data = malloc(sizeof(struct pn532_uart_data));
((struct pn532_uart_data*)(pnd->driver_data))->port = sp;
pnd->chip_data = malloc(sizeof(struct pn53x_data));
((struct pn53x_data*)(pnd->chip_data))->type = PN532;
((struct pn53x_data*)(pnd->chip_data))->state = SLEEP;
pnd->driver = &pn532_uart_driver;
// Check communication using "Diagnose" command, with "Communication test" (0x00)
if (!pn532_uart_check_communication (pnd))
return NULL;
DBG ("Successfully connected to: %s", pndd->pcPort);
return pnd;
}
@ -177,196 +170,220 @@ pn532_uart_connect (const nfc_device_desc_t * pndd)
void
pn532_uart_disconnect (nfc_device_t * pnd)
{
uart_close ((serial_port) pnd->nds);
uart_close (((struct pn532_uart_data*)(pnd->driver_data))->port);
free (pnd->driver_data);
free (pnd->chip_data);
free (pnd);
}
#define TX_BUFFER_LEN (256)
#define RX_BUFFER_LEN (PN53x_EXTENDED_FRAME_MAX_LEN + PN53x_EXTENDED_FRAME_OVERHEAD)
#define PN532_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD)
bool
pn532_uart_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, byte_t * pbtRx,
size_t * pszRx)
pn532_uart_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szData)
{
byte_t abtTxBuf[TX_BUFFER_LEN] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff"
byte_t abtRxBuf[RX_BUFFER_LEN];
size_t szRxBufLen = MIN( RX_BUFFER_LEN, *pszRx );
size_t szPos;
int res;
// Packet length = data length (len) + checksum (1) + end of stream marker (1)
abtTxBuf[3] = szTx;
// Packet length checksum
abtTxBuf[4] = 256 - abtTxBuf[3];
// Copy the PN53X command into the packet buffer
memmove (abtTxBuf + 5, pbtTx, szTx);
// Calculate data payload checksum
abtTxBuf[szTx + 5] = 0;
for (szPos = 0; szPos < szTx; szPos++) {
abtTxBuf[szTx + 5] -= abtTxBuf[szPos + 5];
DBG ("state: %d (SLEEP: %d, NORMAL: %d, EXECUTE: %d)", ((struct pn53x_data*)(pnd->chip_data))->state, SLEEP, NORMAL, EXECUTE);
if (((struct pn53x_data*)(pnd->chip_data))->state == SLEEP) {
// if (1) {
/** PN532C106 wakeup. */
/** High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for PN532 being wakeup. */
const byte_t pn532_wakeup_preamble[] = { 0x55, 0x55, 0x00, 0x00, 0x00 };
//, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uart_send (((struct pn532_uart_data*)(pnd->driver_data))->port, pn532_wakeup_preamble, sizeof (pn532_wakeup_preamble));
((struct pn53x_data*)(pnd->chip_data))->state = NORMAL; // PN532 should now be awake
// 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 (!pn53x_SAMConfiguration (pnd, 0x01)) {
return false;
}
}
// End of stream marker
abtTxBuf[szTx + 6] = 0;
byte_t abtTxBuf[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff"
#ifdef DEBUG
PRINT_HEX ("TX", abtTxBuf, szTx + 7);
#endif
res = uart_send ((serial_port) pnd->nds, abtTxBuf, szTx + 7);
pnd->iLastCommand = pbtData[0];
size_t szFrame = 0;
if (szData <= PN53x_NORMAL_FRAME__DATA_MAX_LEN) {
// LEN - Packet length = data length (len) + checksum (1) + end of stream marker (1)
abtTxBuf[3] = szData + 1;
// LCS - Packet length checksum
abtTxBuf[4] = 256 - (szData + 1);
// TFI
abtTxBuf[5] = 0xD4;
// DATA - Copy the PN53X command into the packet buffer
memcpy (abtTxBuf + 6, pbtData, szData);
// DCS - Calculate data payload checksum
byte_t btDCS = (256 - 0xD4);
for (size_t szPos = 0; szPos < szData; szPos++) {
btDCS -= pbtData[szPos];
}
abtTxBuf[6 + szData] = btDCS;
// 0x00 - End of stream marker
abtTxBuf[szData + 7] = 0x00;
szFrame = szData + PN53x_NORMAL_FRAME__OVERHEAD;
} else {
// FIXME: Build extended frame
abort();
}
int res = uart_send (((struct pn532_uart_data*)(pnd->driver_data))->port, abtTxBuf, szFrame);
if (res != 0) {
ERR ("%s", "Unable to transmit data. (TX)");
pnd->iLastError = res;
return false;
}
res = uart_receive ((serial_port) pnd->nds, abtRxBuf, &szRxBufLen);
byte_t abtRxBuf[6];
res = uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, abtRxBuf, 6);
if (res != 0) {
ERR ("%s", "Unable to read ACK");
pnd->iLastError = res;
return false;
}
if (pn53x_check_ack_frame_callback (pnd, abtRxBuf, sizeof(abtRxBuf))) {
((struct pn53x_data*)(pnd->chip_data))->state = EXECUTE;
} else {
return false;
}
return true;
}
int
pn532_uart_receive (nfc_device_t * pnd, byte_t * pbtData, const size_t szDataLen)
{
byte_t abtRxBuf[5];
size_t len;
int res = uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, abtRxBuf, 5);
if (res != 0) {
ERR ("%s", "Unable to receive data. (RX)");
pnd->iLastError = res;
return false;
}
#ifdef DEBUG
PRINT_HEX ("RX", abtRxBuf, szRxBufLen);
#endif
// WARN: UART is a per byte reception, so you usually receive ACK and next frame the same time
if (!pn53x_check_ack_frame_callback (pnd, abtRxBuf, szRxBufLen))
return false;
szRxBufLen -= sizeof (ack_frame);
memmove (abtRxBuf, abtRxBuf + sizeof (ack_frame), szRxBufLen);
if (szRxBufLen == 0) {
szRxBufLen = RX_BUFFER_LEN;
do {
delay_ms (10);
res = uart_receive ((serial_port) pnd->nds, abtRxBuf, &szRxBufLen);
} while (res != 0);
#ifdef DEBUG
PRINT_HEX ("RX", abtRxBuf, szRxBufLen);
#endif
return -1;
}
#ifdef DEBUG
PRINT_HEX ("TX", ack_frame, sizeof(ack_frame));
#endif
res = uart_send ((serial_port) pnd->nds, ack_frame, sizeof(ack_frame));
if (res != 0) {
ERR ("%s", "Unable to transmit data. (TX)");
pnd->iLastError = res;
return false;
const byte_t pn53x_preamble[3] = { 0x00, 0x00, 0xff };
if (0 != (memcmp (abtRxBuf, pn53x_preamble, 3))) {
ERR ("%s", "Frame preamble+start code mismatch");
pnd->iLastError = DEIO;
return -1;
}
if (!pn53x_check_error_frame_callback (pnd, abtRxBuf, szRxBufLen))
return false;
// When the answer should be ignored, just return a successful result
if (pbtRx == NULL || pszRx == NULL)
return true;
// Only succeed when the result is at least 00 00 FF xx Fx Dx xx .. .. .. xx 00 (x = variable)
if (szRxBufLen < 9) {
pnd->iLastError = DEINVAL;
return false;
}
// Remove the preceding and appending bytes 00 00 ff 00 ff 00 00 00 FF xx Fx .. .. .. xx 00 (x = variable)
*pszRx = szRxBufLen - 9;
memcpy (pbtRx, abtRxBuf + 7, *pszRx);
return true;
}
void
pn532_uart_ack (const nfc_device_spec_t nds)
{
#ifdef DEBUG
PRINT_HEX ("TX", ack_frame, sizeof (ack_frame));
#endif
uart_send ((serial_port) nds, ack_frame, sizeof (ack_frame));
}
bool
pn532_uart_wait_for_ack(const nfc_device_spec_t nds)
{
byte_t abtRx[RX_BUFFER_LEN];
size_t szRx = sizeof(ack_frame);
if (0 == uart_receive ((serial_port) nds, abtRx, &szRx)) {
#ifdef DEBUG
PRINT_HEX ("RX", abtRx, szRx);
#endif
if ((0x01 == abtRxBuf[3]) && (0xff == abtRxBuf[4])) {
// Error frame
uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, abtRxBuf, 3);
ERR ("%s", "Application level error detected");
pnd->iLastError = DEISERRFRAME;
return -1;
} else if ((0xff == abtRxBuf[3]) && (0xff == abtRxBuf[4])) {
// Extended frame
// FIXME: Code this
abort ();
} else {
ERR ("No ACK.");
return false;
}
if (0 != memcmp (ack_frame, abtRx, szRx))
return false;
return true;
}
// Normal frame
if (256 != (abtRxBuf[3] + abtRxBuf[4])) {
// TODO: Retry
ERR ("%s", "Length checksum mismatch");
pnd->iLastError = DEIO;
return -1;
}
#define PN53X_RX_OVERHEAD 6
void
pn532_uart_wakeup (const nfc_device_spec_t nds)
{
byte_t abtRx[RX_BUFFER_LEN];
size_t szRx = PN53x_NORMAL_FRAME_OVERHEAD + 2;
/** PN532C106 wakeup. */
/** High Speed Unit (HSU) wake up consist to send 0x55 and wait a "long" delay for PN532 being wakeup. */
/** After the preamble we request the PN532C106 chip to switch to "normal" mode (SAM is not used) */
const byte_t pncmd_pn532c106_wakeup_preamble[] =
{ 0x55, 0x55, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0x03, 0xfd, 0xd4, 0x14, 0x01, 0x17, 0x00 }; // Here we send a SAMConfiguration command (Normal mode, the SAM is not used; this is the default mode)
#ifdef DEBUG
PRINT_HEX ("TX", pncmd_pn532c106_wakeup_preamble, sizeof (pncmd_pn532c106_wakeup_preamble));
#endif
uart_send ((serial_port) nds, pncmd_pn532c106_wakeup_preamble, sizeof (pncmd_pn532c106_wakeup_preamble));
pn532_uart_wait_for_ack(nds);
if (0 == uart_receive ((serial_port) nds, abtRx, &szRx)) {
#ifdef DEBUG
PRINT_HEX ("RX", abtRx, szRx);
#endif
} else {
ERR ("Unable to wakeup the PN532.");
}
}
bool
pn532_uart_check_communication (const nfc_device_spec_t nds, bool * success)
{
byte_t abtRx[RX_BUFFER_LEN];
const byte_t attempted_result[] =
{ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x09, 0xf7, 0xD5, 0x01, 0x00, 'l', 'i', 'b', 'n', 'f', 'c',
0xbc, 0x00 };
size_t szRx = sizeof(attempted_result);
int res;
/** To be sure that PN532 is alive, we have put a "Diagnose" command to execute a "Communication Line Test" */
const byte_t pncmd_communication_test[] =
{ 0x00, 0x00, 0xff, 0x09, 0xf7, 0xd4, 0x00, 0x00, 'l', 'i', 'b', 'n', 'f', 'c', 0xbe, 0x00 };
*success = false;
#ifdef DEBUG
PRINT_HEX ("TX", pncmd_communication_test, sizeof (pncmd_communication_test));
#endif
res = uart_send ((serial_port) nds, pncmd_communication_test, sizeof (pncmd_communication_test));
if (res != 0) {
ERR ("%s", "Unable to transmit data. (TX)");
return false;
// abtRxBuf[3] (LEN) include TFI + (CC+1)
len = abtRxBuf[3] - 2;
}
res = uart_receive ((serial_port) nds, abtRx, &szRx);
if (len > szDataLen) {
ERR ("Unable to receive data: buffer too small. (szDataLen: %zu, len: %zu)", szDataLen, len);
pnd->iLastError = DEIO;
return -1;
}
// TFI + PD0 (CC+1)
res = uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, abtRxBuf, 2);
if (res != 0) {
ERR ("%s", "Unable to receive data. (RX)");
return false;
pnd->iLastError = res;
return -1;
}
#ifdef DEBUG
PRINT_HEX ("RX", abtRx, szRx);
#endif
if (0 == memcmp (abtRx, attempted_result, sizeof (attempted_result)))
*success = true;
if (abtRxBuf[0] != 0xD5) {
ERR ("%s", "TFI Mismatch");
pnd->iLastError = DEIO;
return -1;
}
return true;
if (abtRxBuf[1] != pnd->iLastCommand + 1) {
ERR ("%s", "Command Code verification failed");
pnd->iLastError = DEIO;
return -1;
}
if (len) {
res = uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, pbtData, len);
if (res != 0) {
ERR ("%s", "Unable to receive data. (RX)");
pnd->iLastError = res;
return -1;
}
}
res = uart_receive (((struct pn532_uart_data*)(pnd->driver_data))->port, abtRxBuf, 2);
if (res != 0) {
ERR ("%s", "Unable to receive data. (RX)");
pnd->iLastError = res;
return -1;
}
byte_t btDCS = (256 - 0xD5);
btDCS -= pnd->iLastCommand + 1;
for (size_t szPos = 0; szPos < len; szPos++) {
btDCS -= pbtData[szPos];
}
if (btDCS != abtRxBuf[0]) {
ERR ("%s", "Data checksum mismatch");
pnd->iLastError = DEIO;
return -1;
}
if (0x00 != abtRxBuf[1]) {
ERR ("%s", "Frame postamble mismatch");
pnd->iLastError = DEIO;
return -1;
}
((struct pn53x_data*)(pnd->chip_data))->state = NORMAL;
return len;
}
void
pn532_uart_ack (nfc_device_t * pnd)
{
uart_send (((struct pn532_uart_data*)(pnd->driver_data))->port, ack_frame, sizeof (ack_frame));
}
bool
pn532_uart_check_communication (nfc_device_t *pnd)
{
const byte_t abtMsg[] = { Diagnose, 0x00, 'l', 'i', 'b', 'n', 'f', 'c' };
if (!pn532_uart_send (pnd, abtMsg, sizeof (abtMsg)))
return false;
const byte_t abtExpectedRes[] = { 0x00, 'l', 'i', 'b', 'n', 'f', 'c' };
byte_t abtRes[sizeof(abtExpectedRes)];
int res;
if ((res = pn532_uart_receive (pnd, abtRes, sizeof(abtRes))) < 0)
return false;
return ((sizeof(abtRes) == res) && (0 == memcmp (abtRes, abtExpectedRes, sizeof(abtExpectedRes))));
}
const struct nfc_driver_t pn532_uart_driver = {
.name = PN532_UART_DRIVER_NAME,
.probe = pn532_uart_probe,
.connect = pn532_uart_connect,
.send = pn532_uart_send,
.receive = pn532_uart_receive,
.disconnect = pn532_uart_disconnect,
.strerror = pn53x_strerror,
};

View file

@ -1,7 +1,8 @@
/**
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2009, Roel Verdult
* Copyright (C) 2010, Roel Verdult, Romuald Conty
* Copyright (C) 2011, Romuald Conty, 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
@ -30,13 +31,15 @@
// Functions used by developer to handle connection to this device
nfc_device_desc_t *pn532_uart_pick_device (void);
bool pn532_uart_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound);
bool pn532_uart_probe (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound);
nfc_device_t *pn532_uart_connect (const nfc_device_desc_t * pndd);
void pn532_uart_disconnect (nfc_device_t * pnd);
// Callback function used by libnfc to transmit commands to the PN53X chip
bool pn532_uart_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, byte_t * pbtRx,
size_t * pszRx);
bool pn532_uart_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szData);
int pn532_uart_receive (nfc_device_t * pnd, byte_t * pbtData, const size_t szDataLen);
extern const struct nfc_driver_t pn532_uart_driver;
#endif // ! __NFC_DRIVER_PN532_UART_H__

56
libnfc/nfc-internal.h Normal file
View file

@ -0,0 +1,56 @@
/*-
* Public platform independent Near Field Communication (NFC) library
*
* Copyright (C) 2011, Romain Tartière, 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 nfc-internal.h
* @brief Internal defines and macros
*/
#ifndef __NFC_INTERNAL_H__
# define __NFC_INTERNAL_H__
# ifdef DEBUG
//# if 1
# define PRINT_HEX(pcTag, pbtData, szBytes) do { \
size_t __szPos; \
printf(" %s: ", pcTag); \
for (__szPos=0; __szPos < (size_t)(szBytes); __szPos++) { \
printf("%02x ",pbtData[__szPos]); \
} \
printf("\n"); \
} while (0);
# else
# define PRINT_HEX(pcTag, pbtData, szBytes) do { \
(void) pcTag; \
(void) pbtData; \
(void) szBytes; \
} while (0);
# endif
struct nfc_driver_t {
const char *name;
bool (*probe)(nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound);
nfc_device_t * (*connect)(const nfc_device_desc_t * pndd);
bool (*send)(nfc_device_t * pnd, const byte_t * pbtData, const size_t szData);
int (*receive)(nfc_device_t * pnd, byte_t * pbtData, const size_t szDataLen);
void (*disconnect)(nfc_device_t * pnd);
const char *(*strerror)(const nfc_device_t * pnd);
};
#endif // __NFC_INTERNAL_H__

View file

@ -3,6 +3,7 @@
*
* Copyright (C) 2009, Roel Verdult, Romuald Conty
* Copyright (C) 2010, Roel Verdult, Romuald Conty, Romain Tartière
* Copyright (C) 2011, Romuald Conty, 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
@ -23,6 +24,8 @@
* @brief NFC library implementation
*/
/* vim:set ts=2 sw=2 et: */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif // HAVE_CONFIG_H
@ -40,11 +43,19 @@
#include "chips.h"
#include "drivers.h"
#include "nfc-internal.h"
#include <nfc/nfc-messages.h>
nfc_device_desc_t *nfc_pick_device (void);
const struct nfc_driver_t *nfc_drivers[] = {
# if defined (DRIVER_PN532_UART_ENABLED)
&pn532_uart_driver,
# endif /* DRIVER_PN532_UART_ENABLED */
NULL
};
/**
* @brief Connect to a NFC device
* @param pndd device description if specific device is wanted, \c NULL otherwise
@ -71,50 +82,32 @@ nfc_device_t *
nfc_connect (nfc_device_desc_t * pndd)
{
nfc_device_t *pnd = NULL;
uint32_t uiDriver;
if (pndd == NULL)
pndd = nfc_pick_device ();
if (pndd == NULL)
return NULL;
// Search through the device list for an available device
for (uiDriver = 0; uiDriver < sizeof (drivers_callbacks_list) / sizeof (drivers_callbacks_list[0]); uiDriver++) {
if (pndd == NULL) {
// No device description specified: try to automatically claim a device
if (drivers_callbacks_list[uiDriver].pick_device != NULL) {
DBG ("Autodetecting available devices using %s driver.", drivers_callbacks_list[uiDriver].acDriver);
pndd = drivers_callbacks_list[uiDriver].pick_device ();
if (pndd != NULL) {
DBG ("Auto-connecting to %s using %s driver", pndd->acDevice, drivers_callbacks_list[uiDriver].acDriver);
pnd = drivers_callbacks_list[uiDriver].connect (pndd);
if (pnd == NULL) {
DBG ("No device available using %s driver", drivers_callbacks_list[uiDriver].acDriver);
pndd = NULL;
}
free (pndd);
}
}
const struct nfc_driver_t *ndr;
const struct nfc_driver_t **pndr = nfc_drivers;
while ((ndr = *pndr)) {
// Specific device is requested: using device description pndd
if (0 != strcmp (ndr->name, pndd->pcDriver)) {
continue;
} else {
// Specific device is requested: using device description pndd
if (0 != strcmp (drivers_callbacks_list[uiDriver].acDriver, pndd->pcDriver)) {
continue;
} else {
pnd = drivers_callbacks_list[uiDriver].connect (pndd);
}
pnd = ndr->connect (pndd);
}
// Test if the connection was successful
if (pnd != NULL) {
DBG ("[%s] has been claimed.", pnd->acName);
// Great we have claimed a device
pnd->pdc = &(drivers_callbacks_list[uiDriver]);
// TODO: Put this pn53x related in driver_init()
if (!pn53x_init (pnd))
return NULL;
if (pnd->pdc->init) {
pnd->pdc->init (pnd);
}
// Set default configuration options
// Make sure we reset the CRC and parity to chip handling.
if (!nfc_configure (pnd, NDO_HANDLE_CRC, true))
@ -144,8 +137,9 @@ nfc_connect (nfc_device_desc_t * pndd)
return pnd;
} else {
DBG ("No device found using driver: %s", drivers_callbacks_list[uiDriver].acDriver);
DBG ("No device found using driver: %s", ndr->name);
}
pndr++;
}
// Too bad, no reader is ready to be claimed
return NULL;
@ -166,7 +160,7 @@ nfc_disconnect (nfc_device_t * pnd)
// Disable RF field to avoid heating
nfc_configure (pnd, NDO_ACTIVATE_FIELD, false);
// Disconnect, clean up and release the device
pnd->pdc->disconnect (pnd);
pnd->driver->disconnect (pnd);
}
}
@ -177,17 +171,31 @@ nfc_disconnect (nfc_device_t * pnd)
nfc_device_desc_t *
nfc_pick_device (void)
{
uint32_t uiDriver;
nfc_device_desc_t *nddRes;
const struct nfc_driver_t *ndr;
const struct nfc_driver_t **pndr = nfc_drivers;
while ((ndr = *pndr)) {
nfc_device_desc_t *pndd;
for (uiDriver = 0; uiDriver < sizeof (drivers_callbacks_list) / sizeof (drivers_callbacks_list[0]); uiDriver++) {
if (drivers_callbacks_list[uiDriver].pick_device != NULL) {
nddRes = drivers_callbacks_list[uiDriver].pick_device ();
if (nddRes != NULL)
return nddRes;
if ((pndd = malloc (sizeof (*pndd)))) {
size_t szN;
if (!ndr->probe (pndd, 1, &szN)) {
DBG ("%s probe failed", ndr->name);
free (pndd);
return NULL;
}
if (szN == 0) {
DBG ("No %s device found", ndr->name);
free (pndd);
} else {
return pndd;
}
}
pndr++;
}
DBG ("%s", "No device found with any driver :-(");
return NULL;
}
@ -200,22 +208,19 @@ nfc_pick_device (void)
void
nfc_list_devices (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDeviceFound)
{
uint32_t uiDriver;
size_t szN;
*pszDeviceFound = 0;
for (uiDriver = 0; uiDriver < sizeof (drivers_callbacks_list) / sizeof (drivers_callbacks_list[0]); uiDriver++) {
if (drivers_callbacks_list[uiDriver].list_devices != NULL) {
szN = 0;
if (drivers_callbacks_list[uiDriver].list_devices
(pnddDevices + (*pszDeviceFound), szDevices - (*pszDeviceFound), &szN)) {
*pszDeviceFound += szN;
DBG ("%ld device(s) found using %s driver", (unsigned long) szN, drivers_callbacks_list[uiDriver].acDriver);
}
} else {
DBG ("No listing function avaible for %s driver", drivers_callbacks_list[uiDriver].acDriver);
const struct nfc_driver_t *ndr;
const struct nfc_driver_t **pndr = nfc_drivers;
while ((ndr = *pndr)) {
szN = 0;
if (ndr->probe (pnddDevices + (*pszDeviceFound), szDevices - (*pszDeviceFound), &szN)) {
*pszDeviceFound += szN;
DBG ("%ld device(s) found using %s driver", (unsigned long) szN, ndr->name);
}
pndr++;
}
}
@ -253,16 +258,12 @@ nfc_initiator_init (nfc_device_t * pnd)
{
pnd->iLastError = 0;
// Make sure we are dealing with a active device
if (!pnd->bActive)
return false;
// Set the PN53X to force 100% ASK Modified miller decoding (default for 14443A cards)
if (!pn53x_set_reg (pnd, REG_CIU_TX_AUTO, SYMBOL_FORCE_100_ASK, 0x40))
if (!pn53x_write_register (pnd, REG_CIU_TX_AUTO, SYMBOL_FORCE_100_ASK, 0x40))
return false;
// Configure the PN53X to be an Initiator or Reader/Writer
if (!pn53x_set_reg (pnd, REG_CIU_CONTROL, SYMBOL_INITIATOR, 0x10))
if (!pn53x_write_register (pnd, REG_CIU_CONTROL, SYMBOL_INITIATOR, 0x10))
return false;
return true;
@ -294,14 +295,11 @@ nfc_initiator_select_passive_target (nfc_device_t * pnd,
const byte_t * pbtInitData, const size_t szInitData,
nfc_target_t * pnt)
{
byte_t abtInit[MAX_FRAME_LEN];
byte_t abtInit[MAX(12, szInitData)];
size_t szInit;
pnd->iLastError = 0;
// Make sure we are dealing with a active device
if (!pnd->bActive)
return false;
// TODO Put this in a function: this part is defined by ISO14443-3 (UID and Cascade levels)
switch (nm.nmt) {
case NMT_ISO14443A:
@ -662,7 +660,7 @@ nfc_target_receive_bits (nfc_device_t * pnd, byte_t * pbtRx, size_t * pszRxBits,
const char *
nfc_strerror (const nfc_device_t * pnd)
{
return pnd->pdc->pcc->strerror (pnd);
return pnd->driver->strerror (pnd);
}
/**

View file

@ -1,13 +1,11 @@
#include <cutter.h>
#include <nfc/nfc.h>
#include "../libnfc/chips/pn53x.h"
#include "libnfc/chips/pn53x.h"
#define MAX_DEVICE_COUNT 1
#define MAX_TARGET_COUNT 1
bool pn53x_get_reg(nfc_device_t* pnd, uint16_t ui16Reg, uint8_t* ui8Value);
void
test_register_endianness (void)
{
@ -27,21 +25,21 @@ test_register_endianness (void)
uint8_t value;
/* Set a 0xAA test value in writable register memory to test register access */
res = pn53x_set_reg (device, REG_CIU_TX_MODE, 0xFF, 0xAA);
cut_assert_true (res, cut_message ("set a register value to 0xAA"));
res = pn53x_write_register (device, REG_CIU_TX_MODE, 0xFF, 0xAA);
cut_assert_true (res, cut_message ("write register value to 0xAA"));
/* Get test value from register memory */
res = pn53x_get_reg (device, REG_CIU_TX_MODE, &value);
cut_assert_true (res, cut_message ("get register value"));
res = pn53x_read_register (device, REG_CIU_TX_MODE, &value);
cut_assert_true (res, cut_message ("read register value"));
cut_assert_equal_uint (0xAA, value, cut_message ("check register value"));
/* Set a 0x55 test value in writable register memory to test register access */
res = pn53x_set_reg (device, REG_CIU_TX_MODE, 0xFF, 0x55);
cut_assert_true (res, cut_message ("set a register value to 0x55"));
res = pn53x_write_register (device, REG_CIU_TX_MODE, 0xFF, 0x55);
cut_assert_true (res, cut_message ("write register value to 0x55"));
/* Get test value from register memory */
res = pn53x_get_reg (device, REG_CIU_TX_MODE, &value);
cut_assert_true (res, cut_message ("get register value"));
res = pn53x_read_register (device, REG_CIU_TX_MODE, &value);
cut_assert_true (res, cut_message ("read register value"));
cut_assert_equal_uint (0x55, value, cut_message ("check register value"));
nfc_disconnect (device);

View file

@ -5,7 +5,7 @@
#define MAX_DEVICE_COUNT 1
#define MAX_TARGET_COUNT 1
bool pn53x_get_reg(nfc_device_t* pnd, uint16_t ui16Reg, uint8_t* ui8Value);
#include "libnfc/chips/pn53x.h"
void
test_register_endianness (void)
@ -26,11 +26,11 @@ test_register_endianness (void)
uint8_t value;
/* Read valid XRAM memory */
res = pn53x_get_reg (device, 0xF0FF, &value);
res = pn53x_read_register (device, 0xF0FF, &value);
cut_assert_true (res, cut_message ("read register 0xF0FF"));
/* Read invalid SFR register */
res = pn53x_get_reg (device, 0xFFF0, &value);
res = pn53x_read_register (device, 0xFFF0, &value);
cut_assert_false (res, cut_message ("read register 0xFFF0"));
nfc_disconnect (device);