drivers: pn532_i2c: Respect proper timing specifications
The pn532 user manual states that after a i2c stop condition and before a i2c start condition there should be a delay of minimally 1.3 milliseconds. This is probably a limitation of the i2c peripheral or the firmware. In any case, each i2c_read and i2c_write creates the packets which are complemented with start/stop markers. It is thus required to take care of timing in these two functions. We solve this by wrapping the lower i2c_read and i2c_write functions for the pn532, as this requirement is not for all chips. Currently, we keep time using local variable, and thus the code is not thread-safe. With libnfc being single threaded and only one instances of libnfc can open a bus anyway, this is not yet a problem. Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
This commit is contained in:
parent
b953002f8f
commit
8f8f780c2b
1 changed files with 67 additions and 14 deletions
|
@ -67,14 +67,6 @@ struct pn532_i2c_data {
|
||||||
volatile bool abort_flag;
|
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
|
|
||||||
};
|
|
||||||
|
|
||||||
/* preamble and start bytes, see pn532-internal.h for details */
|
/* preamble and start bytes, see pn532-internal.h for details */
|
||||||
const uint8_t pn53x_preamble_and_start[] = { 0x00, 0x00, 0xff };
|
const uint8_t pn53x_preamble_and_start[] = { 0x00, 0x00, 0xff };
|
||||||
#define PN53X_PREAMBLE_AND_START_LEN (sizeof(pn53x_preamble_and_start) / sizeof(pn53x_preamble_and_start[0]))
|
#define PN53X_PREAMBLE_AND_START_LEN (sizeof(pn53x_preamble_and_start) / sizeof(pn53x_preamble_and_start[0]))
|
||||||
|
@ -100,6 +92,70 @@ static size_t pn532_i2c_scan(const nfc_context *context, nfc_connstring connstri
|
||||||
|
|
||||||
#define DRIVER_DATA(pnd) ((struct pn532_i2c_data*)(pnd->driver_data))
|
#define DRIVER_DATA(pnd) ((struct pn532_i2c_data*)(pnd->driver_data))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bus free time (in ms) between a STOP condition and START condition. See
|
||||||
|
* tBuf in the PN532 data sheet, section 12.25: Timing for the I2C interface,
|
||||||
|
* table 320. I2C timing specification, page 211, rev. 3.2 - 2007-12-07.
|
||||||
|
*/
|
||||||
|
#define PN532_BUS_FREE_TIME 1.3
|
||||||
|
static struct timespec __transaction_stop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wrapper around i2c_read to ensure proper timing by respecting the
|
||||||
|
* minimal free bus time between a STOP condition and a START condition.
|
||||||
|
*
|
||||||
|
* @note This is not thread safe, but since libnfc is single threaded
|
||||||
|
* this should be okay.
|
||||||
|
*
|
||||||
|
* @param id I2C device
|
||||||
|
* @param buf pointer on buffer used to store data
|
||||||
|
* @param len length of the buffer
|
||||||
|
* @return length (in bytes) of read data, or driver error code (negative value)
|
||||||
|
*/
|
||||||
|
static ssize_t pn532_i2c_read(const i2c_device id,
|
||||||
|
uint8_t *buf, const size_t len)
|
||||||
|
{
|
||||||
|
struct timespec transaction_start, bus_free_time = { 0 };
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &transaction_start);
|
||||||
|
bus_free_time.tv_nsec = (PN532_BUS_FREE_TIME * 1000 * 1000) -
|
||||||
|
(transaction_start.tv_nsec - __transaction_stop.tv_nsec);
|
||||||
|
nanosleep(&bus_free_time, NULL);
|
||||||
|
|
||||||
|
ret = i2c_read(id, buf, len);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &__transaction_stop);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wrapper around i2c_write to ensure proper timing by respecting the
|
||||||
|
* minimal free bus time between a STOP condition and a START condition.
|
||||||
|
*
|
||||||
|
* @note This is not thread safe, but since libnfc is single threaded
|
||||||
|
* this should be okay.
|
||||||
|
*
|
||||||
|
* @param id I2C device
|
||||||
|
* @param buf pointer on buffer containing data
|
||||||
|
* @param len length of the buffer
|
||||||
|
* @return NFC_SUCCESS on success, otherwise driver error code
|
||||||
|
*/
|
||||||
|
static ssize_t pn532_i2c_write(const i2c_device id,
|
||||||
|
const uint8_t *buf, const size_t len)
|
||||||
|
{
|
||||||
|
struct timespec transaction_start, bus_free_time = { 0 };
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &transaction_start);
|
||||||
|
bus_free_time.tv_nsec = (PN532_BUS_FREE_TIME * 1000 * 1000) -
|
||||||
|
(transaction_start.tv_nsec - __transaction_stop.tv_nsec);
|
||||||
|
nanosleep(&bus_free_time, NULL);
|
||||||
|
|
||||||
|
ret = i2c_write(id, buf, len);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &__transaction_stop);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Scan all available I2C buses to find PN532 devices.
|
* @brief Scan all available I2C buses to find PN532 devices.
|
||||||
*
|
*
|
||||||
|
@ -347,7 +403,7 @@ pn532_i2c_send(nfc_device *pnd, const uint8_t *pbtData, const size_t szData, int
|
||||||
return pnd->last_error;
|
return pnd->last_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = i2c_write(DRIVER_DATA(pnd)->dev, abtFrame, szFrame);
|
res = pn532_i2c_write(DRIVER_DATA(pnd)->dev, abtFrame, szFrame);
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to transmit data. (TX)");
|
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_ERROR, "%s", "Unable to transmit data. (TX)");
|
||||||
|
@ -405,10 +461,7 @@ pn532_i2c_wait_rdyframe(nfc_device *pnd, uint8_t *pbtData, const size_t szDataLe
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Wait a little bit before reading
|
int recCount = pn532_i2c_read(DRIVER_DATA(pnd)->dev, i2cRx, szDataLen + 1);
|
||||||
nanosleep(&rdyDelay, (struct timespec *) NULL);
|
|
||||||
|
|
||||||
int recCount = i2c_read(DRIVER_DATA(pnd)->dev, i2cRx, szDataLen + 1);
|
|
||||||
|
|
||||||
if (DRIVER_DATA(pnd)->abort_flag) {
|
if (DRIVER_DATA(pnd)->abort_flag) {
|
||||||
// Reset abort flag
|
// Reset abort flag
|
||||||
|
@ -575,7 +628,7 @@ error:
|
||||||
int
|
int
|
||||||
pn532_i2c_ack(nfc_device *pnd)
|
pn532_i2c_ack(nfc_device *pnd)
|
||||||
{
|
{
|
||||||
return i2c_write(DRIVER_DATA(pnd)->dev, pn53x_ack_frame, sizeof(pn53x_ack_frame));
|
return pn532_i2c_write(DRIVER_DATA(pnd)->dev, pn53x_ack_frame, sizeof(pn53x_ack_frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue