ndef_utils.c revision 45faad0ff5deeb0c676356345d99398cc4ab695a
1/******************************************************************************
2**
3** File:         ndef_utils.c
4**
5** Description:   This file contains source code for some utility functions to
6**                help parse and build NFC Data Exchange Format (NDEF) messages
7**
8** Copyright (c) 2010-2010  Broadcom Corp.  All Rights Reserved.
9** Broadcom Bluetooth Core. Proprietary and confidential.
10**
11******************************************************************************/
12#include <string.h>
13#include "ndef_utils.h"
14#include "gki.h"
15
16
17
18/*******************************************************************************
19**
20** Function         NDEF_MsgValidate
21**
22** Description      This function validates an NDEF message.
23**
24** Returns          TRUE if all OK, or FALSE if the message is invalid.
25**
26*******************************************************************************/
27tNDEF_STATUS NDEF_MsgValidate (UINT8 *p_msg, UINT32 msg_len, BOOLEAN b_allow_chunks)
28{
29    UINT8   *p_rec = p_msg;
30    UINT8   *p_end = p_msg + msg_len;
31    UINT8   rec_hdr=0, type_len, id_len;
32    int     count;
33    UINT32  payload_len;
34    BOOLEAN bInChunk = FALSE;
35
36    if ( (p_msg == NULL) || (msg_len < 3) )
37        return (NDEF_MSG_TOO_SHORT);
38
39    /* The first record must have the MB bit set */
40    if ((*p_msg & NDEF_MB_MASK) == 0)
41        return (NDEF_MSG_NO_MSG_BEGIN);
42
43    /* The first record cannot be a chunk */
44    if ((*p_msg & NDEF_TNF_MASK) == NDEF_TNF_UNCHANGED)
45        return (NDEF_MSG_UNEXPECTED_CHUNK);
46
47    for (count = 0; p_rec < p_end; count++)
48    {
49        /* if less than short record header */
50        if (p_rec + 3 > p_end)
51            return (NDEF_MSG_TOO_SHORT);
52
53        rec_hdr = *p_rec++;
54
55        /* The second and all subsequent records must NOT have the MB bit set */
56        if ( (count > 0) && (rec_hdr & NDEF_MB_MASK) )
57            return (NDEF_MSG_EXTRA_MSG_BEGIN);
58
59        /* Type field length */
60        type_len = *p_rec++;
61
62        /* Payload length - can be 1 or 4 bytes */
63        if (rec_hdr & NDEF_SR_MASK)
64            payload_len = *p_rec++;
65        else
66        {
67            /* if less than 4 bytes payload length */
68            if (p_rec + 4 > p_end)
69                return (NDEF_MSG_TOO_SHORT);
70
71            BE_STREAM_TO_UINT32 (payload_len, p_rec);
72        }
73
74        /* ID field Length */
75        if (rec_hdr & NDEF_IL_MASK)
76        {
77            /* if less than 1 byte ID field length */
78            if (p_rec + 1 > p_end)
79                return (NDEF_MSG_TOO_SHORT);
80
81            id_len = *p_rec++;
82        }
83        else
84            id_len = 0;
85
86        /* A chunk must have type "unchanged", and no type or ID fields */
87        if (rec_hdr & NDEF_CF_MASK)
88        {
89            if (!b_allow_chunks)
90                return (NDEF_MSG_UNEXPECTED_CHUNK);
91
92            /* Inside a chunk, the type must be unchanged and no type or ID field i sallowed */
93            if (bInChunk)
94            {
95                if ( (type_len != 0) || (id_len != 0) || ((rec_hdr & NDEF_TNF_MASK) != NDEF_TNF_UNCHANGED) )
96                    return (NDEF_MSG_INVALID_CHUNK);
97            }
98            else
99            {
100                /* First record of a chunk must NOT have type "unchanged" */
101                if ((rec_hdr & NDEF_TNF_MASK) == NDEF_TNF_UNCHANGED)
102                    return (NDEF_MSG_INVALID_CHUNK);
103
104                bInChunk = TRUE;
105            }
106        }
107        else
108        {
109            /* This may be the last guy in a chunk. */
110            if (bInChunk)
111            {
112                if ( (type_len != 0) || (id_len != 0) || ((rec_hdr & NDEF_TNF_MASK) != NDEF_TNF_UNCHANGED) )
113                    return (NDEF_MSG_INVALID_CHUNK);
114
115                bInChunk = FALSE;
116            }
117            else
118            {
119                /* If not in a chunk, the record must NOT have type "unchanged" */
120                if ((rec_hdr & NDEF_TNF_MASK) == NDEF_TNF_UNCHANGED)
121                    return (NDEF_MSG_INVALID_CHUNK);
122            }
123        }
124
125        /* An empty record must NOT have a type, ID or payload */
126        if ((rec_hdr & NDEF_TNF_MASK) == NDEF_TNF_EMPTY)
127        {
128            if ( (type_len != 0) || (id_len != 0) || (payload_len != 0) )
129                return (NDEF_MSG_INVALID_EMPTY_REC);
130        }
131
132        if ((rec_hdr & NDEF_TNF_MASK) == NDEF_TNF_UNKNOWN)
133        {
134            if (type_len != 0)
135                return (NDEF_MSG_LENGTH_MISMATCH);
136        }
137
138        /* Point to next record */
139        p_rec += (payload_len + type_len + id_len);
140
141        if (rec_hdr & NDEF_ME_MASK)
142            break;
143
144        rec_hdr = 0;
145    }
146
147    /* The last record should have the ME bit set */
148    if ((rec_hdr & NDEF_ME_MASK) == 0)
149        return (NDEF_MSG_NO_MSG_END);
150
151    /* p_rec should equal p_end if all the length fields were correct */
152    if (p_rec != p_end)
153        return (NDEF_MSG_LENGTH_MISMATCH);
154
155    return (NDEF_OK);
156}
157
158/*******************************************************************************
159**
160** Function         NDEF_MsgGetNumRecs
161**
162** Description      This function gets the number of records in the given NDEF
163**                  message.
164**
165** Returns          The record count, or 0 if the message is invalid.
166**
167*******************************************************************************/
168INT32 NDEF_MsgGetNumRecs (UINT8 *p_msg)
169{
170    UINT8   *p_rec = p_msg;
171    UINT8   rec_hdr, type_len, id_len;
172    int     count;
173    UINT32  payload_len;
174
175    for (count = 0; ; )
176    {
177        count++;
178
179        rec_hdr = *p_rec++;
180
181        if (rec_hdr & NDEF_ME_MASK)
182            break;
183
184        /* Type field length */
185        type_len = *p_rec++;
186
187        /* Payload length - can be 1 or 4 bytes */
188        if (rec_hdr & NDEF_SR_MASK)
189            payload_len = *p_rec++;
190        else
191            BE_STREAM_TO_UINT32 (payload_len, p_rec);
192
193        /* ID field Length */
194        if (rec_hdr & NDEF_IL_MASK)
195            id_len = *p_rec++;
196        else
197            id_len = 0;
198
199        /* Point to next record */
200        p_rec += (payload_len + type_len + id_len);
201    }
202
203    /* Return the number of records found */
204    return (count);
205}
206
207/*******************************************************************************
208**
209** Function         NDEF_MsgGetRecLength
210**
211** Description      This function returns length of the current record in the given
212**                  NDEF message.
213**
214** Returns          Length of record
215**
216*******************************************************************************/
217UINT32 NDEF_MsgGetRecLength (UINT8 *p_cur_rec)
218{
219    UINT8   rec_hdr, type_len, id_len;
220    UINT32  rec_len = 0;
221    UINT32  payload_len;
222
223    /* Get the current record's header */
224    rec_hdr = *p_cur_rec++;
225    rec_len++;
226
227    /* Type field length */
228    type_len = *p_cur_rec++;
229    rec_len++;
230
231    /* Payload length - can be 1 or 4 bytes */
232    if (rec_hdr & NDEF_SR_MASK)
233    {
234        payload_len = *p_cur_rec++;
235        rec_len++;
236    }
237    else
238    {
239        BE_STREAM_TO_UINT32 (payload_len, p_cur_rec);
240        rec_len += 4;
241    }
242
243    /* ID field Length */
244    if (rec_hdr & NDEF_IL_MASK)
245    {
246        id_len = *p_cur_rec++;
247        rec_len++;
248    }
249    else
250        id_len = 0;
251
252    /* Total length of record */
253    rec_len += (payload_len + type_len + id_len);
254
255    return (rec_len);
256}
257
258/*******************************************************************************
259**
260** Function         NDEF_MsgGetNextRec
261**
262** Description      This function gets a pointer to the next record in the given
263**                  NDEF message. If the current record pointer is NULL, a pointer
264**                  to the first record is returned.
265**
266** Returns          Pointer to the start of the record, or NULL if no more
267**
268*******************************************************************************/
269UINT8 *NDEF_MsgGetNextRec (UINT8 *p_cur_rec)
270{
271    UINT8   rec_hdr, type_len, id_len;
272    UINT32  payload_len;
273
274    /* Get the current record's header */
275    rec_hdr = *p_cur_rec++;
276
277    /* If this is the last record, return NULL */
278    if (rec_hdr & NDEF_ME_MASK)
279        return (NULL);
280
281    /* Type field length */
282    type_len = *p_cur_rec++;
283
284    /* Payload length - can be 1 or 4 bytes */
285    if (rec_hdr & NDEF_SR_MASK)
286        payload_len = *p_cur_rec++;
287    else
288        BE_STREAM_TO_UINT32 (payload_len, p_cur_rec);
289
290    /* ID field Length */
291    if (rec_hdr & NDEF_IL_MASK)
292        id_len = *p_cur_rec++;
293    else
294        id_len = 0;
295
296    /* Point to next record */
297    p_cur_rec += (payload_len + type_len + id_len);
298
299    return (p_cur_rec);
300}
301
302/*******************************************************************************
303**
304** Function         NDEF_MsgGetRecByIndex
305**
306** Description      This function gets a pointer to the record with the given
307**                  index (0-based index) in the given NDEF message.
308**
309** Returns          Pointer to the start of the record, or NULL
310**
311*******************************************************************************/
312UINT8 *NDEF_MsgGetRecByIndex (UINT8 *p_msg, INT32 index)
313{
314    UINT8   *p_rec = p_msg;
315    UINT8   rec_hdr, type_len, id_len;
316    INT32   count;
317    UINT32  payload_len;
318
319    for (count = 0; ; count++)
320    {
321        if (count == index)
322            return (p_rec);
323
324        rec_hdr = *p_rec++;
325
326        if (rec_hdr & NDEF_ME_MASK)
327            return (NULL);
328
329        /* Type field length */
330        type_len = *p_rec++;
331
332        /* Payload length - can be 1 or 4 bytes */
333        if (rec_hdr & NDEF_SR_MASK)
334            payload_len = *p_rec++;
335        else
336            BE_STREAM_TO_UINT32 (payload_len, p_rec);
337
338        /* ID field Length */
339        if (rec_hdr & NDEF_IL_MASK)
340            id_len = *p_rec++;
341        else
342            id_len = 0;
343
344        /* Point to next record */
345        p_rec += (payload_len + type_len + id_len);
346    }
347
348    /* If here, there is no record of that index */
349    return (NULL);
350}
351
352
353/*******************************************************************************
354**
355** Function         NDEF_MsgGetLastRecInMsg
356**
357** Description      This function gets a pointer to the last record in the
358**                  given NDEF message.
359**
360** Returns          Pointer to the start of the last record, or NULL if some problem
361**
362*******************************************************************************/
363UINT8 *NDEF_MsgGetLastRecInMsg (UINT8 *p_msg)
364{
365    UINT8   *p_rec = p_msg;
366    UINT8   *pRecStart;
367    UINT8   rec_hdr, type_len, id_len;
368    UINT32  payload_len;
369
370    for ( ; ; )
371    {
372        pRecStart = p_rec;
373        rec_hdr = *p_rec++;
374
375        if (rec_hdr & NDEF_ME_MASK)
376            break;
377
378        /* Type field length */
379        type_len = *p_rec++;
380
381        /* Payload length - can be 1 or 4 bytes */
382        if (rec_hdr & NDEF_SR_MASK)
383            payload_len = *p_rec++;
384        else
385            BE_STREAM_TO_UINT32 (payload_len, p_rec);
386
387        /* ID field Length */
388        if (rec_hdr & NDEF_IL_MASK)
389            id_len = *p_rec++;
390        else
391            id_len = 0;
392
393        /* Point to next record */
394        p_rec += (payload_len + type_len + id_len);
395    }
396
397    return (pRecStart);
398}
399
400
401/*******************************************************************************
402**
403** Function         NDEF_MsgGetFirstRecByType
404**
405** Description      This function gets a pointer to the first record with the given
406**                  record type in the given NDEF message.
407**
408** Returns          Pointer to the start of the record, or NULL
409**
410*******************************************************************************/
411UINT8 *NDEF_MsgGetFirstRecByType (UINT8 *p_msg, UINT8 tnf, UINT8 *p_type, UINT8 tlen)
412{
413    UINT8   *p_rec = p_msg;
414    UINT8   *pRecStart;
415    UINT8   rec_hdr, type_len, id_len;
416    UINT32  payload_len;
417
418    for ( ; ; )
419    {
420        pRecStart = p_rec;
421
422        rec_hdr = *p_rec++;
423
424        /* Type field length */
425        type_len = *p_rec++;
426
427        /* Payload length - can be 1 or 4 bytes */
428        if (rec_hdr & NDEF_SR_MASK)
429            payload_len = *p_rec++;
430        else
431            BE_STREAM_TO_UINT32 (payload_len, p_rec);
432
433        /* ID field Length */
434        if (rec_hdr & NDEF_IL_MASK)
435            id_len = *p_rec++;
436        else
437            id_len = 0;
438
439        /* At this point, p_rec points to the start of the type field. We need to */
440        /* compare the type of the type, the length of the type and the data     */
441        if ( ((rec_hdr & NDEF_TNF_MASK) == tnf)
442         &&  (type_len == tlen)
443         &&  (!memcmp (p_rec, p_type, tlen)) )
444             return (pRecStart);
445
446        /* If this was the last record, return NULL */
447        if (rec_hdr & NDEF_ME_MASK)
448            return (NULL);
449
450        /* Point to next record */
451        p_rec += (payload_len + type_len + id_len);
452    }
453
454    /* If here, there is no record of that type */
455    return (NULL);
456}
457
458/*******************************************************************************
459**
460** Function         NDEF_MsgGetNextRecByType
461**
462** Description      This function gets a pointer to the next record with the given
463**                  record type in the given NDEF message.
464**
465** Returns          Pointer to the start of the record, or NULL
466**
467*******************************************************************************/
468UINT8 *NDEF_MsgGetNextRecByType (UINT8 *p_cur_rec, UINT8 tnf, UINT8 *p_type, UINT8 tlen)
469{
470    UINT8   *p_rec;
471    UINT8   *pRecStart;
472    UINT8   rec_hdr, type_len, id_len;
473    UINT32  payload_len;
474
475    /* If this is the last record in the message, return NULL */
476    if ((p_rec = NDEF_MsgGetNextRec (p_cur_rec)) == NULL)
477        return (NULL);
478
479    for ( ; ; )
480    {
481        pRecStart = p_rec;
482
483        rec_hdr = *p_rec++;
484
485        /* Type field length */
486        type_len = *p_rec++;
487
488        /* Payload length - can be 1 or 4 bytes */
489        if (rec_hdr & NDEF_SR_MASK)
490            payload_len = *p_rec++;
491        else
492            BE_STREAM_TO_UINT32 (payload_len, p_rec);
493
494        /* ID field Length */
495        if (rec_hdr & NDEF_IL_MASK)
496            id_len = *p_rec++;
497        else
498            id_len = 0;
499
500        /* At this point, p_rec points to the start of the type field. We need to */
501        /* compare the type of the type, the length of the type and the data     */
502        if ( ((rec_hdr & NDEF_TNF_MASK) == tnf)
503         &&  (type_len == tlen)
504         &&  (!memcmp (p_rec, p_type, tlen)) )
505             return (pRecStart);
506
507        /* If this was the last record, return NULL */
508        if (rec_hdr & NDEF_ME_MASK)
509            break;
510
511        /* Point to next record */
512        p_rec += (payload_len + type_len + id_len);
513    }
514
515    /* If here, there is no record of that type */
516    return (NULL);
517}
518
519
520/*******************************************************************************
521**
522** Function         NDEF_MsgGetFirstRecById
523**
524** Description      This function gets a pointer to the first record with the given
525**                  record id in the given NDEF message.
526**
527** Returns          Pointer to the start of the record, or NULL
528**
529*******************************************************************************/
530UINT8 *NDEF_MsgGetFirstRecById (UINT8 *p_msg, UINT8 *p_id, UINT8 ilen)
531{
532    UINT8   *p_rec = p_msg;
533    UINT8   *pRecStart;
534    UINT8   rec_hdr, type_len, id_len;
535    UINT32  payload_len;
536
537    for ( ; ; )
538    {
539        pRecStart = p_rec;
540
541        rec_hdr = *p_rec++;
542
543        /* Type field length */
544        type_len = *p_rec++;
545
546        /* Payload length - can be 1 or 4 bytes */
547        if (rec_hdr & NDEF_SR_MASK)
548            payload_len = *p_rec++;
549        else
550            BE_STREAM_TO_UINT32 (payload_len, p_rec);
551
552        /* ID field Length */
553        if (rec_hdr & NDEF_IL_MASK)
554            id_len = *p_rec++;
555        else
556            id_len = 0;
557
558        /* At this point, p_rec points to the start of the type field. Skip it */
559        p_rec += type_len;
560
561        /* At this point, p_rec points to the start of the ID field. Compare length and data */
562        if ( (id_len == ilen) && (!memcmp (p_rec, p_id, ilen)) )
563             return (pRecStart);
564
565        /* If this was the last record, return NULL */
566        if (rec_hdr & NDEF_ME_MASK)
567            return (NULL);
568
569        /* Point to next record */
570        p_rec += (id_len + payload_len);
571    }
572
573    /* If here, there is no record of that ID */
574    return (NULL);
575}
576
577/*******************************************************************************
578**
579** Function         NDEF_MsgGetNextRecById
580**
581** Description      This function gets a pointer to the next record with the given
582**                  record id in the given NDEF message.
583**
584** Returns          Pointer to the start of the record, or NULL
585**
586*******************************************************************************/
587UINT8 *NDEF_MsgGetNextRecById (UINT8 *p_cur_rec, UINT8 *p_id, UINT8 ilen)
588{
589    UINT8   *p_rec;
590    UINT8   *pRecStart;
591    UINT8   rec_hdr, type_len, id_len;
592    UINT32  payload_len;
593
594    /* If this is the last record in the message, return NULL */
595    if ((p_rec = NDEF_MsgGetNextRec (p_cur_rec)) == NULL)
596        return (NULL);
597
598    for ( ; ; )
599    {
600        pRecStart = p_rec;
601
602        rec_hdr = *p_rec++;
603
604        /* Type field length */
605        type_len = *p_rec++;
606
607        /* Payload length - can be 1 or 4 bytes */
608        if (rec_hdr & NDEF_SR_MASK)
609            payload_len = *p_rec++;
610        else
611            BE_STREAM_TO_UINT32 (payload_len, p_rec);
612
613        /* ID field Length */
614        if (rec_hdr & NDEF_IL_MASK)
615            id_len = *p_rec++;
616        else
617            id_len = 0;
618
619        /* At this point, p_rec points to the start of the type field. Skip it */
620        p_rec += type_len;
621
622        /* At this point, p_rec points to the start of the ID field. Compare length and data */
623        if ( (id_len == ilen) && (!memcmp (p_rec, p_id, ilen)) )
624             return (pRecStart);
625
626        /* If this was the last record, return NULL */
627        if (rec_hdr & NDEF_ME_MASK)
628            break;
629
630        /* Point to next record */
631        p_rec += (id_len + payload_len);
632    }
633
634    /* If here, there is no record of that ID */
635    return (NULL);
636}
637
638/*******************************************************************************
639**
640** Function         NDEF_RecGetType
641**
642** Description      This function gets a pointer to the record type for the given NDEF record.
643**
644** Returns          Pointer to Type (NULL if none). TNF and len are filled in.
645**
646*******************************************************************************/
647UINT8 *NDEF_RecGetType (UINT8 *p_rec, UINT8 *p_tnf, UINT8 *p_type_len)
648{
649    UINT8   rec_hdr, type_len;
650
651    /* First byte is the record header */
652    rec_hdr = *p_rec++;
653
654    /* Next byte is the type field length */
655    type_len = *p_rec++;
656
657    /* Skip the payload length */
658    if (rec_hdr & NDEF_SR_MASK)
659        p_rec += 1;
660    else
661        p_rec += 4;
662
663    /* Skip ID field Length, if present */
664    if (rec_hdr & NDEF_IL_MASK)
665        p_rec++;
666
667    /* At this point, p_rec points to the start of the type field.  */
668    *p_type_len = type_len;
669    *p_tnf      = rec_hdr & NDEF_TNF_MASK;
670
671    if (type_len == 0)
672        return (NULL);
673    else
674        return (p_rec);
675}
676
677/*******************************************************************************
678**
679** Function         NDEF_RecGetId
680**
681** Description      This function gets a pointer to the record id for the given NDEF record.
682**
683** Returns          Pointer to Id (NULL if none). ID Len is filled in.
684**
685*******************************************************************************/
686UINT8 *NDEF_RecGetId (UINT8 *p_rec, UINT8 *p_id_len)
687{
688    UINT8   rec_hdr, type_len;
689
690    /* First byte is the record header */
691    rec_hdr = *p_rec++;
692
693    /* Next byte is the type field length */
694    type_len = *p_rec++;
695
696    /* Skip the payload length */
697    if (rec_hdr & NDEF_SR_MASK)
698        p_rec++;
699    else
700        p_rec += 4;
701
702    /* ID field Length */
703    if (rec_hdr & NDEF_IL_MASK)
704        *p_id_len = *p_rec++;
705    else
706        *p_id_len = 0;
707
708    /* p_rec now points to the start of the type field. The ID field follows it */
709    if (*p_id_len == 0)
710        return (NULL);
711    else
712        return (p_rec + type_len);
713}
714
715
716/*******************************************************************************
717**
718** Function         NDEF_RecGetPayload
719**
720** Description      This function gets a pointer to the payload for the given NDEF record.
721**
722** Returns          a pointer to the payload (or NULL none). Payload len filled in.
723**
724*******************************************************************************/
725UINT8 *NDEF_RecGetPayload (UINT8 *p_rec, UINT32 *p_payload_len)
726{
727    UINT8   rec_hdr, type_len, id_len;
728    UINT32  payload_len;
729
730    /* First byte is the record header */
731    rec_hdr = *p_rec++;
732
733    /* Next byte is the type field length */
734    type_len = *p_rec++;
735
736    /* Next is the payload length (1 or 4 bytes) */
737    if (rec_hdr & NDEF_SR_MASK)
738        payload_len = *p_rec++;
739    else
740        BE_STREAM_TO_UINT32 (payload_len, p_rec);
741
742    *p_payload_len = payload_len;
743
744    /* ID field Length */
745    if (rec_hdr & NDEF_IL_MASK)
746        id_len = *p_rec++;
747    else
748        id_len = 0;
749
750    /* p_rec now points to the start of the type field. The ID field follows it, then the payload */
751    if (payload_len == 0)
752        return (NULL);
753    else
754        return (p_rec + type_len + id_len);
755}
756
757
758/*******************************************************************************
759**
760** Function         NDEF_MsgInit
761**
762** Description      This function initializes an NDEF message.
763**
764** Returns          void
765**                  *p_cur_size is initialized to 0
766**
767*******************************************************************************/
768void NDEF_MsgInit (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size)
769{
770    *p_cur_size = 0;
771    memset (p_msg, 0, max_size);
772}
773
774/*******************************************************************************
775**
776** Function         NDEF_MsgAddRec
777**
778** Description      This function adds an NDEF record to the end of an NDEF message.
779**
780** Returns          OK, or error if the record did not fit
781**                  *p_cur_size is updated
782**
783*******************************************************************************/
784extern tNDEF_STATUS  NDEF_MsgAddRec (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
785                                     UINT8 tnf, UINT8 *p_type, UINT8 type_len,
786                                     UINT8 *p_id, UINT8  id_len,
787                                     UINT8 *p_payload, UINT32 payload_len)
788{
789    UINT8   *p_rec = p_msg + *p_cur_size;
790    UINT32  recSize;
791    int     plen = (payload_len < 256) ? 1 : 4;
792    int     ilen = (id_len == 0) ? 0 : 1;
793
794    /* First, make sure the record will fit. we need at least 2 bytes for header and type length */
795    recSize = payload_len + 2 + type_len + plen + ilen + id_len;
796
797    if ((*p_cur_size + recSize) > max_size)
798        return (NDEF_MSG_INSUFFICIENT_MEM);
799
800    if (tnf > NDEF_TNF_RESERVED)
801    {
802        tnf = NDEF_TNF_UNKNOWN;
803        type_len = 0;
804    }
805
806    /* Construct the record header. For the first record, set both begin and end bits */
807    if (*p_cur_size == 0)
808        *p_rec = tnf | NDEF_MB_MASK | NDEF_ME_MASK;
809    else
810    {
811        /* Find the previous last and clear his 'Message End' bit */
812        UINT8  *pLast = NDEF_MsgGetLastRecInMsg (p_msg);
813
814        if (!pLast)
815            return (FALSE);
816
817        *pLast &= ~NDEF_ME_MASK;
818        *p_rec   = tnf | NDEF_ME_MASK;
819    }
820
821    if (plen == 1)
822        *p_rec |= NDEF_SR_MASK;
823
824    if (ilen != 0)
825        *p_rec |= NDEF_IL_MASK;
826
827    p_rec++;
828
829    /* The next byte is the type field length */
830    *p_rec++ = type_len;
831
832    /* Payload length - can be 1 or 4 bytes */
833    if (plen == 1)
834        *p_rec++ = (UINT8)payload_len;
835    else
836         UINT32_TO_BE_STREAM (p_rec, payload_len);
837
838    /* ID field Length (optional) */
839    if (ilen > 0)
840        *p_rec++ = id_len;
841
842    /* Next comes the type */
843    if (type_len)
844    {
845        if (p_type)
846            memcpy (p_rec, p_type, type_len);
847
848        p_rec += type_len;
849    }
850
851    /* Next comes the ID */
852    if (id_len)
853    {
854        if (p_id)
855            memcpy (p_rec, p_id, id_len);
856
857        p_rec += id_len;
858    }
859
860    /* And lastly the payload. If NULL, the app just wants to reserve memory */
861    if (p_payload)
862        memcpy (p_rec, p_payload, payload_len);
863
864    *p_cur_size += recSize;
865
866    return (NDEF_OK);
867}
868
869/*******************************************************************************
870**
871** Function         NDEF_MsgInsertRec
872**
873** Description      This function inserts a record at a specific index into the
874**                  given NDEF message
875**
876** Returns          OK, or error if the record did not fit
877**                  *p_cur_size is updated
878**
879*******************************************************************************/
880extern tNDEF_STATUS NDEF_MsgInsertRec (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size, INT32 index,
881                                       UINT8 tnf, UINT8 *p_type, UINT8 type_len,
882                                       UINT8 *p_id, UINT8  id_len,
883                                       UINT8 *p_payload, UINT32 payload_len)
884{
885    UINT8   *p_rec;
886    UINT32  recSize;
887    INT32   plen = (payload_len < 256) ? 1 : 4;
888    INT32   ilen = (id_len == 0) ? 0 : 1;
889
890    /* First, make sure the record will fit. we need at least 2 bytes for header and type length */
891    recSize = payload_len + 2 + type_len + plen + ilen + id_len;
892
893    if ((*p_cur_size + recSize) > max_size)
894        return (NDEF_MSG_INSUFFICIENT_MEM);
895
896    /* See where the new record goes. If at the end, call the 'AddRec' function */
897    if ( (index >= NDEF_MsgGetNumRecs (p_msg))
898      || ((p_rec = NDEF_MsgGetRecByIndex(p_msg, index)) == NULL) )
899    {
900        return NDEF_MsgAddRec (p_msg, max_size, p_cur_size, tnf, p_type, type_len,
901                               p_id, id_len, p_payload, payload_len);
902    }
903
904    /* If we are inserting at the beginning, remove the MB bit from the current first */
905    if (index == 0)
906        *p_msg &= ~NDEF_MB_MASK;
907
908    /* Make space for the new record */
909    GKI_shiftdown (p_rec, (UINT32)(*p_cur_size - (p_rec - p_msg)), recSize);
910
911    /* If adding at the beginning, set begin bit */
912    if (index == 0)
913        *p_rec = tnf | NDEF_MB_MASK;
914    else
915        *p_rec = tnf;
916
917    if (plen == 1)
918        *p_rec |= NDEF_SR_MASK;
919
920    if (ilen != 0)
921        *p_rec |= NDEF_IL_MASK;
922
923    p_rec++;
924
925    /* The next byte is the type field length */
926    *p_rec++ = type_len;
927
928    /* Payload length - can be 1 or 4 bytes */
929    if (plen == 1)
930        *p_rec++ = (UINT8)payload_len;
931    else
932         UINT32_TO_BE_STREAM (p_rec, payload_len);
933
934    /* ID field Length (optional) */
935    if (ilen != 0)
936        *p_rec++ = id_len;
937
938    /* Next comes the type */
939    if (type_len)
940    {
941        if (p_type)
942            memcpy (p_rec, p_type, type_len);
943
944        p_rec += type_len;
945    }
946
947    /* Next comes the ID */
948    if (ilen != 0)
949    {
950        if (p_id)
951            memcpy (p_rec, p_id, id_len);
952
953        p_rec += id_len;
954    }
955
956    /* And lastly the payload. If NULL, the app just wants to reserve memory */
957    if (p_payload)
958        memcpy (p_rec, p_payload, payload_len);
959
960    *p_cur_size += recSize;
961
962    return (NDEF_OK);
963}
964
965/*******************************************************************************
966**
967** Function         NDEF_MsgAppendRec
968**
969** Description      This function adds NDEF records to the end of an NDEF message.
970**
971** Returns          OK, or error if the record did not fit
972**                  *p_cur_size is updated
973**
974*******************************************************************************/
975extern tNDEF_STATUS  NDEF_MsgAppendRec (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
976                                        UINT8 *p_new_rec, UINT32 new_rec_len)
977{
978    UINT8   *p_rec;
979    tNDEF_STATUS    status;
980
981    /* First, validate new records */
982    if ((status = NDEF_MsgValidate(p_new_rec, new_rec_len, FALSE)) != NDEF_OK)
983        return (status);
984
985    /* First, make sure the record will fit */
986    if ((*p_cur_size + new_rec_len) > max_size)
987        return (NDEF_MSG_INSUFFICIENT_MEM);
988
989    /* Find where to copy new record */
990    if (*p_cur_size == 0)
991        p_rec = p_msg;
992    else
993    {
994        /* Find the previous last and clear his 'Message End' bit */
995        UINT8  *pLast = NDEF_MsgGetLastRecInMsg (p_msg);
996
997        if (!pLast)
998            return (NDEF_MSG_NO_MSG_END);
999
1000        *pLast &= ~NDEF_ME_MASK;
1001        p_rec   = p_msg + *p_cur_size;
1002
1003        /* clear 'Message Begin' bit of new record */
1004        *p_new_rec &= ~NDEF_MB_MASK;
1005    }
1006
1007    /* append new records */
1008    memcpy (p_rec, p_new_rec, new_rec_len);
1009
1010    *p_cur_size += new_rec_len;
1011
1012    return (NDEF_OK);
1013}
1014
1015/*******************************************************************************
1016**
1017** Function         NDEF_MsgAppendPayload
1018**
1019** Description      This function appends extra payload to a specific record in the
1020**                  given NDEF message
1021**
1022** Returns          OK, or error if the extra payload did not fit
1023**                  *p_cur_size is updated
1024**
1025*******************************************************************************/
1026tNDEF_STATUS NDEF_MsgAppendPayload (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
1027                                    UINT8 *p_rec, UINT8 *p_add_pl, UINT32 add_pl_len)
1028{
1029    UINT32      prev_paylen, new_paylen;
1030    UINT8       *p_prev_pl, *pp;
1031    UINT8       incr_lenfld = 0;
1032    UINT8       type_len, id_len;
1033
1034    /* Skip header */
1035    pp = p_rec + 1;
1036
1037    /* Next byte is the type field length */
1038    type_len = *pp++;
1039
1040    /* Next is the payload length (1 or 4 bytes) */
1041    if (*p_rec & NDEF_SR_MASK)
1042        prev_paylen = *pp++;
1043    else
1044        BE_STREAM_TO_UINT32 (prev_paylen, pp);
1045
1046    /* ID field Length */
1047    if (*p_rec & NDEF_IL_MASK)
1048        id_len = *pp++;
1049    else
1050        id_len = 0;
1051
1052    p_prev_pl = pp + type_len + id_len;
1053
1054    new_paylen = prev_paylen + add_pl_len;
1055
1056    /* Previous payload may be < 256, and this addition may make it larger than 256 */
1057    /* If that were to happen, the payload length field goes from 1 byte to 4 bytes */
1058    if ( (prev_paylen < 256) && (new_paylen > 255) )
1059        incr_lenfld = 3;
1060
1061    /* Check that it all fits */
1062    if ((*p_cur_size + add_pl_len + incr_lenfld) > max_size)
1063        return (NDEF_MSG_INSUFFICIENT_MEM);
1064
1065    /* Point to payload length field */
1066    pp = p_rec + 2;
1067
1068    /* If we need to increase the length field from 1 to 4 bytes, do it first */
1069    if (incr_lenfld)
1070    {
1071        GKI_shiftdown (pp + 1, (UINT32)(*p_cur_size - (pp - p_msg) - 1), 3);
1072        p_prev_pl += 3;
1073    }
1074
1075    /* Store in the new length */
1076    if (new_paylen > 255)
1077    {
1078        *p_rec &= ~NDEF_SR_MASK;
1079        UINT32_TO_BE_STREAM (pp, new_paylen);
1080    }
1081    else
1082        *pp = (UINT8)new_paylen;
1083
1084    /* Point to the end of the previous payload */
1085    pp = p_prev_pl + prev_paylen;
1086
1087    /* If we are not the last record, make space for the extra payload */
1088    if ((*p_rec & NDEF_ME_MASK) == 0)
1089        GKI_shiftdown (pp, (UINT32)(*p_cur_size - (pp - p_msg)), add_pl_len);
1090
1091    /* Now copy in the additional payload data */
1092    memcpy (pp, p_add_pl, add_pl_len);
1093
1094    *p_cur_size += add_pl_len + incr_lenfld;
1095
1096    return (NDEF_OK);
1097}
1098
1099/*******************************************************************************
1100**
1101** Function         NDEF_MsgReplacePayload
1102**
1103** Description      This function replaces the payload of a specific record in the
1104**                  given NDEF message
1105**
1106** Returns          OK, or error if the new payload did not fit
1107**                  *p_cur_size is updated
1108**
1109*******************************************************************************/
1110tNDEF_STATUS NDEF_MsgReplacePayload (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
1111                                     UINT8 *p_rec, UINT8 *p_new_pl, UINT32 new_pl_len)
1112{
1113    UINT32      prev_paylen;
1114    UINT8       *p_prev_pl, *pp;
1115    UINT32      paylen_delta;
1116    UINT8       type_len, id_len;
1117
1118    /* Skip header */
1119    pp = p_rec + 1;
1120
1121    /* Next byte is the type field length */
1122    type_len = *pp++;
1123
1124    /* Next is the payload length (1 or 4 bytes) */
1125    if (*p_rec & NDEF_SR_MASK)
1126        prev_paylen = *pp++;
1127    else
1128        BE_STREAM_TO_UINT32 (prev_paylen, pp);
1129
1130    /* ID field Length */
1131    if (*p_rec & NDEF_IL_MASK)
1132        id_len = *pp++;
1133    else
1134        id_len = 0;
1135
1136    p_prev_pl = pp + type_len + id_len;
1137
1138    /* Point to payload length field again */
1139    pp = p_rec + 2;
1140
1141    if (new_pl_len > prev_paylen)
1142    {
1143        /* New payload is larger than the previous */
1144        paylen_delta = new_pl_len - prev_paylen;
1145
1146        /* If the previous payload length was < 256, and new is > 255 */
1147        /* the payload length field goes from 1 byte to 4 bytes       */
1148        if ( (prev_paylen < 256) && (new_pl_len > 255) )
1149        {
1150            if ((*p_cur_size + paylen_delta + 3) > max_size)
1151                return (NDEF_MSG_INSUFFICIENT_MEM);
1152
1153            GKI_shiftdown (pp + 1, (UINT32)(*p_cur_size - (pp - p_msg) - 1), 3);
1154            p_prev_pl   += 3;
1155            *p_cur_size += 3;
1156            *p_rec      &= ~NDEF_SR_MASK;
1157        }
1158        else if ((*p_cur_size + paylen_delta) > max_size)
1159            return (NDEF_MSG_INSUFFICIENT_MEM);
1160
1161        /* Store in the new length */
1162        if (new_pl_len > 255)
1163        {
1164            UINT32_TO_BE_STREAM (pp, new_pl_len);
1165        }
1166        else
1167            *pp = (UINT8)new_pl_len;
1168
1169        /* Point to the end of the previous payload */
1170        pp = p_prev_pl + prev_paylen;
1171
1172        /* If we are not the last record, make space for the extra payload */
1173        if ((*p_rec & NDEF_ME_MASK) == 0)
1174            GKI_shiftdown (pp, (UINT32)(*p_cur_size - (pp - p_msg)), paylen_delta);
1175
1176        *p_cur_size += paylen_delta;
1177    }
1178    else if (new_pl_len < prev_paylen)
1179    {
1180        /* New payload is smaller than the previous */
1181        paylen_delta = prev_paylen - new_pl_len;
1182
1183        /* If the previous payload was > 256, and new is less than 256 */
1184        /* the payload length field goes from 4 bytes to 1 byte        */
1185        if ( (prev_paylen > 255) && (new_pl_len < 256) )
1186        {
1187            GKI_shiftup (pp + 1, pp + 4, (UINT32)(*p_cur_size - (pp - p_msg) - 3));
1188            p_prev_pl   -= 3;
1189            *p_cur_size -= 3;
1190            *p_rec      |= NDEF_SR_MASK;
1191        }
1192
1193        /* Store in the new length */
1194        if (new_pl_len > 255)
1195        {
1196            UINT32_TO_BE_STREAM (pp, new_pl_len);
1197        }
1198        else
1199            *pp = (UINT8)new_pl_len;
1200
1201        /* Point to the end of the previous payload */
1202        pp = p_prev_pl + prev_paylen;
1203
1204        /* If we are not the last record, remove the extra space from the previous payload */
1205        if ((*p_rec & NDEF_ME_MASK) == 0)
1206            GKI_shiftup (pp - paylen_delta, pp, (UINT32)(*p_cur_size - (pp - p_msg)));
1207
1208        *p_cur_size -= paylen_delta;
1209    }
1210
1211    /* Now copy in the new payload data */
1212    if (p_new_pl)
1213        memcpy (p_prev_pl, p_new_pl, new_pl_len);
1214
1215    return (NDEF_OK);
1216}
1217
1218/*******************************************************************************
1219**
1220** Function         NDEF_MsgReplaceType
1221**
1222** Description      This function replaces the type field of a specific record in the
1223**                  given NDEF message
1224**
1225** Returns          OK, or error if the new type field did not fit
1226**                  *p_cur_size is updated
1227**
1228*******************************************************************************/
1229tNDEF_STATUS NDEF_MsgReplaceType (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
1230                                  UINT8 *p_rec, UINT8 *p_new_type, UINT8 new_type_len)
1231{
1232    UINT8       typelen_delta;
1233    UINT8       *p_prev_type, prev_type_len;
1234    UINT8       *pp;
1235
1236    /* Skip header */
1237    pp = p_rec + 1;
1238
1239    /* Next byte is the type field length */
1240    prev_type_len = *pp++;
1241
1242    /* Skip the payload length */
1243    if (*p_rec & NDEF_SR_MASK)
1244        pp += 1;
1245    else
1246        pp += 4;
1247
1248    if (*p_rec & NDEF_IL_MASK)
1249        pp++;
1250
1251    /* Save pointer to the start of the type field */
1252    p_prev_type = pp;
1253
1254    if (new_type_len > prev_type_len)
1255    {
1256        /* New type is larger than the previous */
1257        typelen_delta = new_type_len - prev_type_len;
1258
1259        if ((*p_cur_size + typelen_delta) > max_size)
1260            return (NDEF_MSG_INSUFFICIENT_MEM);
1261
1262        /* Point to the end of the previous type, and make space for the extra data */
1263        pp = p_prev_type + prev_type_len;
1264        GKI_shiftdown (pp, (UINT32)(*p_cur_size - (pp - p_msg)), typelen_delta);
1265
1266        *p_cur_size += typelen_delta;
1267    }
1268    else if (new_type_len < prev_type_len)
1269    {
1270        /* New type field is smaller than the previous */
1271        typelen_delta = prev_type_len - new_type_len;
1272
1273        /* Point to the end of the previous type, and shift up to fill the the unused space */
1274        pp = p_prev_type + prev_type_len;
1275        GKI_shiftup (pp - typelen_delta, pp, (UINT32)(*p_cur_size - (pp - p_msg)));
1276
1277        *p_cur_size -= typelen_delta;
1278    }
1279
1280    /* Save in new type length */
1281    p_rec[1] = new_type_len;
1282
1283    /* Now copy in the new type field data */
1284    if (p_new_type)
1285        memcpy (p_prev_type, p_new_type, new_type_len);
1286
1287    return (NDEF_OK);
1288}
1289
1290/*******************************************************************************
1291**
1292** Function         NDEF_MsgReplaceId
1293**
1294** Description      This function replaces the ID field of a specific record in the
1295**                  given NDEF message
1296**
1297** Returns          OK, or error if the new ID field did not fit
1298**                  *p_cur_size is updated
1299**
1300*******************************************************************************/
1301tNDEF_STATUS NDEF_MsgReplaceId (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
1302                                UINT8 *p_rec, UINT8 *p_new_id, UINT8 new_id_len)
1303{
1304    UINT8       idlen_delta;
1305    UINT8       *p_prev_id, *p_idlen_field;
1306    UINT8       prev_id_len, type_len;
1307    UINT8       *pp;
1308
1309    /* Skip header */
1310    pp = p_rec + 1;
1311
1312    /* Next byte is the type field length */
1313    type_len = *pp++;
1314
1315    /* Skip the payload length */
1316    if (*p_rec & NDEF_SR_MASK)
1317        pp += 1;
1318    else
1319        pp += 4;
1320
1321    p_idlen_field = pp;
1322
1323    if (*p_rec & NDEF_IL_MASK)
1324        prev_id_len = *pp++;
1325    else
1326        prev_id_len = 0;
1327
1328    /* Save pointer to the start of the ID field (right after the type field) */
1329    p_prev_id = pp + type_len;
1330
1331    if (new_id_len > prev_id_len)
1332    {
1333        /* New ID field is larger than the previous */
1334        idlen_delta = new_id_len - prev_id_len;
1335
1336        /* If the previous ID length was 0, we need to add a 1-byte ID length */
1337        if (prev_id_len == 0)
1338        {
1339            if ((*p_cur_size + idlen_delta + 1) > max_size)
1340                return (NDEF_MSG_INSUFFICIENT_MEM);
1341
1342            GKI_shiftdown (p_idlen_field, (UINT32)(*p_cur_size - (p_idlen_field - p_msg)), 1);
1343            p_prev_id   += 1;
1344            *p_cur_size += 1;
1345            *p_rec      |= NDEF_IL_MASK;
1346        }
1347        else if ((*p_cur_size + idlen_delta) > max_size)
1348            return (NDEF_MSG_INSUFFICIENT_MEM);
1349
1350        /* Point to the end of the previous ID field, and make space for the extra data */
1351        pp = p_prev_id + prev_id_len;
1352        GKI_shiftdown (pp, (UINT32)(*p_cur_size - (pp - p_msg)), idlen_delta);
1353
1354        *p_cur_size += idlen_delta;
1355    }
1356    else if (new_id_len < prev_id_len)
1357    {
1358        /* New ID field is smaller than the previous */
1359        idlen_delta = prev_id_len - new_id_len;
1360
1361        /* Point to the end of the previous ID, and shift up to fill the the unused space */
1362        pp = p_prev_id + prev_id_len;
1363        GKI_shiftup (pp - idlen_delta, pp, (UINT32)(*p_cur_size - (pp - p_msg)));
1364
1365        *p_cur_size -= idlen_delta;
1366
1367        /* If removing the ID, make sure that length field is also removed */
1368        if (new_id_len == 0)
1369        {
1370            GKI_shiftup (p_idlen_field, p_idlen_field + 1, (UINT32)(*p_cur_size - (p_idlen_field - p_msg - (UINT32)1)));
1371            *p_rec      &= ~NDEF_IL_MASK;
1372            *p_cur_size -= 1;
1373        }
1374    }
1375
1376    /* Save in new ID length and data */
1377    if (new_id_len)
1378    {
1379        *p_idlen_field = new_id_len;
1380
1381        if (p_new_id)
1382            memcpy (p_prev_id, p_new_id, new_id_len);
1383    }
1384
1385    return (NDEF_OK);
1386}
1387
1388/*******************************************************************************
1389**
1390** Function         NDEF_MsgRemoveRec
1391**
1392** Description      This function removes the record at the given
1393**                  index in the given NDEF message.
1394**
1395** Returns          TRUE if OK, FALSE if the index was invalid
1396**                  *p_cur_size is updated
1397**
1398*******************************************************************************/
1399tNDEF_STATUS NDEF_MsgRemoveRec (UINT8 *p_msg, UINT32 *p_cur_size, INT32 index)
1400{
1401    UINT8   *p_rec = NDEF_MsgGetRecByIndex (p_msg, index);
1402    UINT8   *pNext, *pPrev;
1403
1404    if (!p_rec)
1405        return (NDEF_REC_NOT_FOUND);
1406
1407    /* If this is the first record in the message... */
1408    if (*p_rec & NDEF_MB_MASK)
1409    {
1410        /* Find the second record (if any) and set his 'Message Begin' bit */
1411        if ((pNext = NDEF_MsgGetRecByIndex(p_msg, 1)) != NULL)
1412        {
1413            *pNext |= NDEF_MB_MASK;
1414
1415            *p_cur_size -= (UINT32)(pNext - p_msg);
1416
1417            GKI_shiftup (p_msg, pNext, *p_cur_size);
1418        }
1419        else
1420            *p_cur_size = 0;              /* No more records, lenght must be zero */
1421
1422        return (NDEF_OK);
1423    }
1424
1425    /* If this is the last record in the message... */
1426    if (*p_rec & NDEF_ME_MASK)
1427    {
1428        if (index > 0)
1429        {
1430            /* Find the previous record and set his 'Message End' bit */
1431            if ((pPrev = NDEF_MsgGetRecByIndex(p_msg, index - 1)) == NULL)
1432                return (FALSE);
1433
1434            *pPrev |= NDEF_ME_MASK;
1435        }
1436        *p_cur_size = (UINT32)(p_rec - p_msg);
1437
1438        return (NDEF_OK);
1439    }
1440
1441    /* Not the first or the last... get the address of the next record */
1442    if ((pNext = NDEF_MsgGetNextRec (p_rec)) == NULL)
1443        return (FALSE);
1444
1445    /* We are removing p_rec, so shift from pNext to the end */
1446    GKI_shiftup (p_rec, pNext, (UINT32)(*p_cur_size - (pNext - p_msg)));
1447
1448    *p_cur_size -= (UINT32)(pNext - p_rec);
1449
1450    return (NDEF_OK);
1451}
1452
1453
1454/*******************************************************************************
1455**
1456** Function         NDEF_MsgCopyAndDechunk
1457**
1458** Description      This function copies and de-chunks an NDEF message.
1459**                  It is assumed that the destination is at least as large
1460**                  as the source, since the source may not actually contain
1461**                  any chunks.
1462**
1463** Returns          The output byte count
1464**
1465*******************************************************************************/
1466tNDEF_STATUS NDEF_MsgCopyAndDechunk (UINT8 *p_src, UINT32 src_len, UINT8 *p_dest, UINT32 *p_out_len)
1467{
1468    UINT32          out_len, max_out_len;
1469    UINT8           *p_rec;
1470    UINT8           *p_prev_rec = p_dest;
1471    UINT8           *p_type, *p_id, *p_pay;
1472    UINT8           type_len, id_len, tnf;
1473    UINT32          pay_len;
1474    tNDEF_STATUS    status;
1475
1476    /* First, validate the source */
1477    if ((status = NDEF_MsgValidate(p_src, src_len, TRUE)) != NDEF_OK)
1478        return (status);
1479
1480    /* The output buffer must be at least as large as the input buffer */
1481    max_out_len = src_len;
1482
1483    /* Initialize output */
1484    NDEF_MsgInit (p_dest, max_out_len, &out_len);
1485
1486    p_rec = p_src;
1487
1488    /* Now, copy record by record */
1489    while (p_rec != NULL)
1490    {
1491        p_type = NDEF_RecGetType (p_rec, &tnf, &type_len);
1492        p_id   = NDEF_RecGetId (p_rec, &id_len);
1493        p_pay  = NDEF_RecGetPayload (p_rec, &pay_len);
1494
1495        /* If this is the continuation of a chunk, append the payload to the previous */
1496        if (tnf == NDEF_TNF_UNCHANGED)
1497        {
1498            NDEF_MsgAppendPayload (p_dest, max_out_len, &out_len, p_prev_rec, p_pay, pay_len);
1499        }
1500        else
1501        {
1502            p_prev_rec = p_dest + out_len;
1503
1504            NDEF_MsgAddRec (p_dest, max_out_len, &out_len, tnf, p_type, type_len,
1505                            p_id, id_len, p_pay, pay_len);
1506        }
1507
1508        p_rec = NDEF_MsgGetNextRec (p_rec);
1509    }
1510
1511    *p_out_len = out_len;
1512
1513    return (NDEF_OK);
1514}
1515
1516