nfctype4: Add support for blank tag handling
authorRavi kumar Veeramally <ravikumar.veeramally@linux.intel.com>
Thu, 16 Aug 2012 14:31:53 +0000 (16:31 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Thu, 16 Aug 2012 14:31:53 +0000 (16:31 +0200)
When using a new tag for the first time, the CC fie and ndef file might
not be created (un-formatted tag) this was causing the tag to be dropped.
This patch add handling for such tags:

    * When a tag with out CC file is found, it is marked as blank tag
      and is not dropped.
    * Add a new nfctype4_format() function used for creating iso file
      (D2760000850101h), CC (E103h) file and empty NDEF (E104h) file
      in blank t4 (mifare desfire ev1 2K, 4K and 8K) tags with desfire
      commands. And select iso file, cc file and ndef file with
      iso 7816-4 commands to make sure ndef file is ready to write.

plugins/nfctype4.c

index 534bde4..e987e85 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdint.h>
 #include <errno.h>
 #include <string.h>
+#include <math.h>
 #include <sys/socket.h>
 
 #include <linux/socket.h>
 #define APDU_OK                        0x9000
 #define APDU_NOT_FOUND         0x6A82
 
+/* PICC Level Commands */
+#define PICC_CLASS             0x90
+#define GET_VERSION            0x60
+#define CREATE_APPLICATION     0xCA
+#define SELECT_APPLICATION     0x5A
+#define CREATE_STD_DATA_FILE   0xCD
+#define WRITE_DATA_TO_FILE     0x3D
+
+#define DESFire_EV1_MAJOR_VERSION      0x01
+#define PICC_LEVEL_APDU_OK             0x9100
+#define GET_VERSION_FRAME_RESPONSE_BYTE 0xAF
+#define DESFIRE_KEY_SETTINGS   0x0F
+#define        DESFIRE_NUM_OF_KEYS     0x21
+#define DESFIRE_CC_FILE_NUM    0x01
+#define DESFIRE_NDEF_FILE_NUM  0x02
+#define DESFIRE_COMMSET                0x00
+#define MAPPING_VERSION                0x20
+#define        FREE_READ_ACCESS        0x00
+#define        FREE_WRITE_ACCESS       0x00
+
+#define MIFARE_DESFIRE_EV1_SIZE(x)     (pow(2, (x >> 1)))
+
 #define T4_ALL_ACCESS          0x00
 #define T4_READ_ONLY           0xFF
 
@@ -94,6 +117,36 @@ struct type4_cc {                   /* Capability Container */
        uint8_t tlv_blocks[];
 } __attribute__((packed));
 
+struct desfire_app {
+       uint8_t aid[3];
+       uint8_t key_settings;
+       uint8_t number_of_keys;
+       uint8_t file_id[2];
+       uint8_t iso_appname[7];
+} __attribute__((packed));
+
+struct desfire_std_file {
+       uint8_t file_num;
+       uint8_t file_id[2];
+       uint8_t comm_set;
+       uint8_t access_rights[2];
+       uint8_t size[3];
+} __attribute__((packed));
+
+struct desfire_cc_file {
+       uint8_t file_num;
+       uint8_t offset[3];
+       uint8_t max_len[3];
+       uint8_t cc_len[2];
+       uint8_t version;
+       uint8_t mle[2];
+       uint8_t mlc[2];
+       uint8_t ndef_tlv[4];
+       uint8_t ndef_size[2];
+       uint8_t read_access;
+       uint8_t write_access;
+} __attribute__((packed));
+
 struct t4_cookie {
        uint32_t adapter_idx;
        uint32_t target_idx;
@@ -105,6 +158,7 @@ struct t4_cookie {
        uint16_t max_ndef_size;
        uint8_t write_access;
        struct near_ndef_message *ndef;
+       uint16_t memory_size;
 };
 
 /* ISO functions: This code prepares APDU */
@@ -114,17 +168,28 @@ static int ISO_send_cmd(uint8_t class,
                        uint8_t param2,
                        uint8_t *cmd_data,
                        uint8_t cmd_data_length,
+                       near_bool_t le,
                        near_recv cb,
                        void *in_data)
 {
        struct type4_cmd *cmd;
        struct t4_cookie *in_rcv = in_data;
+       uint8_t total_cmd_length;
        int err;
 
-       DBG("CLA:x%02x INS:x%02x P1:%02x P2:%02x",
+       DBG("CLA-%02x INS-%02x P1-%02x P2-%02x",
                        class, instruction, param1, param2);
 
-       cmd = g_try_malloc0(APDU_HEADER_LEN + cmd_data_length);
+       if (le == FALSE) {
+               if (cmd_data)
+                       total_cmd_length = APDU_HEADER_LEN + cmd_data_length;
+               else
+                       total_cmd_length = APDU_HEADER_LEN;
+       } else {  /* Extra byte for Le */
+               total_cmd_length = APDU_HEADER_LEN + cmd_data_length + 1;
+       }
+
+       cmd = g_try_malloc0(total_cmd_length);
        if (cmd == NULL) {
                DBG("Mem alloc failed");
                err = -ENOMEM;
@@ -137,13 +202,16 @@ static int ISO_send_cmd(uint8_t class,
        cmd->param2     =       param2 ;
        cmd->data_length =      cmd_data_length;
 
-       if (cmd_data)
+       if (cmd_data) {
                memcpy(cmd->data, cmd_data, cmd_data_length);
-       else
-               cmd_data_length = 0 ;
+               /* The Le byte set to 0x00 defined that any length
+                * of PICC response is allowed */
+               if (le == TRUE)
+                       cmd->data[cmd_data_length] = 0;
+       }
 
        err =  near_adapter_send(in_rcv->adapter_idx, (uint8_t *)cmd,
-                       APDU_HEADER_LEN + cmd_data_length , cb, in_rcv);
+                                       total_cmd_length, cb, in_rcv);
        if (err < 0)
                g_free(in_rcv);
 
@@ -170,6 +238,7 @@ static int ISO_Select(uint8_t *filename, uint8_t fnamelen, uint8_t P1,
                        0x00,           /* P2: First or only occurrence */
                        filename,       /* cmd_data */
                        fnamelen,       /* uint8_t cmd_data_length*/
+                       FALSE,
                        cb,
                        cookie);
 }
@@ -186,6 +255,7 @@ static int ISO_ReadBinary(uint16_t offset, uint8_t readsize,
                        (uint8_t)(offset & 0xFF),
                        0,              /* no data send */
                        readsize,       /* bytes to read */
+                       FALSE,
                        cb,
                        cookie);
 }
@@ -202,6 +272,7 @@ static int ISO_Update(uint16_t offset, uint8_t nlen,
                        (uint8_t)(offset & 0xFF),
                        data,                   /* length of NDEF data */
                        nlen,                   /* NLEN + NDEF data */
+                       FALSE,
                        cb,
                        cookie);
 }
@@ -762,12 +833,724 @@ out_err:
        return t4_cookie_release(err, cookie);
 }
 
+static int select_ndef_file(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               near_error("select ndef file resp failed %02X",
+                                       resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       DBG("ndef file selected");
+
+       if (cookie->cb)
+               cookie->cb(cookie->adapter_idx, cookie->target_idx, 0);
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int read_cc_file(uint8_t *resp, int length, void *data)
+{
+       struct t4_cookie *cookie = data;
+       struct near_tag *tag;
+       struct type4_cc *read_cc = NULL;
+       int err = 0;
+
+       DBG("%d", length);
+
+       if (length < 0) {
+               err = length;
+               goto out_err;
+       }
+
+       /* Check APDU error ( the two last bytes of the resp) */
+       if (APDU_STATUS(resp + length - 2) != APDU_OK) {
+               near_error("read cc failed SWx%04x",
+                                       APDU_STATUS(resp + length - 2));
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* -2 for status word and -1 is for NFC first byte... */
+       read_cc = g_try_malloc0(length - 2 - NFC_STATUS_BYTE_LEN);
+       if (read_cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(read_cc, &resp[1], length - 2 - NFC_STATUS_BYTE_LEN) ;
+       cookie->c_apdu_max_size = g_ntohs(read_cc->max_C_apdu_data_size);
+       cookie->max_ndef_size = g_ntohs(read_cc->tlv_fc.max_ndef_size);
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       near_tag_set_max_ndef_size(tag, cookie->memory_size);
+       near_tag_set_c_apdu_max_size(tag, cookie->c_apdu_max_size);
+
+       if (read_cc->tlv_fc.tag  != 0x4) {
+               near_error("NDEF File not found") ;
+               err = -EINVAL ;
+               goto out_err;
+       }
+
+       err = ISO_Select((uint8_t *)&read_cc->tlv_fc.file_id,
+                       LEN_ISO_CC_FILEID, 0, select_ndef_file, cookie);
+       if (err < 0) {
+               near_error("select ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(read_cc);
+       return 0;
+
+out_err:
+       g_free(read_cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+
+       if (length < 0) {
+               near_error("CC file select resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + STATUS_WORD_1) != APDU_OK) {
+               near_error("CC file select response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = ISO_ReadBinary(0, LEN_ISO_CC_READ_SIZE, read_cc_file, cookie);
+       if (err < 0) {
+               near_error("read cc file req failed %d", err);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_iso_appname_v2(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+
+       if (length < 0) {
+               near_error("iso app select resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[NFC_STATUS] != 0x00) {
+               near_error("iso app select response %02X",
+                                       resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       err = ISO_Select(iso_cc_fileid, LEN_ISO_CC_FILEID, 0,
+                       select_cc_file, cookie);
+       if (err < 0) {
+               near_error("select cc req failed %d", err);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int format_resp(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct near_tag *tag;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("write data to ndef file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("wrtie data to ndef file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       tag = near_tag_get_tag(cookie->adapter_idx, cookie->target_idx);
+       if (tag == NULL) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       DBG("Formatting is done");
+       near_tag_set_blank(tag, FALSE);
+
+       /*
+        * 1) Till now all commands which are used for formatting are
+        *    at mifare desfire level. Now select iso appname_v2,
+        *    cc file and ndef file with ISO 7816-4 commands.
+        * 2) Selecting ndef file means making sure that read write
+        *    operations will perform on NDEF file.
+        */
+       err = ISO_Select(iso_appname_v2, ARRAY_SIZE(iso_appname_v2),
+                       0x4, select_iso_appname_v2, cookie);
+       if (err < 0) {
+               near_error("iso_select appnamev2 req failed %d", err);
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int write_data_to_ndef_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t ndef_file_offset[] = {0x00, 0x00, 0x00};
+       uint8_t empty_ndef_file_len[] = {0x02, 0x00, 0x00}; /* 000002h */
+       uint8_t ndef_nlen[] = {0x00, 0x00}; /* 0000h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create ndef file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create ndef file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Step8 : Write data to NDEF file ( no NDEF message) */
+       cmd_data_length = 1 /* File num */
+                               + ARRAY_SIZE(ndef_file_offset)
+                               + ARRAY_SIZE(empty_ndef_file_len)
+                               + ARRAY_SIZE(ndef_nlen);
+
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cmd_data[0] = DESFIRE_NDEF_FILE_NUM;
+       memcpy(cmd_data + 1, ndef_file_offset, ARRAY_SIZE(ndef_file_offset));
+       memcpy(cmd_data + 4, empty_ndef_file_len,
+                       ARRAY_SIZE(empty_ndef_file_len));
+       memcpy(cmd_data + 7, ndef_nlen, ARRAY_SIZE(ndef_nlen));
+
+       err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, format_resp, cookie);
+       if (err < 0) {
+               near_error("wrtie data to ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_ndef_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_std_file *ndef = NULL;
+       uint8_t iso_ndef_file_id[] = {0x04, 0xE1}; /* E104h */
+       uint8_t ndef_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("write data to cc file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("write data to cc file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       ndef = g_try_malloc0(sizeof(struct desfire_std_file));
+       if (ndef == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       ndef->file_num = DESFIRE_NDEF_FILE_NUM;
+       memcpy(ndef->file_id, iso_ndef_file_id, ARRAY_SIZE(iso_ndef_file_id));
+       ndef->comm_set = DESFIRE_COMMSET;
+       memcpy(ndef->access_rights, ndef_file_access_rights,
+                               ARRAY_SIZE(ndef_file_access_rights));
+       ndef->size[0] = 0;
+       ndef->size[1] = (uint8_t) (cookie->memory_size >> 8);
+       ndef->size[2] = (uint8_t) cookie->memory_size;
+
+       err = ISO_send_cmd(PICC_CLASS, CREATE_STD_DATA_FILE,
+                               0x00, 0x00, (uint8_t *)ndef,
+                               sizeof(struct desfire_std_file),
+                               TRUE, write_data_to_ndef_file, cookie);
+       if (err < 0) {
+               near_error("create ndef file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(ndef);
+       return 0;
+
+out_err:
+       g_free(ndef);
+       return t4_cookie_release(err, cookie);
+}
+
+static int write_data_to_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_cc_file *cc = NULL;
+       uint8_t cc_file_offset[] = {0x00, 0x00, 0x00};
+       uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */
+       uint8_t cc_len[] = {0x00, 0x0F}; /* 000Fh*/
+       uint8_t mle_r_apdu[] = {0x00, 0x3B}; /* 003Bh */
+       uint8_t mlc_c_apdu[] = {0x00, 0x34}; /* 0034h */
+       /* T: 04, L: 06: V: E104h (NDEF ISO FID = E104h)*/
+       uint8_t ndef_tlv[] = {0x04, 0x06, 0xE1, 0x04};
+
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create cc file resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create cc file response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       cc = g_try_malloc0(sizeof(struct desfire_cc_file));
+       if (cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cc->file_num = DESFIRE_CC_FILE_NUM;
+       memcpy(cc->offset, cc_file_offset, ARRAY_SIZE(cc_file_offset));
+       memcpy(cc->max_len, cc_file_max_len, ARRAY_SIZE(cc_file_max_len));
+       memcpy(cc->cc_len, cc_len, ARRAY_SIZE(cc_len));
+       cc->version = MAPPING_VERSION;
+       memcpy(cc->mle, mle_r_apdu, ARRAY_SIZE(mle_r_apdu));
+       memcpy(cc->mlc, mlc_c_apdu, ARRAY_SIZE(mlc_c_apdu));
+       memcpy(cc->ndef_tlv, ndef_tlv, ARRAY_SIZE(ndef_tlv));
+       cc->ndef_size[0] = (uint8_t) (cookie->memory_size >> 8);
+       cc->ndef_size[1] = (uint8_t) cookie->memory_size;
+       cc->read_access = FREE_READ_ACCESS;
+       cc->write_access = FREE_WRITE_ACCESS;
+
+       err = ISO_send_cmd(PICC_CLASS, WRITE_DATA_TO_FILE,
+                               0x00, 0x00, (uint8_t *)cc,
+                               sizeof(struct desfire_cc_file),
+                               TRUE, create_ndef_file, cookie);
+       if (err < 0) {
+               near_error("write data to cc file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cc);
+       return 0;
+
+out_err:
+       g_free(cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_cc_file(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       struct desfire_std_file *cc = NULL;
+       uint8_t iso_cc_file_id[] = {0x03, 0xe1}; /* E103h */
+       uint8_t cc_file_access_rights[] = {0xE0, 0xEE}; /* EEE0h */
+       uint8_t cc_file_max_len[] = {0x0F, 0x00, 0x00}; /* 00000Fh */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("select application1 resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("select application1 response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       cc = g_try_malloc0(sizeof(struct desfire_std_file));
+       if (cc == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       cc->file_num = DESFIRE_CC_FILE_NUM;
+       memcpy(cc->file_id, iso_cc_file_id, ARRAY_SIZE(iso_cc_file_id));
+       cc->comm_set = DESFIRE_COMMSET;
+       memcpy(cc->access_rights, cc_file_access_rights,
+                               ARRAY_SIZE(cc_file_access_rights));
+       memcpy(cc->size, cc_file_max_len, ARRAY_SIZE(cc_file_max_len));
+
+       err = ISO_send_cmd(PICC_CLASS,
+                               CREATE_STD_DATA_FILE,
+                               0x00, 0x00, (uint8_t *)cc,
+                               sizeof(struct desfire_std_file),
+                               TRUE, write_data_to_cc_file, cookie);
+       if (err < 0) {
+               near_error("create cc file req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cc);
+       return 0;
+
+out_err:
+       g_free(cc);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_application_1(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("create application resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("create application response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* Step4 : Select application (which is created just now) */
+       cmd_data_length = ARRAY_SIZE(desfire_aid_1);
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(cmd_data, desfire_aid_1, cmd_data_length);
+       err = ISO_send_cmd(PICC_CLASS, SELECT_APPLICATION,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, create_cc_file, cookie);
+       if (err < 0) {
+               near_error("select application1 req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int create_application(uint8_t *resp, int length, void *data)
+{
+       int err = 0;
+       struct t4_cookie *cookie = data;
+       uint8_t desfire_aid_1[] = {0x01, 0x00, 0x00}; /* 000001h */
+       uint8_t desfire_file_id[] = {0x10, 0xE1}; /* E110h */
+       struct desfire_app *app = NULL;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("select application resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (APDU_STATUS(resp + 1) != PICC_LEVEL_APDU_OK) {
+               near_error("select application response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       app = g_try_malloc0(sizeof(struct desfire_app));
+       if (app == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(app->aid, desfire_aid_1, ARRAY_SIZE(desfire_aid_1));
+       app->key_settings = DESFIRE_KEY_SETTINGS;
+       app->number_of_keys = DESFIRE_NUM_OF_KEYS;
+       memcpy(app->file_id, desfire_file_id, ARRAY_SIZE(desfire_file_id));
+       memcpy(app->iso_appname, iso_appname_v2, ARRAY_SIZE(iso_appname_v2));
+
+       /* Step3 : Create Application */
+       err = ISO_send_cmd(PICC_CLASS, CREATE_APPLICATION,
+                               0x00, 0x00, (uint8_t *)app,
+                               sizeof(struct desfire_app),
+                               TRUE, select_application_1, cookie);
+       if (err < 0) {
+               near_error("create application req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(app);
+       return 0;
+
+out_err:
+       g_free(app);
+       return t4_cookie_release(err, cookie);
+}
+
+static int select_application(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+       uint8_t *cmd_data = NULL;
+       uint8_t cmd_data_length;
+       uint8_t desfire_aid[] = {0x00, 0x00, 0x00}; /* 000000h */
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("get version3 resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[length - 1] != 0x00) {
+               near_error("get version3 response %02X",
+                                               resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       /* AID : 000000h */
+       cmd_data_length = ARRAY_SIZE(desfire_aid);
+       cmd_data = g_try_malloc0(cmd_data_length);
+       if (cmd_data == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+
+       memcpy(cmd_data, desfire_aid, cmd_data_length);
+       /* Step2 : Select Application */
+       err = ISO_send_cmd(PICC_CLASS,
+                               SELECT_APPLICATION,
+                               0x00, 0x00, cmd_data, cmd_data_length,
+                               TRUE, create_application, cookie);
+       if (err < 0) {
+               near_error("select application req failed %d", err);
+               goto out_err;
+       }
+
+       g_free(cmd_data);
+       return 0;
+
+out_err:
+       g_free(cmd_data);
+       return t4_cookie_release(err, cookie);
+}
+
+static int get_version_frame3(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error("get version2 resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[4] == 0x01 /* Major Version */
+               && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) {
+
+               err = ISO_send_cmd(PICC_CLASS,
+                                       GET_VERSION_FRAME_RESPONSE_BYTE,
+                                       0x00, 0x00, NULL, 0, FALSE,
+                                       select_application, cookie);
+               if (err < 0) {
+                       near_error("get version3 req failed %d", err);
+                       goto out_err;
+               }
+       } else {
+               near_error("get version2 response %02X", resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+static int get_version_frame2(uint8_t *resp, int length, void *data)
+{
+       int err;
+       struct t4_cookie *cookie = data;
+
+       DBG("");
+
+       if (length < 0) {
+               near_error(" get version resp failed %d", length);
+               err = length;
+               goto out_err;
+       }
+
+       if (resp[4] == 0x01 /* Major Version */
+               && resp[length - 1] == GET_VERSION_FRAME_RESPONSE_BYTE) {
+
+               cookie->memory_size = MIFARE_DESFIRE_EV1_SIZE(resp[6]);
+               err = ISO_send_cmd(PICC_CLASS,
+                                       GET_VERSION_FRAME_RESPONSE_BYTE,
+                                       0x00, 0x00, NULL, 0, FALSE,
+                                       get_version_frame3, cookie);
+               if (err < 0) {
+                       near_error("get version2 req failed %d", err);
+                       goto out_err;
+               }
+       } else {
+               near_error("get version response %02X", resp[length - 1]);
+               err = -EIO;
+               goto out_err;
+       }
+
+       return 0;
+
+out_err:
+       return t4_cookie_release(err, cookie);
+}
+
+/* Steps to format Type 4 (MIFARE DESFire EV1) tag as per AN1104.pdf from nxp.
+ * 1) Get version to determine memory size of tag
+ * 2) Select applciation with AID equal to 000000h (PICC level)
+ * 3) Create application with AID equal to 000001h
+ * 4) Select application (Select previously created application in step3)
+ * 5) Create std data file with File number equal to 01h (CC file), ISOFileID
+ *    equal to E103h, ComSet equal to 00h, AccesRights to EEEEh, FileSize bigger
+ *    equal to 00000Fh
+ * 6) Write data to CC file with CCLEN equal to 000Fh, Mapping version equal to
+ *    20h, MLe equal to 003Bh, MLc equal to 0034h, and NDEF File control TLV
+ *    equal to: T=04h, L=06h, V=E1 04 (NDEF ISO FID = E104h), 08 00 (NDEF File
+ *    size = 2048 Bytes) 00 (free read access) 00 (free write access)
+ * 7) Create std data file with File number equal to 02h (NDEF File DESFireFId),
+ *    ISO FileID equal to E104h, ComSet equal to 00h, ComSet equal to 00h,
+ *    AccessRights equal to EEE0h, FileSize equal to 000800h (2048 bytes)
+ * 8) Write data to write content of the NDEF File with NLEN equal to 0000h, and
+ *    no NDEF messsage.
+ * 9) Now Formatting is done, then select ISO appname2, select CC file and read.
+ * 10) Select NDEF file (by doing last two steps means, making sure that read
+ *    write operations perform on NDEF file).
+ * */
+
+static int nfctype4_format(uint32_t adapter_idx, uint32_t target_idx,
+                                               near_tag_io_cb cb)
+{
+       int err;
+       struct t4_cookie *cookie;
+
+       DBG("");
+
+       cookie = g_try_malloc0(sizeof(struct t4_cookie));
+       if (cookie == NULL)
+               return -ENOMEM;
+
+       cookie->adapter_idx = adapter_idx;
+       cookie->target_idx = target_idx;
+       cookie->cb = cb;
+
+       /* Step1 : Get Version */
+       err = ISO_send_cmd(PICC_CLASS, GET_VERSION,
+                               0x00, 0x00, NULL, 0, FALSE,
+                               get_version_frame2, cookie);
+
+       if (err < 0) {
+               near_error("get version req failed %d", err);
+               g_free(cookie);
+       }
+
+       return err;
+}
+
 static struct near_tag_driver type4_driver = {
        .type           = NFC_PROTO_ISO14443,
        .priority       = NEAR_TAG_PRIORITY_DEFAULT,
        .read           = nfctype4_read,
        .write          = nfctype4_write,
        .check_presence = nfctype4_check_presence,
+       .format         = nfctype4_format,
 };
 
 static int nfctype4_init(void)