Added basic support for common Mifare cards
authorKevin Townsend <kevin@ktownsend.com>
Mon, 15 Aug 2011 22:01:27 +0000 (00:01 +0200)
committerKevin Townsend <kevin@ktownsend.com>
Mon, 15 Aug 2011 22:01:27 +0000 (00:01 +0200)
drivers/sensors/pn532/helpers/pn532_mifare.h [new file with mode: 0644]
drivers/sensors/pn532/helpers/pn532_mifare_classic.c [new file with mode: 0644]
drivers/sensors/pn532/helpers/pn532_mifare_classic.h [new file with mode: 0644]
drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c [new file with mode: 0644]
drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h [new file with mode: 0644]
drivers/sensors/pn532/pn532.c
drivers/sensors/pn532/pn532.h
drivers/sensors/pn532/pn532_bus_uart.c [new file with mode: 0644]

diff --git a/drivers/sensors/pn532/helpers/pn532_mifare.h b/drivers/sensors/pn532/helpers/pn532_mifare.h
new file mode 100644 (file)
index 0000000..fedebeb
--- /dev/null
@@ -0,0 +1,29 @@
+/**************************************************************************/
+/*! 
+    @file     pn532_mifare.h
+*/
+/**************************************************************************/
+
+#ifndef __PN532_MIFARE_H__
+#define __PN532_MIFARE_H__
+
+#include "projectconfig.h"
+
+// These may need to be enlarged for multi card support
+#define PN532_RESPONSELEN_INLISTPASSIVETARGET (32)
+#define PN532_RESPONSELEN_INDATAEXCHANGE      (32)  
+
+typedef enum pn532_mifare_cmd_e
+{
+  PN532_MIFARE_CMD_AUTH_A     = 0x60,
+  PN532_MIFARE_CMD_AUTH_B     = 0x61,
+  PN532_MIFARE_CMD_READ       = 0x30,
+  PN532_MIFARE_CMD_WRITE      = 0xA0,
+  PN532_MIFARE_CMD_TRANSFER   = 0xB0,
+  PN532_MIFARE_CMD_DECREMENT  = 0xC0,
+  PN532_MIFARE_CMD_INCREMENT  = 0xC1,
+  PN532_MIFARE_CMD_STORE      = 0xC2
+} 
+pn532_mifare_cmd_t;
+
+#endif
diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_classic.c b/drivers/sensors/pn532/helpers/pn532_mifare_classic.c
new file mode 100644 (file)
index 0000000..b30156a
--- /dev/null
@@ -0,0 +1,457 @@
+/**************************************************************************/
+/*! 
+    @file     pn532_mifare_classic.c
+*/
+/**************************************************************************/
+
+/*  MIFARE CLASSIC DESCRIPTION
+    ==========================
+
+    MIFARE Classic cards come in 1K and 4K varieties.  While several
+    varieties of chips exist, the two main chipsets used are described
+    in the following publicly accessible documents:
+
+        MF1S503x Mifare Classic 1K data sheet:
+        http://www.nxp.com/documents/data_sheet/MF1S503x.pdf
+            
+        MF1S70yyX MIFARE Classic 4K data sheet:
+        http://www.nxp.com/documents/data_sheet/MF1S70YYX.pdf
+
+    Mifare Classic cards typically have a a 4-byte NUID, though you may
+    find cards with 7 byte IDs as well
+        
+    EEPROM MEMORY
+    =============
+    Mifare Classic cards have either 1K or 4K of EEPROM memory. Each
+    memory block can be configured with different access conditions,
+    with two seperate authentication keys present in each block.
+
+    The two main Mifare Classic card types are organised as follows:
+
+        1K Cards: 16 sectors of 4 blocks (0..15)
+        4K Cards: 32 sectors of 4 blocks (0..31) and
+                  8 sectors of 16 blocks (32..39)
+
+    4 block sectors
+    ===============
+    Sector  Block   Bytes                                                           Description
+    ------  -----   -----                                                           -----------
+                    0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
+
+    1       3       [-------KEY A-------]   [Access Bits]   [-------KEY A-------]   Sector Trailer 1
+            2       [                            Data                           ]   Data
+            1       [                            Data                           ]   Data
+            0       [                            Data                           ]   Data
+
+    0       3       [-------KEY A-------]   [Access Bits]   [-------KEY A-------]   Sector Trailer 1
+            2       [                            Data                           ]   Data
+            1       [                            Data                           ]   Data
+            0       [                     Manufacturer Data                     ]   Manufacturer Block
+
+    Sector Trailer (Block 3)
+    ------------------------
+    The sector trailer block contains the two secret keys (Key A and Key B), as well
+    as the access conditions for the four blocks.  It has the following structure:
+
+        Sector Trailer Bytes
+        --------------------------------------------------------------
+        0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
+        [       Key A       ]   [Access Bits]   [       Key B       ]
+
+    For more information in using Keys to access the clock contents, see
+    Accessing Data Blocks further below.
+
+    Data Blocks (Blocks 0..2)
+    -------------------------
+    Data blocks are 16 bytes wide and, depending on the permissions set in the
+    access bits, can be read from and written to. You are free to use the 16 data
+    bytes in any way you wish.  You can easily store text input, store four 32-bit
+    integer values, a 16 character uri, etc.
+
+    Data Blocks as "Value Blocks"
+    -----------------------------
+    An alternative to storing random data in the 16 byte-wide blocks is to
+    configure them as "Value Blocks".  Value blocks allow performing electronic
+    purse functions (valid commands are: read, write, increment, decrement,
+    restore, transfer).
+    
+    Each Value block contains a single signed 32-bit value, and this value is
+    stored 3 times for data integrity and security reasons.  It is stored twice
+    non-inverted, and once inverted.  The last 4 bytes are used for a 1-byte
+    address, which is stored 4 times (twice non-inverted, and twice inverted).
+    
+    Data blocks configured as "Value Blocks" have the following structure:
+
+        Value Block Bytes
+        --------------------------------------------------------------
+        0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
+        [   Value   ]   [   ~Value  ]   [   Value   ]   [A  ~A  A   ~A]
+
+    Manufacturer Block (Sector 0, Block 0)
+    --------------------------------------
+    Sector 0 is special since it contains the Manufacturer Block. This block
+    contains the manufacturer data, and is read-only.  It should be avoided
+    unless you know what you are doing.
+
+    16 block sectors
+    ================
+    16 block sectors are identical to 4 block sectors, but with more data blocks.  The same
+    structure described in the 4 block sectors above applies.
+
+    Sector  Block   Bytes                                                           Description
+    ------  -----   -----                                                           ----------
+                    0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
+
+    32      15      [-------KEY A-------]   [Access Bits]   [-------KEY B-------]   Sector Trailer 32
+            14      [                            Data                           ]   Data
+            13      [                            Data                           ]   Data
+            ...
+            2       [                            Data                           ]   Data
+            1       [                            Data                           ]   Data
+            0       [                            Data                           ]   Data
+
+    ACCESSING DATA BLOCKS
+    =====================
+
+    Before you can access the cards, you must following two steps:
+
+    1.) You must retrieve the 7 byte UID or the 4-byte NUID of the card.
+        This can be done using pn532_mifareclassic_WaitForPassiveTarget()
+        below, which will return the appropriate ID.
+
+    2.) You must authenticate the sector you wish to access according to the
+        access rules defined in the Sector Trailer block for that sector.
+        This can be done using pn532_mifareclassic_AuthenticateBlock(),
+        passing in the appropriate key value.
+          
+        Most new cards have a default Key A of 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF,
+        but some common values worth trying are:
+
+            0XFF 0XFF 0XFF 0XFF 0XFF 0XFF
+            0XD3 0XF7 0XD3 0XF7 0XD3 0XF7
+            0XA0 0XA1 0XA2 0XA3 0XA4 0XA5
+            0XB0 0XB1 0XB2 0XB3 0XB4 0XB5
+            0X4D 0X3A 0X99 0XC3 0X51 0XDD
+            0X1A 0X98 0X2C 0X7E 0X45 0X9A
+            0XAA 0XBB 0XCC 0XDD 0XEE 0XFF
+            0X00 0X00 0X00 0X00 0X00 0X00
+            0XAB 0XCD 0XEF 0X12 0X34 0X56
+
+    3.) Once authenication has succeeded, and depending on the sector
+        permissions, you can then read/write/increment/decrement the
+        contents of the specific block, using one of the helper functions
+        included in this module.
+
+*/
+
+#include <string.h>
+
+#include "../pn532.h"
+#include "../pn532_bus.h"
+#include "pn532_mifare.h"
+#include "pn532_mifare_classic.h"
+
+#include "core/systick/systick.h"
+
+/**************************************************************************/
+/*! 
+      Indicates whether the specified block number is the first block
+      in the sector (block 0 relative to the current sector)
+*/
+/**************************************************************************/
+bool is_first_block (uint32_t uiBlock)
+{
+  // Test if we are in the small or big sectors
+  if (uiBlock < 128)
+    return ((uiBlock) % 4 == 0);
+  else
+    return ((uiBlock) % 16 == 0);
+}
+
+/**************************************************************************/
+/*! 
+      Indicates whether the specified block number is the sector trailer
+*/
+/**************************************************************************/
+bool is_trailer_block (uint32_t uiBlock)
+{
+  // Test if we are in the small or big sectors
+  if (uiBlock < 128)
+    return ((uiBlock + 1) % 4 == 0);
+  else
+    return ((uiBlock + 1) % 16 == 0);
+}
+
+/**************************************************************************/
+/*! 
+    Tries to detect MIFARE targets in passive mode.  This needs to be done
+    before anything useful can be accomplished with a tag since you need
+    the tag's unique UID to communicate with it.
+
+    @param  pbtCUID     Pointer to the byte array where the card's UID
+                        will be stored once a card is detected
+    @param  pszUIDLen   Pointer to the size of the card UID in bytes
+
+    Response for a valid ISO14443A 106KBPS (Mifare Classic, etc.)
+    should be in the following format.  See UM0701-02 section
+    7.3.5 for more information
+    
+    byte            Description
+    -------------   ------------------------------------------
+    b0..6           Frame header and preamble
+    b7              Tags Found
+    b8              Tag Number (only one used in this example)
+    b9..10          SENS_RES
+    b11             SEL_RES
+    b12             NFCID Length
+    b13..NFCIDLen   NFCID                                      
+    
+    SENS_RES   SEL_RES     Manufacturer/Card Type    NFCID Len
+    --------   -------     -----------------------   ---------
+    00 04      08          NXP Mifare Classic 1K     4 bytes   
+    00 02      18          NXP Mifare Classic 4K     4 bytes
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_WRONGCARDTYPE
+*/
+/**************************************************************************/
+pn532_error_t pn532_mifareclassic_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen)
+{
+  byte_t abtResponse[PN532_RESPONSELEN_INLISTPASSIVETARGET];
+  pn532_error_t error;
+  size_t szLen;
+
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Waiting for an ISO14443A Card%s", CFG_PRINTF_NEWLINE);
+  #endif
+
+  /* Try to initialise a single ISO14443A tag at 106KBPS                  */
+  /* Note:  To wait for a card with a known UID, append the four byte     */
+  /*        UID to the end of the command.                                */ 
+  byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS};
+  error = pn532Write(abtCommand, sizeof(abtCommand));
+  if (error) 
+    return error;
+
+  /* Wait until we get a valid response or a timeout                      */
+  do
+  {
+    systickDelay(25);
+    error = pn532Read(abtResponse, &szLen);
+  } while (error == PN532_ERROR_RESPONSEBUFFEREMPTY);
+  if (error) 
+    return error;
+
+  /* Check SENSE_RES to make sure this is a Mifare Classic card           */
+  /*          Classic 1K       = 00 04                                    */
+  /*          Classic 4K       = 00 02                                    */
+  /*          Classic Emulated = 00 08                                    */
+  if ((abtResponse[10] == 0x02) || 
+      (abtResponse[10] == 0x04) || 
+      (abtResponse[10] == 0x08))
+  {
+    /* Card appears to be Mifare Classic */
+    *szCUIDLen = abtResponse[12];
+    for (uint8_t i=0; i < *szCUIDLen; i++) 
+    {
+      pbtCUID[i] = abtResponse[13+i];
+    }
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Card Found: %s", CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("      ATQA: ");
+      pn532PrintHex(abtResponse+9, 2);
+      PN532_DEBUG("      SAK: %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("      UID: ");
+      pn532PrintHex(pbtCUID, *szCUIDLen);
+    #endif
+  }
+  else
+  {
+    /* Card is ISO14443A but doesn't appear to be Mifare Classic          */
+    /*    Mifare Ultralight    = 0x0044                                   */
+    /*    Mifare DESFire       = 0x0344                                   */
+    /*    Innovision Jewel     = 0x0C00                                   */
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Wrong Card Type (Expected ATQA 00 02, 00 04 or 00 08) %s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  ATQA       : ");
+      pn532PrintHex(abtResponse+9, 2);
+      PN532_DEBUG("  SAK        : %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  UID Length : %d%s", abtResponse[12], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  UID        : ");
+      size_t pos;
+      for (pos=0; pos < abtResponse[12]; pos++) 
+      {
+        printf("%02x ", abtResponse[13 + pos]);
+      }
+      printf("%s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE);
+    #endif
+    return PN532_ERROR_WRONGCARDTYPE;
+  }
+
+  return PN532_ERROR_NONE;
+}
+
+
+/**************************************************************************/
+/*! 
+    Tries to authenticate a block of memory on a MIFARE card using the
+    INDATAEXCHANGE command.  See section 7.3.8 of the PN532 User Manual
+    for more information on sending MIFARE and other commands.
+
+    @param  pbtCUID       Pointer to a byte array containing the card UID
+    @param  szCUIDLen     The length (in bytes) of the card's UID (Should
+                          be 4 for MIFARE Classic)
+    @param  uiBlockNumber The block number to authenticate.  (0..63 for
+                          1KB cards, and 0..255 for 4KB cards).
+    @param  uiKeyType     Which key type to use during authentication
+                          (PN532_MIFARE_CMD_AUTH_A or PN532_MIFARE_CMD_AUTH_B)
+    @param  pbtKeys       Pointer to a byte array containing the 6 byte
+                          key value
+*/
+/**************************************************************************/
+pn532_error_t pn532_mifareclassic_AuthenticateBlock (byte_t * pbtCUID, size_t szCUIDLen, uint32_t uiBlockNumber, uint8_t uiKeyType, byte_t * pbtKeys)
+{
+  pn532_error_t error;
+  byte_t abtCommand[17];
+  byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE];
+  size_t szLen;
+
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Trying to authenticate card ");
+  pn532PrintHex(pbtCUID, szCUIDLen);
+  #endif
+
+  /* Prepare the authentication command */
+  abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE;   /* Data Exchange Header */
+  abtCommand[1] = 1;                              /* Max card numbers */
+  abtCommand[2] = (uiKeyType) ? PN532_MIFARE_CMD_AUTH_A : PN532_MIFARE_CMD_AUTH_B;
+  abtCommand[3] = uiBlockNumber;                  /* Block Number (1K = 0..63, 4K = 0..255 */
+  memcpy (abtCommand+4, pbtKeys, 6);
+  uint8_t i;
+  for (i = 0; i < szCUIDLen; i++)
+  {
+    abtCommand[10+i] = pbtCUID[i];                /* 4 byte card ID */
+  }
+  
+  /* Send the command */
+  error = pn532Write(abtCommand, 10+szCUIDLen);
+  if (error)
+  {
+    /* Problem with the serial bus, etc. */
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Authentification failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }
+
+  /* Read the authentification response */
+  memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE);
+  do
+  {
+    systickDelay(25);
+    error = pn532Read(abtResponse, &szLen);
+  }
+  while (error == PN532_ERROR_RESPONSEBUFFEREMPTY);
+  if (error)
+  {
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Authentification failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }
+
+  // ToDo: How to check if authentification really worked (bad key, etc.)?
+
+  /* Output the authentification data */
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Authenticated block %d %s", uiBlockNumber, CFG_PRINTF_NEWLINE);
+  #endif
+
+  // Return OK signal
+  return PN532_ERROR_NONE;
+}
+
+/**************************************************************************/
+/*! 
+    Tries to read an entire 16-byte data block at the specified block
+    address.
+
+    @param  uiBlockNumber The block number to authenticate.  (0..63 for
+                          1KB cards, and 0..255 for 4KB cards).
+    @param  pbtData       Pointer to the byte array that will hold the
+                          retrieved data (if any)
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_BLOCKREADFAILED
+*/
+/**************************************************************************/
+pn532_error_t pn532_mifareclassic_ReadDataBlock (uint8_t uiBlockNumber, byte_t * pbtData)
+{
+  pn532_error_t error;
+  byte_t abtCommand[4];
+  byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE];
+  size_t szLen;
+
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Reading 16 bytes at block %03d%s", uiBlockNumber, CFG_PRINTF_NEWLINE);
+  #endif
+
+  /* Prepare the command */
+  abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE;
+  abtCommand[1] = 1;                            /* Card number */
+  abtCommand[2] = PN532_MIFARE_CMD_READ;        /* Mifare Read command = 0x30 */
+  abtCommand[3] = uiBlockNumber;                /* Block Number (0..63 for 1K, 0..255 for 4K) */
+  
+  /* Send the commands */
+  error = pn532Write(abtCommand, sizeof(abtCommand));
+  if (error)
+  {
+    /* Bus error, etc. */
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }
+
+  /* Read the response */
+  memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE);
+  do
+  {
+    systickDelay(50);
+    error = pn532Read(abtResponse, &szLen);
+  }
+  while (error == PN532_ERROR_RESPONSEBUFFEREMPTY);
+  if (error)
+  {
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }
+
+  /* Make sure we have a valid response (should be 26 bytes long) */
+  if (szLen == 26)
+  {
+    /* Copy the 16 data bytes to the output buffer        */
+    /* Block content starts at byte 8 of a valid response */
+    memcpy (pbtData, abtResponse+7, 16);    
+  }
+  else
+  {
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Unexpected response reading block %d.  Bad key?%s", uiBlockNumber, CFG_PRINTF_NEWLINE);
+    #endif
+    return PN532_ERROR_BLOCKREADFAILED;
+  }
+
+  /* Display data for debug if requested */
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Block %03d: ", uiBlockNumber, CFG_PRINTF_NEWLINE);
+    pn532PrintHexVerbose(pbtData, 16);
+  #endif
+
+  // Return OK signal
+  return PN532_ERROR_NONE;
+}
diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_classic.h b/drivers/sensors/pn532/helpers/pn532_mifare_classic.h
new file mode 100644 (file)
index 0000000..81177e9
--- /dev/null
@@ -0,0 +1,17 @@
+/**************************************************************************/
+/*! 
+    @file     pn532_mifare_classic.h
+*/
+/**************************************************************************/
+
+#ifndef __PN532_MIFARE_CLASSIC_H__
+#define __PN532_MIFARE_CLASSIC_H__
+
+#include "projectconfig.h"
+#include "pn532_mifare.h"
+
+pn532_error_t pn532_mifareclassic_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen);
+pn532_error_t pn532_mifareclassic_AuthenticateBlock (byte_t * pbtCUID, size_t szCUIDLen, uint32_t uiBlockNumber, uint8_t uiKeyType, byte_t * pbtKeys);
+pn532_error_t pn532_mifareclassic_ReadDataBlock (uint8_t uiBlockNumber, byte_t * pbtData);
+
+#endif
diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.c
new file mode 100644 (file)
index 0000000..f22f4c6
--- /dev/null
@@ -0,0 +1,314 @@
+/**************************************************************************/
+/*! 
+    @file     pn532_mifare_ultralight.c
+*/
+/**************************************************************************/
+
+/*  MIFARE ULTRALIGHT DESCRIPTION
+    =============================
+
+    MIFARE Ultralight cards typically contain 512 bits (64 bytes) of
+    memory, including 4 bytes (32-bits) of OTP (One Time Programmable)
+    memory where the individual bits can be written but not erased.
+
+        MF0ICU1 Mifare Ultralight Functional Specification:
+        http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf
+            
+
+    Mifare Ultralight cards have a 7-byte UID
+    
+    EEPROM MEMORY
+    =============
+    Mifare Ultralight cards have 512 bits (64 bytes) of EEPROM memory,
+    including 4 byte (32 bits) of OTP memory.  Unlike Mifare Classic cards,
+    there is no authentication on a per block level, although the blocks
+    can be set to "read-only" mode using Lock Bytes (described below).
+
+    EEPROM memory is organised into 16 pages of four bytes eachs, in
+    the following order
+
+    Page   Description
+    ----   ------------
+    0      Serial Number (4 bytes)
+    1      Serial Number (4 bytes)
+    2      Byte 0:    Serial Number
+           Byte 1:    Internal Memory
+           Byte 2..3: lock bytes
+    3      One-time programmable memory (4 bytes)
+    4..15  User memory (4 bytes)
+
+    Lock Bytes (Page 2)
+    -------------------
+    Bytes 2 and 3 of page 2 are referred to as "Lock Bytes".  Each
+    page from 0x03 and higher can individually locked by setting the
+    corresponding locking bit to "1" to prevent further write access,
+    effectively making the memory read only.
+
+    For information on the lock byte mechanism, refer to section 8.5.2 of
+    the datasheet (referenced above).
+
+    OTP Bytes (Page 3)
+    ------------------
+    Page 3 is the OTP memory, and by default all bits on this page are
+    set to 0.  These bits can be bitwise modified using the Mifare WRITE
+    command, and individual bits can be set to 1, but can not be changed
+    back to 0.
+
+    Data Pages (Pages 4..15)
+    ------------------------
+    Pages 4 to 15 are can be freely read from and written to, 
+    provided there is no conflict with the Lock Bytes described above.
+
+    After production, the bytes have the following default values:
+
+    Page    Byte Values
+    ----    ----------------------
+            0     1     2     3
+    4       0xFF  0xFF  0xFF  0xFF
+    5..15   0x00  0x00  0x00  0x00
+
+    ACCESSING DATA BLOCKS
+    =====================
+
+    Before you can access the cards, you must following two steps:
+
+    1.) 'Connect' to a Mifare Ultralight card and retrieve the 7 byte
+        UID of the card.
+
+    2.) Memory can be read and written directly once a passive mode
+        connection has been made.  No authentication is required for
+        Mifare Ultralight cards.
+
+*/
+
+#include <string.h>
+
+#include "../pn532.h"
+#include "../pn532_bus.h"
+#include "pn532_mifare_ultralight.h"
+
+#include "core/systick/systick.h"
+
+/**************************************************************************/
+/*! 
+    Tries to detect MIFARE targets in passive mode.
+
+    @param  pbtCUID     Pointer to the byte array where the card's 7 byte
+                        UID will be stored once a card is detected
+    @param  pszUIDLen   Pointer to the size of the card UID in bytes
+
+    Response for a valid ISO14443A 106KBPS (Mifare Ultralight, etc.)
+    should be in the following format.  See UM0701-02 section
+    7.3.5 for more information
+    
+    byte            Description
+    -------------   ------------------------------------------
+    b0..6           Frame header and preamble
+    b7              Tags Found
+    b8              Tag Number (only one used in this example)
+    b9..10          SENS_RES
+    b11             SEL_RES
+    b12             NFCID Length
+    b13..NFCIDLen   NFCID                                      
+    
+    SENS_RES   SEL_RES     Manufacturer/Card Type    NFCID Len
+    --------   -------     -----------------------   ---------
+    00 44      00          NXP Mifare Ultralight     7 bytes   
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_WRONGCARDTYPE
+*/
+/**************************************************************************/
+pn532_error_t pn532_mifareultralight_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen)
+{
+  byte_t abtResponse[PN532_RESPONSELEN_INLISTPASSIVETARGET];
+  pn532_error_t error;
+  size_t szLen;
+
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Waiting for an ISO14443A Card%s", CFG_PRINTF_NEWLINE);
+  #endif
+
+  /* Try to initialise a single ISO14443A tag at 106KBPS                  */
+  /* Note:  To wait for a card with a known UID, append the four byte     */
+  /*        UID to the end of the command.                                */ 
+  byte_t abtCommand[] = { PN532_COMMAND_INLISTPASSIVETARGET, 0x01, PN532_MODULATION_ISO14443A_106KBPS};
+  error = pn532Write(abtCommand, sizeof(abtCommand));
+  if (error) 
+    return error;
+
+  /* Wait until we get a valid response or a timeout                      */
+  do
+  {
+    systickDelay(25);
+    error = pn532Read(abtResponse, &szLen);
+  } while (error == PN532_ERROR_RESPONSEBUFFEREMPTY);
+  if (error) 
+    return error;
+
+  /* Check SENS_RES to make sure this is a Mifare Ultralight card         */
+  /*          Mifare Ultralight = 00 44                                   */
+  if (abtResponse[10] == 0x44)
+  {
+    /* Card appears to be Mifare Ultralight */
+    *szCUIDLen = abtResponse[12];
+    for (uint8_t i=0; i < *szCUIDLen; i++) 
+    {
+      pbtCUID[i] = abtResponse[13+i];
+    }
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Card Found: %s", CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("      ATQA: ");
+      pn532PrintHex(abtResponse+9, 2);
+      PN532_DEBUG("      SAK: %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("      UID: ");
+      pn532PrintHex(pbtCUID, *szCUIDLen);
+    #endif
+  }
+  else
+  {
+    /* Card is ISO14443A but doesn't appear to be Mifare Ultralight       */
+    /*    Mifare Classic       = 0x0002, 0x0004, 0x0008                   */
+    /*    Mifare DESFire       = 0x0344                                   */
+    /*    Innovision Jewel     = 0x0C00                                   */
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Wrong Card Type (Expected ATQA 00 44) %s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  ATQA       : ");
+      pn532PrintHex(abtResponse+9, 2);
+      PN532_DEBUG("  SAK        : %02x%s", abtResponse[11], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  UID Length : %d%s", abtResponse[12], CFG_PRINTF_NEWLINE);
+      PN532_DEBUG("  UID        : ");
+      size_t pos;
+      for (pos=0; pos < abtResponse[12]; pos++) 
+      {
+        printf("%02x ", abtResponse[13 + pos]);
+      }
+      printf("%s%s", CFG_PRINTF_NEWLINE, CFG_PRINTF_NEWLINE);
+    #endif
+    return PN532_ERROR_WRONGCARDTYPE;
+  }
+
+  return PN532_ERROR_NONE;
+}
+
+/**************************************************************************/
+/*! 
+    Tries to read an entire 4-byte page at the specified address.
+
+    @param  page        The page number (0..63 in most cases)
+    @param  pbtBuffer   Pointer to the byte array that will hold the
+                        retrieved data (if any)
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_ADDRESSOUTOFRANGE
+            - PN532_ERROR_BLOCKREADFAILED
+*/
+/**************************************************************************/
+pn532_error_t pn532_mifareultralight_ReadPage (uint8_t page, byte_t * pbtBuffer)
+{
+  pn532_error_t error;
+  byte_t abtCommand[4];
+  byte_t abtResponse[PN532_RESPONSELEN_INDATAEXCHANGE];
+  size_t szLen;
+
+  if (page >= 64)
+  {
+    return PN532_ERROR_ADDRESSOUTOFRANGE;
+  }
+
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Reading page %03d%s", page, CFG_PRINTF_NEWLINE);
+  #endif
+
+  /* Prepare the command */
+  abtCommand[0] = PN532_COMMAND_INDATAEXCHANGE;
+  abtCommand[1] = 1;                            /* Card number */
+  abtCommand[2] = PN532_MIFARE_CMD_READ;        /* Mifare Read command = 0x30 */
+  abtCommand[3] = page;                         /* Page Number (0..63 in most cases) */
+  
+  /* Send the commands */
+  error = pn532Write(abtCommand, sizeof(abtCommand));
+  if (error)
+  {
+    /* Bus error, etc. */
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }
+
+  /* Read the response */
+  memset(abtResponse, 0, PN532_RESPONSELEN_INDATAEXCHANGE);
+  do
+  {
+    systickDelay(50);
+    error = pn532Read(abtResponse, &szLen);
+  }
+  while (error == PN532_ERROR_RESPONSEBUFFEREMPTY);
+  if (error)
+  {
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Read failed%s", CFG_PRINTF_NEWLINE);
+    #endif
+    return error;
+  }  
+
+  /* Make sure we have a valid response (should be 26 bytes long) */
+  if (szLen == 26)
+  {
+    /* Copy the 4 data bytes to the output buffer         */
+    /* Block content starts at byte 8 of a valid response */
+    /* Note that the command actually reads 16 byte or 4  */
+    /* pages at a time ... we simply discard the last 12  */
+    /* bytes                                              */
+    memcpy (pbtBuffer, abtResponse+8, 4);
+  }
+  else
+  {
+    #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Unexpected response reading block %d.  Bad key?%s", page, CFG_PRINTF_NEWLINE);
+    #endif
+    return PN532_ERROR_BLOCKREADFAILED;
+  }
+
+  /* Display data for debug if requested */
+  #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Page %02d: ", page, CFG_PRINTF_NEWLINE);
+    pn532PrintHexVerbose(pbtBuffer, 4);
+  #endif
+
+  // Return OK signal
+  return PN532_ERROR_NONE;
+}
+
+//static  bool
+//read_card (void)
+//{
+//  uint32_t page;
+//  bool    bFailure = false;
+//  uint32_t uiReadedPages = 0;
+//
+//  printf ("Reading %d pages |", uiBlocks + 1);
+//
+//  for (page = 0; page <= uiBlocks; page += 4) {
+//    // Try to read out the data block
+//    if (nfc_initiator_mifare_cmd (pnd, MC_READ, page, &mp)) {
+//      memcpy (mtDump.amb[page / 4].mbd.abtData, mp.mpd.abtData, 16);
+//    } else {
+//      bFailure = true;
+//      break;
+//    }
+//
+//    print_success_or_failure (bFailure, &uiReadedPages);
+//    print_success_or_failure (bFailure, &uiReadedPages);
+//    print_success_or_failure (bFailure, &uiReadedPages);
+//    print_success_or_failure (bFailure, &uiReadedPages);
+//  }
+//  printf ("|\n");
+//  printf ("Done, %d of %d pages readed.\n", uiReadedPages, uiBlocks + 1);
+//  fflush (stdout);
+//
+//  return (!bFailure);
+//}
diff --git a/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h b/drivers/sensors/pn532/helpers/pn532_mifare_ultralight.h
new file mode 100644 (file)
index 0000000..78239c9
--- /dev/null
@@ -0,0 +1,16 @@
+/**************************************************************************/
+/*! 
+    @file     pn532_mifare_ultralight.h
+*/
+/**************************************************************************/
+
+#ifndef __PN532_MIFARE_ULTRALIGHT_H__
+#define __PN532_MIFARE_ULTRALIGHT_H__
+
+#include "projectconfig.h"
+#include "pn532_mifare.h"
+
+pn532_error_t pn532_mifareultralight_WaitForPassiveTarget (byte_t * pbtCUID, size_t * szCUIDLen);
+pn532_error_t pn532_mifareultralight_ReadPage (uint8_t page, byte_t * pbtBuffer);
+
+#endif
index ca91aba..78eae7f 100644 (file)
@@ -41,7 +41,7 @@ void pn532PrintHex(const byte_t * pbtData, const size_t szBytes)
     @param  szBytes   Data length in bytes
 */
 /**************************************************************************/
-void pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes)
+void pn532PrintHexChar(const byte_t * pbtData, const size_t szBytes)
 {
   size_t szPos;
   for (szPos=0; szPos < szBytes; szPos++) 
@@ -51,7 +51,7 @@ void pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes)
   printf("  ");
   for (szPos=0; szPos < szBytes; szPos++) 
   {
-    printf("%c", pbtData[szPos] == 0 ? '.' : pbtData[szPos]);
+    printf("%c", pbtData[szPos] <= 0x1F ? '.' : pbtData[szPos]);
   }
   printf(CFG_PRINTF_NEWLINE);
 }
index a48ad1c..d6fa5ac 100644 (file)
@@ -10,7 +10,7 @@
 #include "projectconfig.h"
 
 // Comment out this line to disable debug output
-#define PN532_DEBUGMODE
+// #define PN532_DEBUGMODE
 #define PN532_DEBUG(fmt, args...)             printf(fmt, ##args) 
 
 /* Error messages generated by the stack */
@@ -134,7 +134,7 @@ typedef struct
 } pn532_pcb_t;
 
 void          pn532PrintHex(const byte_t * pbtData, const size_t szBytes);
-void          pn532PrintHexVerbose(const byte_t * pbtData, const size_t szBytes);
+void          pn532PrintHexChar(const byte_t * pbtData, const size_t szBytes);
 pn532_pcb_t * pn532GetPCB();
 void          pn532Init();
 pn532_error_t pn532Read(byte_t *pbtResponse, size_t * pszLen);
diff --git a/drivers/sensors/pn532/pn532_bus_uart.c b/drivers/sensors/pn532/pn532_bus_uart.c
new file mode 100644 (file)
index 0000000..981df74
--- /dev/null
@@ -0,0 +1,298 @@
+/**************************************************************************/
+/*! 
+    @file   pn532_bus_uart.c
+*/
+/**************************************************************************/
+#include <string.h>
+
+#include "pn532.h"
+#include "pn532_bus.h"
+
+#ifdef PN532_BUS_UART
+
+#include "core/systick/systick.h"
+#include "core/gpio/gpio.h"
+#include "core/uart/uart.h"
+
+/**************************************************************************/
+/*! 
+    @brief  Builds a standard PN532 frame using the supplied data
+
+    @param  pbtFrame  Pointer to the field that will hold the frame data
+    @param  pszFrame  Pointer to the field that will hold the frame length
+    @param  pbtData   Pointer to the data to insert in a frame
+    @param  swData    Length of the data to insert in bytes
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_EXTENDEDFRAME
+*/
+/**************************************************************************/
+pn532_error_t pn532_bus_BuildFrame(byte_t * pbtFrame, size_t * pszFrame, const byte_t * pbtData, const size_t szData)
+{
+  if (szData > PN532_NORMAL_FRAME__DATA_MAX_LEN) 
+  {
+    // Extended frames currently unsupported
+    return PN532_ERROR_EXTENDEDFRAME;
+  }
+
+  // LEN - Packet length = data length (len) + checksum (1) + end of stream marker (1)
+  pbtFrame[3] = szData + 1;
+  // LCS - Packet length checksum
+  pbtFrame[4] = 256 - (szData + 1);
+  // TFI
+  pbtFrame[5] = 0xD4;
+  // DATA - Copy the PN53X command into the packet buffer
+  memcpy (pbtFrame + 6, pbtData, szData);
+
+  // DCS - Calculate data payload checksum
+  byte_t btDCS = (256 - 0xD4);
+  size_t szPos;
+  for (szPos = 0; szPos < szData; szPos++) 
+  {
+    btDCS -= pbtData[szPos];
+  }
+  pbtFrame[6 + szData] = btDCS;
+
+  // 0x00 - End of stream marker
+  pbtFrame[szData + 7] = 0x00;
+
+  (*pszFrame) = szData + PN532_NORMAL_FRAME__OVERHEAD;
+
+  return PN532_ERROR_NONE;
+}
+
+/**************************************************************************/
+/*! 
+    @brief  Initialises UART and configures the PN532
+*/
+/**************************************************************************/
+void pn532_bus_HWInit(void)
+{
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Initialising UART (%d)%s", PN532_UART_BAUDRATE, CFG_PRINTF_NEWLINE);
+  #endif
+  uartInit(PN532_UART_BAUDRATE);
+
+  // Set reset pin as output and reset device
+  gpioSetDir(PN532_RSTPD_PORT, PN532_RSTPD_PIN, gpioDirection_Output);
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Resetting the PN532...\r\n");
+  #endif
+  gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 0);
+  systickDelay(400);
+  gpioSetValue(PN532_RSTPD_PORT, PN532_RSTPD_PIN, 1);
+
+  // Wait for the PN532 to finish booting
+  systickDelay(100);
+}
+
+/**************************************************************************/
+/*! 
+    @brief  Sends the specified command to the PN532, automatically
+            creating an appropriate frame for it
+
+    @param  pdbData   Pointer to the byte data to send
+    @param  szData    Length in bytes of the data to send
+
+    @note   Possible error messages are:
+
+            - PN532_ERROR_BUSY
+            - PN532_ERROR_NOACK
+            - PN532_ERROR_INVALIDACK
+*/
+/**************************************************************************/
+pn532_error_t pn532_bus_SendCommand(const byte_t * pbtData, const size_t szData)
+{
+  pn532_pcb_t *pn532 = pn532GetPCB();
+    
+  // Check if we're busy
+  if (pn532->state == PN532_STATE_BUSY)
+  {
+    return PN532_ERROR_BUSY;
+  }
+
+  // Flag the stack as busy
+  pn532->state = PN532_STATE_BUSY;
+
+  // Every packet must start with "00 00 ff"
+  byte_t  abtFrame[PN532_BUFFER_LEN] = { 0x00, 0x00, 0xff };
+  size_t szFrame = 0;
+
+  // Build the frame
+  pn532_bus_BuildFrame (abtFrame, &szFrame, pbtData, szData);
+
+  // Keep track of the last command that was sent
+  pn532->lastCommand = pbtData[0];
+
+  // Output the frame data for debugging if requested
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Sending  (%02d): ", szFrame);
+  pn532PrintHex(abtFrame, szFrame);
+  #endif
+
+  // Send data to the PN532
+  uartSend (abtFrame, szFrame);
+
+  // Wait for ACK
+  byte_t abtRxBuf[6];
+  uart_pcb_t *uart = uartGetPCB();
+  systickDelay(10);   // FIXME: How long should we wait for ACK?
+  if (uart->rxfifo.len < 6) 
+  {
+    // Unable to read ACK
+    #ifdef PN532_DEBUGMODE
+    PN532_DEBUG ("Unable to read ACK%s", CFG_PRINTF_NEWLINE);
+    #endif
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_NOACK;
+  }
+
+  // Read ACK ... this will also remove it from the buffer
+  const byte_t abtAck[6] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 };
+  abtRxBuf[0] = uartRxBufferRead();
+  abtRxBuf[1] = uartRxBufferRead();
+  abtRxBuf[2] = uartRxBufferRead();
+  abtRxBuf[3] = uartRxBufferRead();
+  abtRxBuf[4] = uartRxBufferRead();
+  abtRxBuf[5] = uartRxBufferRead();
+
+  // Make sure the received ACK matches the prototype
+  if (0 != (memcmp (abtRxBuf, abtAck, 6))) 
+  {
+    #ifdef PN532_DEBUGMODE
+    PN532_DEBUG ("Invalid ACK: ");
+    pn532PrintHex(abtRxBuf, 6);
+    PN532_DEBUG("%s", CFG_PRINTF_NEWLINE);
+    #endif
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_INVALIDACK;
+  }
+
+  pn532->state = PN532_STATE_READY;
+  return PN532_ERROR_NONE;
+}
+
+/**************************************************************************/
+/*! 
+    @brief  Reads a response from the PN532
+
+    @note   Possible error message are:
+
+            - PN532_ERROR_BUSY
+            - PN532_ERROR_RESPONSEBUFFEREMPTY
+            - PN532_ERROR_PREAMBLEMISMATCH
+            - PN532_ERROR_APPLEVELERROR
+            - PN532_ERROR_EXTENDEDFRAME
+            - PN532_ERROR_LENCHECKSUMMISMATCH
+*/
+/**************************************************************************/
+pn532_error_t pn532_bus_ReadResponse(byte_t * pbtResponse, size_t * pszRxLen)
+{
+  pn532_pcb_t *pn532 = pn532GetPCB();
+
+  // Check if we're busy
+  if (pn532->state == PN532_STATE_BUSY)
+  {
+    return PN532_ERROR_BUSY;
+  }
+
+  // Flag the stack as busy
+  pn532->state = PN532_STATE_BUSY;
+
+  // Reset the app error flag
+  pn532->appError = PN532_APPERROR_NONE;
+
+  // Read response from uart
+  if (!uartRxBufferReadArray(pbtResponse, pszRxLen)) 
+  {
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_RESPONSEBUFFEREMPTY;
+  }
+
+  // Display the raw response data for debugging if requested
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Received (%02d): ", *pszRxLen);
+  pn532PrintHex(pbtResponse, *pszRxLen);
+  #endif
+  
+  // Check preamble
+  const byte_t pn53x_preamble[3] = { 0x00, 0x00, 0xff };
+  if (0 != (memcmp (pbtResponse, pn53x_preamble, 3))) 
+  {
+    #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Frame preamble + start code mismatch%s", CFG_PRINTF_NEWLINE);
+    #endif
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_PREAMBLEMISMATCH;
+  }
+
+  // Check the frame type
+  if ((0x01 == pbtResponse[3]) && (0xff == pbtResponse[4])) 
+  {
+    // Error frame
+    #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Application level error (0x%02x)%s", pbtResponse[5], CFG_PRINTF_NEWLINE);
+    #endif
+    // Set application error message ID
+    pn532->appError = pbtResponse[5];
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_APPLEVELERROR;
+  } 
+  else if ((0xff == pbtResponse[3]) && (0xff == pbtResponse[4])) 
+  {
+    // Extended frame
+    #ifdef PN532_DEBUGMODE
+    PN532_DEBUG("Extended frames currently unsupported%s", CFG_PRINTF_NEWLINE);
+    #endif
+    pn532->state = PN532_STATE_READY;
+    return PN532_ERROR_EXTENDEDFRAME;
+  } 
+  else 
+  {
+    // Normal frame
+    if (256 != (pbtResponse[3] + pbtResponse[4])) 
+    {
+      // TODO: Retry
+      #ifdef PN532_DEBUGMODE
+      PN532_DEBUG("Length checksum mismatch%s", CFG_PRINTF_NEWLINE);
+      #endif
+      pn532->state = PN532_STATE_READY;
+      return PN532_ERROR_LENCHECKSUMMISMATCH;
+    }
+    // size_t szPayloadLen = abtRx[3] - 2;
+  }
+
+  pn532->state = PN532_STATE_READY;
+  return PN532_ERROR_NONE;
+}
+
+/**************************************************************************/
+/*! 
+    @brief      Sends the wakeup sequence to the PN532.
+*/
+/**************************************************************************/
+pn532_error_t pn532_bus_Wakeup(void)
+{
+  size_t szLen;
+  byte_t abtWakeUp[] = { 0x55,0x55,0x00,0x00,0x00,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00,0x00,0xff,0x03,0xfd,0xd4,0x14,0x01,0x17,0x00 };
+
+  pn532_pcb_t *pn532 = pn532GetPCB();
+
+  #ifdef PN532_DEBUGMODE
+  PN532_DEBUG("Sending Wakeup Sequence%s", CFG_PRINTF_NEWLINE);
+  #endif
+  uartSend(abtWakeUp,sizeof(abtWakeUp));
+  systickDelay(100);
+
+  byte_t response[32];
+  pn532_bus_ReadResponse(response, &szLen);
+
+  // Todo: Check for error ... currently throws a checksum error
+  // that isn't really an error
+
+  pn532->state = PN532_STATE_READY;
+  return PN532_ERROR_NONE;
+}
+
+#endif  // #ifdef PN532_BUS_UART
\ No newline at end of file
This page took 0.055703 seconds and 4 git commands to generate.