summaryrefslogtreecommitdiff
path: root/nfclib/iso14443a.c
blob: 10cd6b2725dcc419eb5feb8c1a504f772fda6aef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
//*****************************************************************************
//
// iso14443a.c - ISO 14443A implementation.
//
// Copyright (c) 2010-2014 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package.
//
//*****************************************************************************

#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_types.h"
#include "driverlib/sysctl.h"
#include "trf79x0.h"
#include "iso14443a.h"

//*****************************************************************************
//
// Global anti-collision state for use by ISO14443ASelectFirst() and
// ISO14443ASelectNext().
//
//*****************************************************************************
static struct ISO14443AAnticolState g_sAnticolState;

//*****************************************************************************
//
// ISO14443-A Anti-collision implementation, iterative depth-first tree search
// with optional backtracking.
//
// Usage:
// In ISO 14443 A there are two types of resting states for cards: IDLE and
// HALT.  A card enters IDLE state after powering up and performing all the
// necessary internal initialization.  The specification states that the card
// must be in IDLE state and ready to accept commands 5ms after being put into
// an unmodulated (e.g. no commands sent) field of the necessary strength.
//
// This pause is guaranteed by ISO14443APowerOn().
//
// During the selection and anti-collision phase cards will be in intermediary
// states (READY and READY*) but then always return to the original state
// (IDLE and HALT).
//
// After a card has been selected by any of the ISO14443ASelect* functions
// of this module it can be sent to the HALT state with ISO14443AHalt(),
// and must be sent to HALT (or deactivated in another way) before calling
// another ISO14443ASelect* function.
//
// Cards in the IDLE state react to both WUPA and REQA commands, cards in
// HALT state react only to WUPA commands.  Cards in HALT state can not
// return to IDLE state except through completely powering off the card
// and powering it up again, but the specification makes no claims as to how
// long the field must be off in order for the card to power off and this time
// will vary between card types.
//
// The ISO14443ASelectFirst/Next functions take one parameter (\e ucCmd) that
// must be \b ISO14443A_REQA or \b ISO14443A_WUPA to specify which wake up
// method to use.  ISO14443ASelect will always use WUPA.
//
// This leads to two main usage protocols:
// <h3>A: Detect only new cards</h3> <ul>
//  <li> Keep field enabled at all times
//  <li> Use ISO14443ASelectFirst() with ISO14443A_REQA to find new cards that
//     entered the field.  Note: Ensure a pause of 5ms before each call to
//     ISO14443ASelectFirst(), e.g. with ISO14443APowerOn().
//  <li> If a card was found by SelectFirst, operate on that card and
//     deactivate it with ISO14443AHalt().  Note: all successful calls to any
//     ISO14443ASelect* function should always be paired with a call to
//     ISO14443AHalt() before the next call to any ISO14443ASelect* function.
//  <li> Repeatedly call ISO14443ASelectFirst() with \b ISO14443A_REQA in a
//      loop.  It will only find new cards and not relist the cards that were
//      already handled and halted
//  </ul>
//  Pseudo C: <pre>
//  while(1) {
//      ISO14443APowerOn();
//      if(ISO14443ASelectFirst(ISO14443A_REQA, ...)) {
//
//          <i>Do something with the card</i>
//
//          ISO14443AHalt();
//      }
//
//      <i>Do NOT power off the field</i>
//  }
// </pre>
//
// <h3>B: List all cards in the field</h3> <ul>
//  <li> Optionally disable the field or do other things, but enable the
//      field at least for 5ms (e.g. with ISO14443APowerOn())
//  <li> Call ISO14443ASelectFirst() with \b ISO14443A_WUPA to find the first
//      card, handle it, call ISO14443AHalt().  If at least one card was
//      found, use ISO14443ASelectNext() with \b ISO14443A_WUPA in a loop to
//      find more cards, handle them and call ISO14443AHalt() on them.
//  <li> You may disable the field and restart the procedure at any time with
//      ISO14443APowerOn() and ISO14443ASelectFirst().  It will always list
//      all cards in the field, not only new cards.
// </ul>
//  Pseudo C: <pre>
//  while(1) {
//      ISO14443APowerOn();
//      if(ISO14443ASelectFirst(ISO14443A_WUPA, ...)) {
//          do {
//
//              <i>Do something with the card</i>
//
//              ISO14443AHalt();
//          } while(ISO14443ASelectNext(ISO14443A_WUPA, ...));
//      }
//
//      <i>You may power off the field here</i>
//  }
// </pre>
//
// In both cases ISO14443ASelect() can be used at any time (after halting a
// previously selected card) to select a card by known UID.
//
//*****************************************************************************

//*****************************************************************************
//
// This structure stores the UID that we're currently working on.
//
//*****************************************************************************
struct ISO14443AAnticolState
{
    //
    // This field stores the raw responses that the anti-collision is actually
    // performed over, e.g. 3 times 5 bytes.  Same goes for \e ucCollisions.
    // Before returning the UID to the calling code this must be cleaned,
    // that is remove cascade tag and BCC.
    //
    unsigned char ucUID[15];
    //
    // Stores the collision positions discovered so far.  It is a bit field
    // with the same indices as \e ucUID.
    //
    unsigned char ucCollisions[15];
    //
    // Stores the number of bits that we've successfully received or
    // disambiguated.  Note: Real count for the \e ucUID field of this
    // structure, not NVB format.  For example 8 means 1 byte and 0 bits, 40
    // means full cascade level 1, 41 means full cascade level 1 plus 1 bit in
    // cascade level 2.
    //
    unsigned int iBitPos;
};

//*****************************************************************************
//
// Set up registers for ISO 14443 A 106Kbit/s operation.  This function must
// be called after initializing the TRF79x0 (for example with TRF79x0Init()
// or TRF79x0DirectCommand() with argument \b TRF79X0_SOFT_INIT_CMD) and before
// calling any of the other ISO14443A functions.
//
//*****************************************************************************
void
ISO14443ASetupRegisters(void)
{
    //
    // Set the ISO format to ISO1443A 106Kbps.
    //
    TRF79x0WriteRegister(TRF79X0_ISO_CONTROL_REG,
                         TRF79X0_ISO_CONTROL_14443A_106K);

    //
    // Set the TX pulse to 106ns (0x20 * 73.7ns).
    //
    TRF79x0WriteRegister(TRF79X0_TX_PULSE_LENGTH_CTRL_REG, 0x20);

    //
    // Set the RX No response wait time to 529us (0xe * 37.76us).
    //
    TRF79x0WriteRegister(TRF79X0_RX_NO_RESPONSE_WAIT_REG, 0x0e);

    //
    // Set the RX wait time to 66us (7 * 9.44us).
    //
    TRF79x0WriteRegister(TRF79X0_RX_WAIT_TIME_REG, 0x07);

    //
    // Set the SYSCLK to 6.78MHz and the Modulation Depth to OOK.
    //
    TRF79x0WriteRegister(TRF79X0_MODULATOR_CONTROL_REG,
                         (TRF79X0_MOD_CTRL_SYS_CLK_6_78MHZ |
                          TRF79X0_MOD_CTRL_MOD_OOK_100));

    //
    // Configure the Special Settings Register.
    //
    TRF79x0WriteRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG,
       (TRF79x0ReadRegister(TRF79X0_RX_SPECIAL_SETTINGS_REG) & 0x0f) |
        TRF79X0_RX_SP_SET_M848);

    //
    // Configure the Test Settings Register.
    //
    TRF79x0WriteRegister(TRF79X0_TEST_SETTING1_REG, 0x20);

    //
    // Set the regulator voltage to be automatic.
    //
    TRF79x0WriteRegister(TRF79X0_REGULATOR_CONTROL_REG,
                         TRF79X0_REGULATOR_CTRL_AUTO_REG);
}

//*****************************************************************************
//
// Power on the field and wait for a time that is long enough to guarantee
// that all cards in the field will be initialized.
//
//*****************************************************************************
void
ISO14443APowerOn(void)
{
    unsigned char ucReg;

    //
    // Enable RF field and receiver.
    //
    ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG);
    TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG,
                         ucReg | TRF79X0_STATUS_CTRL_RF_ON);

    //
    // Wait 5ms (as per ISO 14443-3 clause 5).
    //
    SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000);
}

//*****************************************************************************
//
// Power off the field and wait for some time.
//
//*****************************************************************************
void
ISO14443APowerOff(void)
{
    unsigned char ucReg;

    //
    // Disable RF field and receiver.
    //
    ucReg = TRF79x0ReadRegister(TRF79X0_CHIP_STATUS_CTRL_REG);

    TRF79x0WriteRegister(TRF79X0_CHIP_STATUS_CTRL_REG,
                         ucReg & ~TRF79X0_STATUS_CTRL_RF_ON);

    //
    // Wait 5ms.
    //
    SysCtlDelay(((SysCtlClockGet() / 3) * 5) / 1000);
}

//*****************************************************************************
//
// Transmit a HLTA command that should HALT the currently selected card.  You
// should always call this function after a successful call to either
// ISO14443ASelect(), ISO14443ASelectFirst() or ISO14443ASelectNext() and
// before any other call to any of those functions.
//
//*****************************************************************************
void
ISO14443AHalt(void)
{
    //
    // HLTA command.
    //
    const unsigned char pucHLTA[2] = {0x50, 0x00};

    TRF79x0Transceive(pucHLTA, sizeof(pucHLTA), 0, NULL, NULL, NULL,
                      TRF79X0_TRANSCEIVE_CRC);
}

//*****************************************************************************
//
// Transceive ISO 14443-A REQA type command.
//
// \param ucCmd is the command, either \b ISO14443A_REQA or \b ISO14443A_WUPA
// \param piATQA is a pointer to an integer to store the received ATQA and
//  will be set to -1 if a collision occurred.
//
// \return true if at least one card responded that is capable of bit-frame
//  anti-collision (e.g. no collision and one of the lower 5 bits of response
//  set, or collision within the first 5 bits, or collision not in the first
//  5 bits but at least one of the first 5 bits is a 1-bit) and false
// otherwise.
//
// \note User code usually does not need to call this function since it is
// implicitly called in ISO14443ASelect(), ISO14443ASelectFirst() or
// ISO14443ASelectNext().
//
//*****************************************************************************
int
ISO14443AREQA(unsigned char ucCmd, int *piATQA)
{
    unsigned char pucResponse[2];
    unsigned int uiRxSize;
    int iColPos;

    uiRxSize = sizeof(pucResponse);

    //
    // Transmit WUPA/REQA, receive ATQA.
    //
    TRF79x0Transceive(&ucCmd, 0, 7, pucResponse, &uiRxSize, 0,
                      TRF79X0_TRANSCEIVE_NO_CRC);

    if(uiRxSize == 2)
    {
        //
        // Valid ATQA received, return it as an integer.  Was transmitted
        // LSByte first.
        //
        if(piATQA != NULL)
        {
            *piATQA = pucResponse[0] | (pucResponse[1] << 8);
        }

        //
        // Return true if one of the lower 5 bits was set.
        //
        return((pucResponse[0] & 0x1F) != 0);
    }
    else
    {
        //
        // No valid ATQA received.
        //
        if(piATQA != NULL)
        {
            *piATQA = -1;
        }

        if(uiRxSize == 0)
        {
            //
            // No response at all -> no card with bit-frame anti-collision.
            //
            return(0);
        }
        else
        {
            //
            // Probably some collision.
            //
            iColPos = TRF79x0GetCollisionPosition();

            if(iColPos > 5)
            {
                //
                // Collision not within the first 5 bits, return true if one of
                // the lower 5 bits was set.
                //
                return((pucResponse[0] & 0x1F) != 0);
            }
            else if(iColPos > 0 && iColPos <= 5)
            {
                //
                // Collision within the first 5 bits, so at least one of them
                // was 1.
                //
                return(1);
            }
            else
            {
                //
                // No collision, but only 1 byte sent?  That card's not right.
                //
                return(0);
            }
        }
    }
}

//*****************************************************************************
//
// Find one card through the anti-collision procedure with given \e psState.
//
// \param psState is the anti-collision state to start from.  If this state
// already specifies a full UID then it will be selected, otherwise
// anti-collision will be tried to complete that starting state, with no
// backtracking.
// \param pucUID is an output buffer to write the selected UID and may be
// \b NULL in which case the UID will not be returned.
// \param puiUIDSize inputs the available space in bytes in \e pucUID and
// returns with the actual length that has been stored there.
// \param pucSAK is an output parameter that stores the received SAK value.
// May be \b NULL in which case the SAK will not be returned
//
// This is a depth first search in a binary tree over the UID space.  On each
// attempt we can learn up to 4 bytes of the UID of the card(s) currently in
// the field.  If the UIDs of two cards differ we will learn that too and get
// the collision position: the position of the bit where the UID of at least
// two cards differs.  We will mark this position in the appropriate field in
// the structure ISO14443AnticolState and then branch first in the direction of
// 0 and increase iBitPos to include this bit.
//
// \return This function returns 1 if a card was selected and 0 otherwise.
//
//*****************************************************************************
static int
ISO14443ADoAnticol(struct ISO14443AAnticolState *psState, unsigned char *pucUID,
                   unsigned int *puiUIDSize, unsigned char *pucSAK)
{
    int iCascadeLevel, iPos;
    unsigned char pucCmd[7], pucResponse[5];
    unsigned int uiRxSize;
    int iIdx, iMaskPosition, iCollPosition, iValidBits, iMaxLength, iNVB;

    iCascadeLevel = 1;

    while(iCascadeLevel < 4)
    {
        //
        // Already known bits for this cascade level, e.g. not including
        // the possible 5 bytes * 8 bits/byte for the lower levels.
        //
        iValidBits = psState->iBitPos - (iCascadeLevel - 1) * 5 * 8;

        //
        // Clamp to a full cascade level.
        //
        if(iValidBits > 40)
        {
            iValidBits = 40;
        }

        //
        // NVB format: bytes.
        //
        iNVB = (iValidBits / 8) << 4;

        //
        // NVB format: bits.
        //
        iNVB |= (iValidBits % 8);

        //
        // Also count the command byte and the NVB byte itself.
        //
        iNVB += 0x20;

        //
        // Prepare command for this level: ANTICOLLISION if less than a full 5
        // bytes for the current cascade level, SELECT otherwise.
        //
        switch (iCascadeLevel)
        {
            case 1:
            {
                pucCmd[0] = 0x93;
                break;
            }
            case 2:
            {
                pucCmd[0] = 0x95;
                break;
            }
            case 3:
            {
                pucCmd[0] = 0x97;
                break;
            }
            default:
            {
                break;
            }
        }

        pucCmd[1] = iNVB;

        //
        // Copy over known bytes (number of bits for this level divided by 8,
        // rounded up).
        //
        memcpy(pucCmd + 2, psState->ucUID + (iCascadeLevel - 1) * 5,
               (iValidBits + 7) / 8);

        //
        // Enforce a small delay of ~600us before each anti-collision frame.
        //
        SysCtlDelay(((SysCtlClockGet() / 3) * 6) / 10000);

        //
        // Maximal expected response length.
        //
        uiRxSize = 5;

        if(iNVB != 0x70)
        {
            //
            // Anti-collision command.
            //
            TRF79x0Transceive(pucCmd, pucCmd[1] >> 4, pucCmd[1] & 0xf,
                              pucResponse, &uiRxSize, NULL,
                              TRF79X0_TRANSCEIVE_NO_CRC);

            if(uiRxSize == 0)
            {
                return(0);
            }

            iCollPosition = TRF79x0GetCollisionPosition();

            if(iCollPosition < 0)
            {
                //
                // No collision occurred, add full response data to known bits.
                //
                iCollPosition = 40;
            }
            else
            {
                //
                // Collision occurred, only add the part that was received
                // correctly.
                //
                // TF7960 Collision position register is in NVB format,
                // convert to straight bit position.  This will be the number
                // of bits that were the same in all responding cards.
                //
                iCollPosition -= 0x20;
                iCollPosition = ((iCollPosition >> 4) * 8) + (iCollPosition & 0xf);
            }

            //
            // Bounds check the results and return 0 if it was invalid.
            //
            if(iCollPosition < 0 || iCollPosition > 40)
            {
                return(0);
            }

            //
            // Mask out the invalid bits in the last byte of the response, if
            // any.
            //
            // Graphic:
            // UID bytes:  | first  || second || third  || fourth || fifth  |
            //             | iValidBits |
            //             |       iCollPosition       |
            // In this graphic the first byte is fully valid.  The second byte
            // was sent partially invalid, but should have been masked on a
            // previous run.  The third byte is received partially invalid and
            // needs to be masked.  Response will only contain the second and
            // third byte (although both are received properly byte-aligned).
            //
            //
            // This many bits in response are valid or at least compatible
            // with the UID.
            //
            iMaskPosition = iCollPosition - (iValidBits / 8) * 8;

            if(iMaskPosition % 8)
            {
                //
                // Need to construct a mask for iMaskPosition%8 bits and
                // apply it at iMaskPosition/8.
                //
                pucResponse[iMaskPosition / 8] &= ~((~0) << (iMaskPosition % 8));
            }

            //
            // Merge in up to iMaskPosition/8 (rounded up) byte into response
            // at index iBitPos/8 (rounded down).
            //
            for(iIdx = 0; iIdx < (iMaskPosition + 7) / 8; iIdx++)
            {
                psState->ucUID[(psState->iBitPos / 8) + iIdx] |=
                    pucResponse[iIdx];
            }

            psState->iBitPos += iCollPosition - iValidBits;

            //
            // Only within this cascade level:
            //
            if(psState->iBitPos % 40 != 0)
            {
                //
                // Mark backtracking point.
                //
                psState->ucCollisions[psState->iBitPos / 8] |=
                    1 << (psState->iBitPos % 8);

                //
                // Walk into the 0 direction.
                //
                psState->iBitPos += 1;
            }
        }
        else
        {
            //
            // Select command.
            //
            TRF79x0Transceive(pucCmd, pucCmd[1] >> 4, pucCmd[1] & 0xf,
                              pucResponse, &uiRxSize, NULL,
                              TRF79X0_TRANSCEIVE_CRC);

            if(uiRxSize == 1)
            {
                //
                // SAK received.
                //
                if(pucResponse[0] & 0x04)
                {
                    //
                    // UID not complete, increase cascade level.
                    //
                    iCascadeLevel++;

                    if(iCascadeLevel > 3)
                    {
                        break;
                    }
                }
                else
                {
                    //
                    // UID complete, return.
                    //
                    break;
                }
            }
            else
            {
                //
                // Some error, card not selected.
                //
                memset(psState->ucUID, 0, sizeof(psState->ucUID));
                psState->iBitPos = 0;
                break;
            }
        }
    }

    //
    // Some error, not fully selected.
    //
    if(((psState->iBitPos % 40) != 0) || (psState->iBitPos == 0))
    {
        return(0);
    }

    //
    // Fully selected a card.  pucResponse[0] should be from the last
    // transaction, of a SELECT command, and therefore contain the SAK
    //
    if(pucSAK != NULL)
    {
        *pucSAK = pucResponse[0];
    }

    //
    // If requested, return the UID, without cascade tag and BCC.
    //
    if(pucUID != NULL && puiUIDSize != NULL)
    {
        iMaxLength = *puiUIDSize;
        iPos = 0;
        *puiUIDSize = 0;

        //
        // From the 5 bytes in each cascade level the 3 middle bytes need to be
        // copied for each level except for the last, where the first 4 bytes
        // need to be copied.
        //
        for(iPos = 0; iPos < psState->iBitPos / 8; iPos += 5)
        {
            if(iPos + 5 < psState->iBitPos / 8)
            {
                //
                // Not the last cascade level.
                //
                if(*puiUIDSize + 3 > iMaxLength)
                {
                    //
                    // Not enough space
                    //
                    *puiUIDSize = 0;
                    break;
                }

                //
                // Copy 3 bytes (e.g. don't copy cascade tag and BCC).
                //
                memcpy(pucUID + *puiUIDSize, psState->ucUID + iPos + 1, 3);

                *puiUIDSize += 3;
            }
            else
            {
                //
                // Last cascade level.
                //
                if(*puiUIDSize + 4 > iMaxLength)
                {
                    //
                    // Not enough space.
                    //
                    *puiUIDSize = 0;

                    break;
                }

                //
                // Copy 4 bytes (e.g. don't copy BCC).
                //
                memcpy(pucUID + *puiUIDSize, psState->ucUID + iPos, 4);

                *puiUIDSize += 4;
            }
        }
    }
    return(1);
}

//*****************************************************************************
//
// Selects the first (or only) card and returns its UID, UID length and
// SAK bytes.
//
// \param ucCmd must be ISO14443A_REQA or ISO14443A_WUPA.
// \param pucUID will store UID of the card that was selected.  May be NULL
// in which case the UID will not be returned.
// \param puiUIDSize must be initialized with the length of the buffer in
// \e pucUID and will return the number of bytes actually stored.
// \param pucSAK will store the SAK byte of the card that was selected and may
// be NULL in which case the SAK byte will not be returned.
//
// The function call initializes and updates a static internal state that
// marks the position in the anti-collision procedure.  ISO14443ASelectNext()
// can be used to continue with the anti-collision from that starting point.
//
// \note You should call ISO14443AHalt() if this function returned true and
// you are done operating on the card.
//
// \return Function returns 1 if a card was selected, 0 otherwise.
//
//*****************************************************************************
int
ISO14443ASelectFirst(unsigned char ucCmd, unsigned char *pucUID,
                     unsigned int *puiUIDSize, unsigned char *pucSAK)
{
    //
    // Initialize/clear static state.
    //
    memset(&g_sAnticolState, 0, sizeof(g_sAnticolState));

    //
    // Wake up all or only new tags.
    //
    if(ISO14443AREQA(ucCmd, NULL) == 0)
    {
        //
        // No tag with support for bit frame anti-collision found.
        //
        return(0);
    }

    return(ISO14443ADoAnticol(&g_sAnticolState, pucUID, puiUIDSize, pucSAK));
}

//*****************************************************************************
//
// Selects the next card and returns its UID, UID length and SAK bytes.
//
// \param ucCmd must be ISO14443A_REQA or ISO14443A_WUPA.
// \param UID will store UID of the card that was selected and may be NULL
// in which case the UID will not be returned.
// \param puiUIDSize must be initialized with the length of the buffer in
// \e UID and will return the number of bytes actually stored.
// \param pucSAK will store the SAK byte of the card that was selected and may
// be NULL in which case the SAK byte will not be returned.
//
// Uses the state that was initialized by ISO14443SelectFirst() and tries
// to find more cards in the field.
//
// \note You should call ISO14443AHalt() if this function returned true and
// you are done operating on the card.
//
// \return This function returns 1 if a card was selected and 0 otherwise.
//
//*****************************************************************************
int
ISO14443ASelectNext(unsigned char ucCmd, unsigned char *pucUID,
                    unsigned int *puiUIDSize, unsigned char *pucSAK)
{
    //
    // Backtrack through static state: starting at iBitPos and going reverse,
    // find the first bit that's set in collisions, walk into the 1 direction,
    // clear the collision indicator and set iBitPos to that position.
    //
    while(--g_sAnticolState.iBitPos > 0)
    {
        //
        // Clear UID bit at this position to clean the state.
        //
        g_sAnticolState.ucUID[g_sAnticolState.iBitPos / 8] &=
            ~(1 << (g_sAnticolState.iBitPos % 8));

        if(g_sAnticolState.ucCollisions[g_sAnticolState.iBitPos / 8] &
           (1 << (g_sAnticolState.iBitPos % 8)))
        {
            //
            // This is our new starting point, set UID bit to walk into the
            // 1 direction.
            //
            g_sAnticolState.ucUID[g_sAnticolState.iBitPos / 8] |=
                1 << (g_sAnticolState.iBitPos % 8);

            //
            // Remove backtracking marker.
            //
            g_sAnticolState.ucCollisions[g_sAnticolState.iBitPos / 8] &=
                ~(1 << (g_sAnticolState.iBitPos % 8));

            //
            // Increment bit position to account for the bit that we just
            // added, then break loop to perform anti-collision with the new
            // partial UID.
            //
            g_sAnticolState.iBitPos++;

            break;
        }

        //
        // Not a backtracking point, go further back.
        //
    }

    //
    // No further backtracking points -> no other cards.
    //
    if(g_sAnticolState.iBitPos <= 0)
    {
        return(0);
    }

    //
    // Wake up all or only new tags.
    //
    if(!ISO14443AREQA(ucCmd, NULL))
    {
        //
        // No tag with support for bit frame anti-collision found.
        //
        return(0);
    }

    return(ISO14443ADoAnticol(&g_sAnticolState, pucUID, puiUIDSize, pucSAK));
}

//*****************************************************************************
//
// Selects a card with given UID and return its SAK byte.
//
// \param pucUID must point to the UID of the card that should be selected and
// may not be NULL.
// \param uiUIDSize must be the length in bytes of the UID stored in \e pucUID.
// \param pucSAK will store the SAK byte of the card that was selected.  May be
// \b NULL in which case the SAK byte will not be returned.
//
// \note You should call ISO14443AHalt() if this function returned true and
// you are done operating on the card.
//
// \return This function will return 1 if a card was selected and 0 otherwise.
//
//*****************************************************************************
int
ISO14443ASelect(unsigned char const *pucUID, unsigned int uiUIDSize,
                unsigned char *pucSAK)
{
    int iIdx, iPos;
    struct ISO14443AAnticolState sState;

    //
    // Check if the given UID size is supported.
    //
    if((uiUIDSize != 4) && (uiUIDSize != 7) && (uiUIDSize != 10))
    {
        return(0);
    }

    //
    // Prepare a state for the given UID.
    //
    sState.iBitPos = 0;

    for(iPos = 0; iPos < uiUIDSize;)
    {
        //
        // Check if this is the final cascade level.
        //
        if(iPos + 4 < uiUIDSize)
        {
            //
            // If this was not the final cascade level then add a cascade tag.
            //
            sState.ucUID[sState.iBitPos / 8] = 0x88;

            //
            // Copy three bytes of UID.
            //
            memcpy(sState.ucUID + (sState.iBitPos / 8) + 1, pucUID + iPos, 3);

            //
            // Increment position in UID.
            //
            iPos += 3;
        }
        else
        {
            //
            // For the final cascade level just copy four bytes of UID.
            //
            memcpy(sState.ucUID + (sState.iBitPos / 8), pucUID + iPos, 4);

            //
            // Increment position in UID.
            //
            iPos += 4;
        }

        //
        // Calculate BCC.
        //
        sState.ucUID[sState.iBitPos / 8 + 4] = 0;

        for(iIdx = 0; iIdx < 4; iIdx++)
        {
            sState.ucUID[sState.iBitPos / 8 + 4] ^=
                sState.ucUID[sState.iBitPos / 8 + iIdx];
        }

        //
        // Increment position in state.
        //
        sState.iBitPos += 40;
    }

    //
    // Always wake up all cards.
    //
    if(!ISO14443AREQA(ISO14443A_WUPA, NULL))
    {
        //
        // No tag with support for bit frame anti-collision found.
        //
        return(0);
    }

    return(ISO14443ADoAnticol(&sState, NULL, NULL, pucSAK));
}

//*****************************************************************************
//
// Helper functions for ISO 14443-A frames to be sent or received in Direct
// Mode.
//
//*****************************************************************************

//*****************************************************************************
//
// Calculates odd parity for one byte.
//
//*****************************************************************************
static unsigned char
ParityByte(unsigned char ucByte)
{
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    ucByte ^= ucByte >> 1;
    return((ucByte & 1) ^ 1);
}

//*****************************************************************************
//
// Checks that data has correct (odd) parity
//
// \param pusData is the data buffer to check and must store 16 bits per one
// logical byte: the lower 8 bits are the data byte, the LSBit in the upper
// byte is the parity.
// \param lSize is the number of logical bytes/16 bit words in \e pusData.
//
// \return This function returns 1 if the parity was correct and 0 otherwise.
//
//*****************************************************************************
int
ISO14443ACheckParity(const unsigned short * const pusData, const long lSize)
{
    int iFailed, iIdx;

    iFailed = 0;

    for(iIdx = 0; iIdx < lSize; iIdx++)
    {
        iFailed |= (pusData[iIdx] >> 8) ^ ParityByte(pusData[iIdx] & 0xff);
    }

    return(!iFailed);
}

//*****************************************************************************
//
// Sets data to correct (odd) parity
//
// \param pusData is the data buffer to update and must store 16 bits per one
// logical byte: the lower 8 bits are the data byte, the LSBit in the upper
// byte is the parity.
// \param lSize is the number of logical bytes/16 bit words in \e data
//
//*****************************************************************************
void
ISO14443ACalculateParity(unsigned short * const pusData, const long lSize)
{
    int iIdx;

    for(iIdx = 0; iIdx < lSize; iIdx++)
    {
        pusData[iIdx] = (pusData[iIdx] & 0xff) |
                        (ParityByte(pusData[iIdx] & 0xff) << 8);
    }
}

//*****************************************************************************
//
// Calculate CRC-A and return it.
//
//*****************************************************************************
static unsigned short
CalculateCRC(const unsigned short * const pusData, const long lSize)
{
    unsigned short usCrc;
    int iIdx, iBit;
    unsigned char ucByte, ucBit;

    usCrc = 0x6363;

    for(iIdx = 0; iIdx < lSize; iIdx++)
    {
        ucByte = pusData[iIdx] & 0xff;

        for(iBit = 0; iBit < 8; iBit++)
        {
            ucBit = (usCrc ^ ucByte) & 1;

            ucByte >>= 1;
            usCrc >>= 1;

            if(ucBit)
            {
                usCrc ^= 0x8408;
            }
        }
    }
    return(usCrc);
}

//*****************************************************************************
//
// Check that data has correct CRC in last two bytes.
//
// \param pusData is the data buffer to check and must store 16 bits per one
// logical byte: the lower 8 bits are the data byte, the LSBit in the upper
// byte is the parity.
// \param lSize is the number of logical bytes/16 bit words in \e pusData.
// Must be at least 2, since the CRC consists of two bytes.
//
// \return This function returns 1 if the CRC was correct and 0 otherwise.
//
//*****************************************************************************
int
ISO14443ACheckCRC(const unsigned short * const pusData, const long lSize)
{
    unsigned short usCrc;

    if(lSize < 2)
    {
        return(0);
    }

    usCrc = CalculateCRC(pusData, lSize - 2);

    if(((usCrc & 0xff) == (pusData[lSize - 2] & 0xff)) &&
       (((usCrc >> 8) & 0xff) == (pusData[lSize - 1] & 0xff)))
    {
        return(1);
    }
    return(0);
}

//*****************************************************************************
//
// Appends correct CRC to the data
//
// \param pusData is the data buffer to update and must store 16 bits per one
// logical byte: the lower 8 bits are the data byte, the LSBit in the upper
// byte is the parity.
// \param lSize is the number of logical bytes/16 bit words in \e pusData.  The
// buffer in \e pusData must have room for an additional two logical bytes.
//
// \return This function returns the new length to correctly append the CRC.
//
//*****************************************************************************
long
ISO14443ACalculateCRC(unsigned short * const pusData, const long lSize)
{
    unsigned short usCrc;

    usCrc = CalculateCRC(pusData, lSize);

    pusData[lSize] = usCrc & 0xff;
    pusData[lSize + 1] = (usCrc >> 8) & 0xff;

    ISO14443ACalculateParity(pusData + lSize, 2);

    return(lSize + 2);
}