From 1198a71d64bf9017cb10039b3de9c1384b4fceda Mon Sep 17 00:00:00 2001 From: Romuald Conty Date: Wed, 27 Apr 2011 13:16:36 +0000 Subject: [PATCH] chip/pn53x: handle PN532 "power down" and "low VBat" power mode instead of a simple "sleep" mode. (Fixes Issue 167) --- libnfc/chips/pn53x.c | 19 +++++++++++++ libnfc/chips/pn53x.h | 10 +++---- libnfc/drivers/acr122.c | 2 +- libnfc/drivers/arygon.c | 10 +++---- libnfc/drivers/pn532_uart.c | 57 ++++++++++++++++++++++++++----------- libnfc/drivers/pn53x_usb.c | 6 ++-- 6 files changed, 73 insertions(+), 31 deletions(-) diff --git a/libnfc/chips/pn53x.c b/libnfc/chips/pn53x.c index ec524d7..758520d 100644 --- a/libnfc/chips/pn53x.c +++ b/libnfc/chips/pn53x.c @@ -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) { diff --git a/libnfc/chips/pn53x.h b/libnfc/chips/pn53x.h index fbc35d0..f64b4a1 100644 --- a/libnfc/chips/pn53x.h +++ b/libnfc/chips/pn53x.h @@ -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; diff --git a/libnfc/drivers/acr122.c b/libnfc/drivers/acr122.c index e1244a6..fc5c00b 100644 --- a/libnfc/drivers/acr122.c +++ b/libnfc/drivers/acr122.c @@ -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 diff --git a/libnfc/drivers/arygon.c b/libnfc/drivers/arygon.c index 2b6a7ef..b0a2545 100644 --- a/libnfc/drivers/arygon.c +++ b/libnfc/drivers/arygon.c @@ -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; } diff --git a/libnfc/drivers/pn532_uart.c b/libnfc/drivers/pn532_uart.c index 190e885..2f93068 100644 --- a/libnfc/drivers/pn532_uart.c +++ b/libnfc/drivers/pn532_uart.c @@ -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; } diff --git a/libnfc/drivers/pn53x_usb.c b/libnfc/drivers/pn53x_usb.c index 05249ca..6300d96 100644 --- a/libnfc/drivers/pn53x_usb.c +++ b/libnfc/drivers/pn53x_usb.c @@ -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; }