chip/pn53x: handle PN532 "power down" and "low VBat" power mode instead of a simple "sleep" mode. (Fixes Issue 167)

This commit is contained in:
Romuald Conty 2011-04-27 13:16:36 +00:00
parent 09b18bf6b8
commit 1198a71d64
6 changed files with 73 additions and 31 deletions

View file

@ -136,6 +136,12 @@ pn53x_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, b
// Call the send/receice callback functions of the current driver
if (!CHIP_DATA(pnd)->io->send (pnd, pbtTx, szTx))
return false;
// Handle power mode for PN532
if ((CHIP_DATA(pnd)->type == PN532) && (TgInitAsTarget == pbtTx[0])) { // PN532 automatically goes into PowerDown mode when TgInitAsTarget command will be sent
CHIP_DATA(pnd)->power_mode = POWERDOWN;
}
int res = CHIP_DATA(pnd)->io->receive (pnd, pbtRx, *pszRx);
if (res < 0) {
return false;
@ -144,6 +150,10 @@ pn53x_transceive (nfc_device_t * pnd, const byte_t * pbtTx, const size_t szTx, b
if (pnd->iLastError)
return false;
if ((CHIP_DATA(pnd)->type == PN532) && (TgInitAsTarget == pbtTx[0])) { // PN532 automatically wakeup on external RF field
CHIP_DATA(pnd)->power_mode = NORMAL; // When TgInitAsTarget reply that means an external RF have waken up the chip
}
*pszRx = (size_t) res;
switch (pbtTx[0]) {
@ -678,6 +688,15 @@ pn53x_InDeselect (nfc_device_t * pnd, const uint8_t ui8Target)
return (pn53x_transceive (pnd, abtCmd, sizeof (abtCmd), NULL, NULL));
}
/*
typedef enum {
NORMAL = 0x01,
VIRTUAL_CARD = 0x02,
WIRED_MODE = 0x03,
DUAL_CARD = 0x04
} pn532_sam_mode;
*/
bool
pn53x_SAMConfiguration (nfc_device_t * pnd, const uint8_t ui8Mode)
{

View file

@ -137,10 +137,10 @@ typedef enum {
} 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;
NORMAL, // In that case, there is no power saved but the PN53x reacts as fast as possible on the host controller interface.
POWERDOWN, // Only on PN532, need to be wake up to process commands with a long preamble
LOWVBAT // Only on PN532, need to be wake up to process commands with a long preamble and SAMConfiguration command
} pn53x_power_mode;
struct pn53x_io {
bool (*send)(nfc_device_t * pnd, const byte_t * pbtData, const size_t szData);
@ -149,7 +149,7 @@ struct pn53x_io {
struct pn53x_data {
pn53x_type type;
pn53x_state state;
pn53x_power_mode power_mode;
const struct pn53x_io * io;
/** Register cache for REG_CIU_BIT_FRAMING, SYMBOL_TX_LAST_BITS: The last TX bits setting, we need to reset this if it does not apply anymore */
uint8_t ui8TxBits;

View file

@ -220,7 +220,7 @@ acr122_connect (const nfc_device_desc_t * pndd)
// Done, we found the reader we are looking for
snprintf (pnd->acName, sizeof (pnd->acName), "%s / %s", pndd->acDevice, pcFirmware);
CHIP_DATA (pnd)->state = NORMAL;
CHIP_DATA (pnd)->power_mode = NORMAL;
CHIP_DATA (pnd)->io = &acr122_io;
// 50: empirical tuning on Touchatag
// 46: empirical tuning on ACR122U

View file

@ -112,7 +112,7 @@ arygon_probe (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * pszDev
pnd->driver_data = malloc(sizeof(struct arygon_data));
DRIVER_DATA (pnd)->port = sp;
pnd->chip_data = malloc(sizeof(struct pn53x_data));
CHIP_DATA (pnd)->state = NORMAL;
CHIP_DATA (pnd)->power_mode = NORMAL;
CHIP_DATA (pnd)->io = &arygon_tama_io;
bool res = arygon_reset_tama (pnd);
@ -172,7 +172,7 @@ arygon_connect (const nfc_device_desc_t * pndd)
pnd->chip_data = malloc(sizeof(struct pn53x_data));
// The PN53x chip connected to ARYGON MCU doesn't seems to be in SLEEP mode
CHIP_DATA (pnd)->state = NORMAL;
CHIP_DATA (pnd)->power_mode = NORMAL;
CHIP_DATA (pnd)->io = &arygon_tama_io;
// empirical tuning
CHIP_DATA (pnd)->timer_correction = 46;
@ -239,7 +239,7 @@ arygon_tama_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szDat
}
if (pn53x_check_ack_frame (pnd, abtRxBuf, sizeof(abtRxBuf))) {
CHIP_DATA (pnd)->state = EXECUTE;
// The PN53x is running the sent command
} else if (0 == memcmp(arygon_error_unknown_mode, abtRxBuf, sizeof(abtRxBuf))) {
ERR( "Bad frame format." );
// We have already read 6 bytes and arygon_error_unknown_mode is 10 bytes long
@ -255,8 +255,6 @@ arygon_tama_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szDat
int
arygon_abort (nfc_device_t *pnd)
{
CHIP_DATA (pnd)->state = NORMAL;
// Send a valid TAMA packet to wakup the PN53x (we will not have an answer, according to Arygon manual)
byte_t dummy[] = { 0x32, 0x00, 0x00, 0xff, 0x09, 0xf7, 0xd4, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x6e, 0x66, 0x63, 0xbe, 0x00 };
@ -384,7 +382,7 @@ arygon_tama_receive (nfc_device_t * pnd, byte_t * pbtData, const size_t szDataLe
pnd->iLastError = DEIO;
return -1;
}
CHIP_DATA (pnd)->state = NORMAL;
// The PN53x command is done and we successfully received the reply
return len;
}

View file

@ -49,7 +49,7 @@
static const byte_t ack_frame[] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 };
int pn532_uart_ack (nfc_device_t * pnd);
// void pn532_uart_wakeup (const nfc_device_spec_t nds);
int pn532_uart_wakeup (nfc_device_t * pnd);
const struct pn53x_io pn532_uart_io;
@ -94,7 +94,7 @@ pn532_uart_probe (nfc_device_desc_t pnddDevices[], size_t szDevices, size_t * ps
DRIVER_DATA (pnd)->port = sp;
pnd->chip_data = malloc(sizeof(struct pn53x_data));
CHIP_DATA (pnd)->type = PN532;
CHIP_DATA (pnd)->state = SLEEP;
CHIP_DATA (pnd)->power_mode = LOWVBAT;
CHIP_DATA (pnd)->io = &pn532_uart_io;
// Check communication using "Diagnose" command, with "Communication test" (0x00)
@ -158,7 +158,7 @@ pn532_uart_connect (const nfc_device_desc_t * pndd)
DRIVER_DATA(pnd)->port = sp;
pnd->chip_data = malloc(sizeof(struct pn53x_data));
CHIP_DATA(pnd)->type = PN532;
CHIP_DATA(pnd)->state = SLEEP;
CHIP_DATA(pnd)->power_mode = LOWVBAT;
CHIP_DATA(pnd)->io = &pn532_uart_io;
// empirical tuning
CHIP_DATA(pnd)->timer_correction = 48;
@ -182,21 +182,42 @@ pn532_uart_disconnect (nfc_device_t * pnd)
nfc_device_free (pnd);
}
int
pn532_uart_wakeup (nfc_device_t * pnd)
{
/* 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 };
int res = uart_send (DRIVER_DATA(pnd)->port, pn532_wakeup_preamble, sizeof (pn532_wakeup_preamble));
CHIP_DATA(pnd)->power_mode = NORMAL; // PN532 should now be awake
return res;
}
#define PN532_BUFFER_LEN (PN53x_EXTENDED_FRAME__DATA_MAX_LEN + PN53x_EXTENDED_FRAME__OVERHEAD)
bool
pn532_uart_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szData)
{
if (CHIP_DATA(pnd)->state == SLEEP) {
/** 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 };
uart_send (DRIVER_DATA(pnd)->port, pn532_wakeup_preamble, sizeof (pn532_wakeup_preamble));
CHIP_DATA(pnd)->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;
switch (CHIP_DATA(pnd)->power_mode) {
case LOWVBAT: {
/** PN532C106 wakeup. */
if (-1 == pn532_uart_wakeup(pnd)) {
return false;
}
// 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;
}
}
}
break;
case POWERDOWN: {
if (-1 == pn532_uart_wakeup(pnd)) {
return false;
}
}
break;
case NORMAL:
// Nothing to do :)
break;
};
byte_t abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff"
CHIP_DATA (pnd)->ui8LastCommand = pbtData[0];
@ -223,7 +244,7 @@ pn532_uart_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szData
}
if (pn53x_check_ack_frame (pnd, abtRxBuf, sizeof(abtRxBuf))) {
CHIP_DATA(pnd)->state = EXECUTE;
// The PN53x is running the sent command
} else {
return false;
}
@ -353,14 +374,18 @@ pn532_uart_receive (nfc_device_t * pnd, byte_t * pbtData, const size_t szDataLen
pnd->iLastError = DEIO;
return -1;
}
CHIP_DATA(pnd)->state = NORMAL;
// The PN53x command is done and we successfully received the reply
return len;
}
int
pn532_uart_ack (nfc_device_t * pnd)
{
CHIP_DATA(pnd)->state = NORMAL;
if (POWERDOWN == CHIP_DATA(pnd)->power_mode) {
if (-1 == pn532_uart_wakeup(pnd)) {
return -1;
}
}
return (0 == uart_send (DRIVER_DATA(pnd)->port, ack_frame, sizeof (ack_frame))) ? 0 : -1;
}

View file

@ -285,7 +285,7 @@ pn53x_usb_connect (const nfc_device_desc_t *pndd)
*DRIVER_DATA (pnd) = data;
pnd->chip_data = malloc(sizeof(struct pn53x_data));
CHIP_DATA (pnd)->state = NORMAL;
CHIP_DATA (pnd)->power_mode = NORMAL;
CHIP_DATA (pnd)->io = &pn53x_usb_io;
switch (DRIVER_DATA (pnd)->model) {
@ -388,7 +388,7 @@ pn53x_usb_send (nfc_device_t * pnd, const byte_t * pbtData, const size_t szData)
}
if (pn53x_check_ack_frame (pnd, abtRxBuf, res)) {
CHIP_DATA(pnd)->state = EXECUTE;
// The PN53x is running the sent command
} else {
return false;
}
@ -529,7 +529,7 @@ read:
pnd->iLastError = DEIO;
return -1;
}
CHIP_DATA (pnd)->state = NORMAL;
// The PN53x command is done and we successfully received the reply
return len;
}