Home | History | Annotate | Download | only in dm
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2010-2014 Broadcom Corporation
      4  *
      5  *  Licensed under the Apache License, Version 2.0 (the "License");
      6  *  you may not use this file except in compliance with the License.
      7  *  You may obtain a copy of the License at:
      8  *
      9  *  http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  *
     17  ******************************************************************************/
     18 
     19 /******************************************************************************
     20  *
     21  *  Handle ndef messages
     22  *
     23  ******************************************************************************/
     24 #include <string.h>
     25 #include "ndef_utils.h"
     26 #include "nfa_api.h"
     27 #include "nfa_dm_int.h"
     28 #include "nfa_sys.h"
     29 #include "nfa_sys_int.h"
     30 #include "nfc_api.h"
     31 
     32 /*******************************************************************************
     33 * URI Well-known-type prefixes
     34 *******************************************************************************/
     35 const uint8_t* nfa_dm_ndef_wkt_uri_str_tbl[] = {
     36     NULL,                                         /* 0x00 */
     37     (const uint8_t*)"http://www.",                /* 0x01 */
     38     (const uint8_t*)"https://www.",               /* 0x02 */
     39     (const uint8_t*)"http://",                    /* 0x03 */
     40     (const uint8_t*)"https://",                   /* 0x04 */
     41     (const uint8_t*)"tel:",                       /* 0x05 */
     42     (const uint8_t*)"mailto:",                    /* 0x06 */
     43     (const uint8_t*)"ftp://anonymous:anonymous@", /* 0x07 */
     44     (const uint8_t*)"ftp://ftp.",                 /* 0x08 */
     45     (const uint8_t*)"ftps://",                    /* 0x09 */
     46     (const uint8_t*)"sftp://",                    /* 0x0A */
     47     (const uint8_t*)"smb://",                     /* 0x0B */
     48     (const uint8_t*)"nfs://",                     /* 0x0C */
     49     (const uint8_t*)"ftp://",                     /* 0x0D */
     50     (const uint8_t*)"dav://",                     /* 0x0E */
     51     (const uint8_t*)"news:",                      /* 0x0F */
     52     (const uint8_t*)"telnet://",                  /* 0x10 */
     53     (const uint8_t*)"imap:",                      /* 0x11 */
     54     (const uint8_t*)"rtsp://",                    /* 0x12 */
     55     (const uint8_t*)"urn:",                       /* 0x13 */
     56     (const uint8_t*)"pop:",                       /* 0x14 */
     57     (const uint8_t*)"sip:",                       /* 0x15 */
     58     (const uint8_t*)"sips:",                      /* 0x16 */
     59     (const uint8_t*)"tftp:",                      /* 0x17 */
     60     (const uint8_t*)"btspp://",                   /* 0x18 */
     61     (const uint8_t*)"btl2cap://",                 /* 0x19 */
     62     (const uint8_t*)"btgoep://",                  /* 0x1A */
     63     (const uint8_t*)"tcpobex://",                 /* 0x1B */
     64     (const uint8_t*)"irdaobex://",                /* 0x1C */
     65     (const uint8_t*)"file://",                    /* 0x1D */
     66     (const uint8_t*)"urn:epc:id:",                /* 0x1E */
     67     (const uint8_t*)"urn:epc:tag:",               /* 0x1F */
     68     (const uint8_t*)"urn:epc:pat:",               /* 0x20 */
     69     (const uint8_t*)"urn:epc:raw:",               /* 0x21 */
     70     (const uint8_t*)"urn:epc:",                   /* 0x22 */
     71     (const uint8_t*)"urn:nfc:"                    /* 0x23 */
     72 };
     73 #define NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE \
     74   (sizeof(nfa_dm_ndef_wkt_uri_str_tbl) / sizeof(uint8_t*))
     75 
     76 /*******************************************************************************
     77 **
     78 ** Function         nfa_dm_ndef_dereg_hdlr_by_handle
     79 **
     80 ** Description      Deregister NDEF record type handler
     81 **
     82 ** Returns          TRUE (message buffer to be freed by caller)
     83 **
     84 *******************************************************************************/
     85 void nfa_dm_ndef_dereg_hdlr_by_handle(tNFA_HANDLE ndef_type_handle) {
     86   tNFA_DM_CB* p_cb = &nfa_dm_cb;
     87   uint16_t hdlr_idx;
     88   hdlr_idx = (uint16_t)(ndef_type_handle & NFA_HANDLE_MASK);
     89 
     90   if (p_cb->p_ndef_handler[hdlr_idx]) {
     91     GKI_freebuf(p_cb->p_ndef_handler[hdlr_idx]);
     92     p_cb->p_ndef_handler[hdlr_idx] = NULL;
     93   }
     94 }
     95 
     96 /*******************************************************************************
     97 **
     98 ** Function         nfa_dm_ndef_dereg_all
     99 **
    100 ** Description      Deregister all NDEF record type handlers (called during
    101 **                  shutdown(.
    102 **
    103 ** Returns          Nothing
    104 **
    105 *******************************************************************************/
    106 void nfa_dm_ndef_dereg_all(void) {
    107   tNFA_DM_CB* p_cb = &nfa_dm_cb;
    108   uint32_t i;
    109 
    110   for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) {
    111     /* If this is a free slot, then remember it */
    112     if (p_cb->p_ndef_handler[i] != NULL) {
    113       GKI_freebuf(p_cb->p_ndef_handler[i]);
    114       p_cb->p_ndef_handler[i] = NULL;
    115     }
    116   }
    117 }
    118 
    119 /*******************************************************************************
    120 **
    121 ** Function         nfa_dm_ndef_reg_hdlr
    122 **
    123 ** Description      Register NDEF record type handler
    124 **
    125 ** Returns          TRUE if message buffer is to be freed by caller
    126 **
    127 *******************************************************************************/
    128 bool nfa_dm_ndef_reg_hdlr(tNFA_DM_MSG* p_data) {
    129   tNFA_DM_CB* p_cb = &nfa_dm_cb;
    130   uint32_t hdlr_idx, i;
    131   tNFA_DM_API_REG_NDEF_HDLR* p_reg_info = (tNFA_DM_API_REG_NDEF_HDLR*)p_data;
    132   tNFA_NDEF_REGISTER ndef_register;
    133 
    134   /* If registering default handler, check to see if one is already registered
    135    */
    136   if (p_reg_info->tnf == NFA_TNF_DEFAULT) {
    137     /* check if default handler is already registered */
    138     if (p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX]) {
    139       NFA_TRACE_WARNING0("Default NDEF handler being changed.");
    140 
    141       /* Free old registration info */
    142       nfa_dm_ndef_dereg_hdlr_by_handle(
    143           (tNFA_HANDLE)NFA_NDEF_DEFAULT_HANDLER_IDX);
    144     }
    145     NFA_TRACE_DEBUG0("Default NDEF handler successfully registered.");
    146     hdlr_idx = NFA_NDEF_DEFAULT_HANDLER_IDX;
    147   }
    148   /* Get available entry in ndef_handler table, and check if requested type is
    149      already registered */
    150   else {
    151     hdlr_idx = NFA_HANDLE_INVALID;
    152 
    153     /* Check if this type is already registered */
    154     for (i = (NFA_NDEF_DEFAULT_HANDLER_IDX + 1); i < NFA_NDEF_MAX_HANDLERS;
    155          i++) {
    156       /* If this is a free slot, then remember it */
    157       if (p_cb->p_ndef_handler[i] == NULL) {
    158         hdlr_idx = i;
    159         break;
    160       }
    161     }
    162   }
    163 
    164   if (hdlr_idx != NFA_HANDLE_INVALID) {
    165     /* Update the table */
    166     p_cb->p_ndef_handler[hdlr_idx] = p_reg_info;
    167 
    168     p_reg_info->ndef_type_handle =
    169         (tNFA_HANDLE)(NFA_HANDLE_GROUP_NDEF_HANDLER | hdlr_idx);
    170 
    171     ndef_register.ndef_type_handle = p_reg_info->ndef_type_handle;
    172     ndef_register.status = NFA_STATUS_OK;
    173 
    174     NFA_TRACE_DEBUG1("NDEF handler successfully registered. Handle=0x%08x",
    175                      p_reg_info->ndef_type_handle);
    176     (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT,
    177                                   (tNFA_NDEF_EVT_DATA*)&ndef_register);
    178 
    179     /* indicate that we will free message buffer when type_handler is
    180      * deregistered */
    181     return false;
    182   } else {
    183     /* Error */
    184     NFA_TRACE_ERROR0("NDEF handler failed to register.");
    185     ndef_register.ndef_type_handle = NFA_HANDLE_INVALID;
    186     ndef_register.status = NFA_STATUS_FAILED;
    187     (*(p_reg_info->p_ndef_cback))(NFA_NDEF_REGISTER_EVT,
    188                                   (tNFA_NDEF_EVT_DATA*)&ndef_register);
    189 
    190     return true;
    191   }
    192 }
    193 
    194 /*******************************************************************************
    195 **
    196 ** Function         nfa_dm_ndef_dereg_hdlr
    197 **
    198 ** Description      Deregister NDEF record type handler
    199 **
    200 ** Returns          TRUE (message buffer to be freed by caller)
    201 **
    202 *******************************************************************************/
    203 bool nfa_dm_ndef_dereg_hdlr(tNFA_DM_MSG* p_data) {
    204   tNFA_DM_API_DEREG_NDEF_HDLR* p_dereginfo =
    205       (tNFA_DM_API_DEREG_NDEF_HDLR*)p_data;
    206 
    207   /* Make sure this is a NDEF_HDLR handle */
    208   if (((p_dereginfo->ndef_type_handle & NFA_HANDLE_GROUP_MASK) !=
    209        NFA_HANDLE_GROUP_NDEF_HANDLER) ||
    210       ((p_dereginfo->ndef_type_handle & NFA_HANDLE_MASK) >=
    211        NFA_NDEF_MAX_HANDLERS)) {
    212     NFA_TRACE_ERROR1("Invalid handle for NDEF type handler: 0x%08x",
    213                      p_dereginfo->ndef_type_handle);
    214   } else {
    215     nfa_dm_ndef_dereg_hdlr_by_handle(p_dereginfo->ndef_type_handle);
    216   }
    217 
    218   return true;
    219 }
    220 
    221 /*******************************************************************************
    222 **
    223 ** Function         nfa_dm_ndef_find_next_handler
    224 **
    225 ** Description      Find next ndef handler for a given record type
    226 **
    227 ** Returns          void
    228 **
    229 *******************************************************************************/
    230 tNFA_DM_API_REG_NDEF_HDLR* nfa_dm_ndef_find_next_handler(
    231     tNFA_DM_API_REG_NDEF_HDLR* p_init_handler, uint8_t tnf,
    232     uint8_t* p_type_name, uint8_t type_name_len, uint8_t* p_payload,
    233     uint32_t payload_len) {
    234   tNFA_DM_CB* p_cb = &nfa_dm_cb;
    235   uint8_t i;
    236 
    237   /* if init_handler is NULL, then start with the first non-default handler */
    238   if (!p_init_handler)
    239     i = NFA_NDEF_DEFAULT_HANDLER_IDX + 1;
    240   else {
    241     /* Point to handler index after p_init_handler */
    242     i = (p_init_handler->ndef_type_handle & NFA_HANDLE_MASK) + 1;
    243   }
    244 
    245   /* Look for next handler */
    246   for (; i < NFA_NDEF_MAX_HANDLERS; i++) {
    247     /* Check if TNF matches */
    248     if ((p_cb->p_ndef_handler[i]) && (p_cb->p_ndef_handler[i]->tnf == tnf)) {
    249       /* TNF matches. */
    250       /* If handler is for a specific URI type, check if type is WKT URI, */
    251       /* and that the URI prefix abrieviation for this handler matches */
    252       if (p_cb->p_ndef_handler[i]->flags & NFA_NDEF_FLAGS_WKT_URI) {
    253         /* This is a handler for a specific URI type */
    254         /* Check if this recurd is WKT URI */
    255         if ((p_payload) && (type_name_len == 1) && (*p_type_name == 'U')) {
    256           /* Check if URI prefix abrieviation matches */
    257           if ((payload_len > 1) &&
    258               (p_payload[0] == p_cb->p_ndef_handler[i]->uri_id)) {
    259             /* URI prefix abrieviation matches */
    260             /* If handler does not specify an absolute URI, then match found. */
    261             /* If absolute URI, then compare URI for match (skip over uri_id in
    262              * ndef payload) */
    263             if ((p_cb->p_ndef_handler[i]->uri_id != NFA_NDEF_URI_ID_ABSOLUTE) ||
    264                 (memcmp(&p_payload[1], p_cb->p_ndef_handler[i]->name,
    265                         p_cb->p_ndef_handler[i]->name_len) == 0)) {
    266               /* Handler found. */
    267               break;
    268             }
    269           }
    270           /* Check if handler is absolute URI but NDEF is using prefix
    271              abrieviation */
    272           else if ((p_cb->p_ndef_handler[i]->uri_id ==
    273                     NFA_NDEF_URI_ID_ABSOLUTE) &&
    274                    (p_payload[0] != NFA_NDEF_URI_ID_ABSOLUTE)) {
    275             /* Handler is absolute URI but NDEF is using prefix abrieviation.
    276              * Compare URI prefix */
    277             if ((p_payload[0] < NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) &&
    278                 (memcmp(p_cb->p_ndef_handler[i]->name,
    279                         (char*)nfa_dm_ndef_wkt_uri_str_tbl[p_payload[0]],
    280                         p_cb->p_ndef_handler[i]->name_len) == 0)) {
    281               /* Handler found. */
    282               break;
    283             }
    284           }
    285           /* Check if handler is using prefix abrieviation, but NDEF is using
    286              absolute URI */
    287           else if ((p_cb->p_ndef_handler[i]->uri_id !=
    288                     NFA_NDEF_URI_ID_ABSOLUTE) &&
    289                    (p_payload[0] == NFA_NDEF_URI_ID_ABSOLUTE)) {
    290             /* Handler is using prefix abrieviation, but NDEF is using absolute
    291              * URI. Compare URI prefix */
    292             if ((p_cb->p_ndef_handler[i]->uri_id <
    293                  NFA_DM_NDEF_WKT_URI_STR_TBL_SIZE) &&
    294                 (memcmp(&p_payload[1],
    295                         nfa_dm_ndef_wkt_uri_str_tbl[p_cb->p_ndef_handler[i]
    296                                                         ->uri_id],
    297                         strlen((const char*)nfa_dm_ndef_wkt_uri_str_tbl
    298                                    [p_cb->p_ndef_handler[i]->uri_id])) == 0)) {
    299               /* Handler found. */
    300               break;
    301             }
    302           }
    303         }
    304       }
    305       /* Not looking for specific URI. Check if type_name for this handler
    306          matches the NDEF record's type_name */
    307       else if (p_cb->p_ndef_handler[i]->name_len == type_name_len) {
    308         if ((type_name_len == 0) || (memcmp(p_cb->p_ndef_handler[i]->name,
    309                                             p_type_name, type_name_len) == 0)) {
    310           /* Handler found */
    311           break;
    312         }
    313       }
    314     }
    315   }
    316 
    317   if (i < NFA_NDEF_MAX_HANDLERS)
    318     return (p_cb->p_ndef_handler[i]);
    319   else
    320     return (NULL);
    321 }
    322 
    323 /*******************************************************************************
    324 **
    325 ** Function         nfa_dm_ndef_clear_notified_flag
    326 **
    327 ** Description      Clear 'whole_message_notified' flag for all the handlers
    328 **                  (flag used to indicate that this handler has already
    329 **                  handled the entire incoming NDEF message)
    330 **
    331 ** Returns          void
    332 **
    333 *******************************************************************************/
    334 void nfa_dm_ndef_clear_notified_flag(void) {
    335   tNFA_DM_CB* p_cb = &nfa_dm_cb;
    336   uint8_t i;
    337 
    338   for (i = 0; i < NFA_NDEF_MAX_HANDLERS; i++) {
    339     if (p_cb->p_ndef_handler[i]) {
    340       p_cb->p_ndef_handler[i]->flags &= ~NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED;
    341     }
    342   }
    343 }
    344 
    345 /*******************************************************************************
    346 **
    347 ** Function         nfa_dm_ndef_handle_message
    348 **
    349 ** Description      Handle incoming ndef message
    350 **
    351 ** Returns          void
    352 **
    353 *******************************************************************************/
    354 void nfa_dm_ndef_handle_message(tNFA_STATUS status, uint8_t* p_msg_buf,
    355                                 uint32_t len) {
    356   tNFA_DM_CB* p_cb = &nfa_dm_cb;
    357   tNDEF_STATUS ndef_status;
    358   uint8_t *p_rec, *p_ndef_start, *p_type, *p_payload, *p_rec_end;
    359   uint32_t payload_len;
    360   uint8_t tnf, type_len, rec_hdr_flags, id_len;
    361   tNFA_DM_API_REG_NDEF_HDLR* p_handler;
    362   tNFA_NDEF_DATA ndef_data;
    363   uint8_t rec_count = 0;
    364   bool record_handled, entire_message_handled;
    365 
    366   NFA_TRACE_DEBUG3("nfa_dm_ndef_handle_message status=%i, msgbuf=%08x, len=%i",
    367                    status, p_msg_buf, len);
    368 
    369   if (status != NFA_STATUS_OK) {
    370     /* If problem reading NDEF message, then exit (no action required) */
    371     return;
    372   }
    373 
    374   /* If in exclusive RF mode is activer, then route NDEF message callback
    375    * registered with NFA_StartExclusiveRfControl */
    376   if ((p_cb->flags & NFA_DM_FLAGS_EXCL_RF_ACTIVE) &&
    377       (p_cb->p_excl_ndef_cback)) {
    378     /* No ndef-handler handle, since this callback is not from
    379      * RegisterNDefHandler */
    380     ndef_data.ndef_type_handle = 0;
    381     ndef_data.p_data = p_msg_buf;
    382     ndef_data.len = len;
    383     (*p_cb->p_excl_ndef_cback)(NFA_NDEF_DATA_EVT,
    384                                (tNFA_NDEF_EVT_DATA*)&ndef_data);
    385     return;
    386   }
    387 
    388   /* Handle zero length - notify default handler */
    389   if (len == 0) {
    390     p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX];
    391     if (p_handler != NULL) {
    392       NFA_TRACE_DEBUG0(
    393           "Notifying default handler of zero-length NDEF message...");
    394       ndef_data.ndef_type_handle = p_handler->ndef_type_handle;
    395       ndef_data.p_data = NULL; /* Start of record */
    396       ndef_data.len = 0;
    397       (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT,
    398                                  (tNFA_NDEF_EVT_DATA*)&ndef_data);
    399     }
    400     return;
    401   }
    402 
    403   /* Validate the NDEF message */
    404   ndef_status = NDEF_MsgValidate(p_msg_buf, len, true);
    405   if (ndef_status != NDEF_OK) {
    406     NFA_TRACE_ERROR1("Received invalid NDEF message. NDEF status=0x%x",
    407                      ndef_status);
    408     return;
    409   }
    410 
    411   /* NDEF message received from backgound polling. Pass the NDEF message to the
    412    * NDEF handlers */
    413 
    414   /* New NDEF message. Clear 'notified' flag for all the handlers */
    415   nfa_dm_ndef_clear_notified_flag();
    416 
    417   /* Indicate that no handler has handled this entire NDEF message (e.g.
    418    * connection-handover handler *) */
    419   entire_message_handled = false;
    420 
    421   /* Get first record in message */
    422   p_rec = p_ndef_start = p_msg_buf;
    423 
    424   /* Check each record in the NDEF message */
    425   while (p_rec != NULL) {
    426     /* Get record type */
    427     p_type = NDEF_RecGetType(p_rec, &tnf, &type_len);
    428 
    429     /* Indicate record not handled yet */
    430     record_handled = false;
    431 
    432     /* Get pointer to record payload */
    433     p_payload = NDEF_RecGetPayload(p_rec, &payload_len);
    434 
    435     /* Find first handler for this type */
    436     p_handler = nfa_dm_ndef_find_next_handler(NULL, tnf, p_type, type_len,
    437                                               p_payload, payload_len);
    438     if (p_handler == NULL) {
    439       /* Not a registered NDEF type. Use default handler */
    440       p_handler = p_cb->p_ndef_handler[NFA_NDEF_DEFAULT_HANDLER_IDX];
    441       if (p_handler != NULL) {
    442         NFA_TRACE_DEBUG0("No handler found. Using default handler...");
    443       }
    444     }
    445 
    446     while (p_handler) {
    447       /* If handler is for whole NDEF message, and it has already been notified,
    448        * then skip notification */
    449       if (p_handler->flags & NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED) {
    450         /* Look for next handler */
    451         p_handler = nfa_dm_ndef_find_next_handler(
    452             p_handler, tnf, p_type, type_len, p_payload, payload_len);
    453         continue;
    454       }
    455 
    456       /* Get pointer to record payload */
    457       NFA_TRACE_DEBUG1("Calling ndef type handler (%x)",
    458                        p_handler->ndef_type_handle);
    459 
    460       ndef_data.ndef_type_handle = p_handler->ndef_type_handle;
    461       ndef_data.p_data = p_rec; /* Start of record */
    462 
    463       /* Calculate length of NDEF record */
    464       if (p_payload != NULL)
    465         ndef_data.len = payload_len + (uint32_t)(p_payload - p_rec);
    466       else {
    467         /* If no payload, calculate length of ndef record header */
    468         p_rec_end = p_rec;
    469 
    470         /* First byte is the header flags */
    471         rec_hdr_flags = *p_rec_end++;
    472 
    473         /* Next byte is the type field length */
    474         type_len = *p_rec_end++;
    475 
    476         /* Next is the payload length (1 or 4 bytes) */
    477         if (rec_hdr_flags & NDEF_SR_MASK) {
    478           p_rec_end++;
    479         } else {
    480           p_rec_end += 4;
    481         }
    482 
    483         /* ID field Length */
    484         if (rec_hdr_flags & NDEF_IL_MASK)
    485           id_len = *p_rec_end++;
    486         else
    487           id_len = 0;
    488         p_rec_end += id_len;
    489 
    490         ndef_data.len = (uint32_t)(p_rec_end - p_rec);
    491       }
    492 
    493       /* If handler wants entire ndef message, then pass pointer to start of
    494        * message and  */
    495       /* set 'notified' flag so handler won't get notified on subsequent records
    496        * for this */
    497       /* NDEF message. */
    498       if (p_handler->flags & NFA_NDEF_FLAGS_HANDLE_WHOLE_MESSAGE) {
    499         ndef_data.p_data = p_ndef_start; /* Start of NDEF message */
    500         ndef_data.len = len;
    501         p_handler->flags |= NFA_NDEF_FLAGS_WHOLE_MESSAGE_NOTIFIED;
    502 
    503         /* Indicate that at least one handler has received entire NDEF message
    504          */
    505         entire_message_handled = true;
    506       }
    507 
    508       /* Notify NDEF type handler */
    509       (*p_handler->p_ndef_cback)(NFA_NDEF_DATA_EVT,
    510                                  (tNFA_NDEF_EVT_DATA*)&ndef_data);
    511 
    512       /* Indicate that at lease one handler has received this record */
    513       record_handled = true;
    514 
    515       /* Look for next handler */
    516       p_handler = nfa_dm_ndef_find_next_handler(
    517           p_handler, tnf, p_type, type_len, p_payload, payload_len);
    518     }
    519 
    520     /* Check if at least one handler was notified of this record (only happens
    521      * if no default handler was register) */
    522     if ((!record_handled) && (!entire_message_handled)) {
    523       /* Unregistered NDEF record type; no default handler */
    524       NFA_TRACE_WARNING1("Unhandled NDEF record (#%i)", rec_count);
    525     }
    526 
    527     rec_count++;
    528     p_rec = NDEF_MsgGetNextRec(p_rec);
    529   }
    530 }
    531