mfrc522 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/core_ext.rb +35 -0
- data/lib/exceptions.rb +14 -0
- data/lib/iso144434.rb +245 -0
- data/lib/mfrc522.rb +228 -336
- data/lib/mifare/classic.rb +51 -58
- data/lib/mifare/des_fire.rb +684 -4
- data/lib/mifare/key.rb +180 -0
- data/lib/mifare/ultralight.rb +36 -22
- data/lib/mifare/ultralight_c.rb +51 -7
- data/lib/picc.rb +28 -0
- metadata +9 -6
- data/lib/mifare/base.rb +0 -22
- data/lib/mifare/plus.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec95f072c266803e40c9178fba2435c8f113d5ab
|
4
|
+
data.tar.gz: 98eb6c503409371dcb7e25f7e61f02494c5c13b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23304b26becaed10dc250a013bc5a098894e9ce03f688986ca9fd403897bdf4472603aba2f7bc216bb26732a70c515c5f302c656e559a4fc2e6e51f5a8ec68a3
|
7
|
+
data.tar.gz: b8afd661c9ed6e8e898716e5c91bbfee19d373bb1287822f12fa8f42890d42e1cce1fc844312e312a52724a1f97ca58c5a53ad85215e12ac7931e12cd797cf88
|
data/lib/core_ext.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class Array
|
2
|
+
def append_uint(number, byte)
|
3
|
+
raise 'Only support unsigned integer' if number < 0
|
4
|
+
raise 'Insufficient bytes' if number.abs >= (1 << (byte * 8))
|
5
|
+
|
6
|
+
until byte == 0
|
7
|
+
self << (number & 0xFF)
|
8
|
+
number >>= 8
|
9
|
+
byte -= 1
|
10
|
+
end
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_uint
|
15
|
+
int = 0
|
16
|
+
self.each_with_index do |byte, index|
|
17
|
+
int |= (byte << (index * 8))
|
18
|
+
end
|
19
|
+
int
|
20
|
+
end
|
21
|
+
|
22
|
+
def append_sint(number, byte)
|
23
|
+
raise 'Insufficient bytes' if number.abs >= (1 << (byte * 8))
|
24
|
+
|
25
|
+
sign = (number < 0) ? 1 : 0
|
26
|
+
number &= (1 << ((byte * 8) - 1)) - 1
|
27
|
+
self.append_uint(number, byte)
|
28
|
+
self << (self.pop | (sign << 7))
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_sint
|
32
|
+
sign = (self.last & 0x80 != 0) ? (-1 ^ ((1 << ((self.size * 8) - 1)) - 1)) : 0
|
33
|
+
sign | self.to_uint
|
34
|
+
end
|
35
|
+
end
|
data/lib/exceptions.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CommunicationError < StandardError; end
|
2
|
+
class PICCTimeoutError < CommunicationError; end
|
3
|
+
class PCDTimeoutError < CommunicationError; end
|
4
|
+
class IncorrectCRCError < CommunicationError; end
|
5
|
+
class CollisionError < CommunicationError; end
|
6
|
+
|
7
|
+
class UnexpectedDataError < StandardError; end
|
8
|
+
|
9
|
+
class MifareNakError < StandardError; end
|
10
|
+
|
11
|
+
class DESFireError < StandardError; end
|
12
|
+
class UnauthenticatedError < DESFireError; end
|
13
|
+
class ReceiptStatusError < DESFireError; end
|
14
|
+
class ReceiptIntegrityError < DESFireError; end
|
data/lib/iso144434.rb
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
class ISO144434 < PICC
|
2
|
+
FSCI_to_FSC = [16, 24, 32, 40, 48, 64, 96, 128, 256]
|
3
|
+
|
4
|
+
CMD_RATS = 0xE0
|
5
|
+
CMD_PPS = 0xD0
|
6
|
+
CMD_DESELECT = 0xC2
|
7
|
+
CMD_SUCCESS = 0x00
|
8
|
+
CMD_ADDITIONAL_FRAME = 0xAF
|
9
|
+
|
10
|
+
def initialize(pcd, uid, sak)
|
11
|
+
super
|
12
|
+
|
13
|
+
@cid = 0x00 # We don't support CID
|
14
|
+
@fsc = 16 # Assume PICC only supports 16 bytes frame
|
15
|
+
@fwt = 256 # 77.33ms(256 ticks) default frame waiting time
|
16
|
+
|
17
|
+
@support_cid = false
|
18
|
+
@support_nad = false
|
19
|
+
@block_number = 0
|
20
|
+
@selected = false
|
21
|
+
end
|
22
|
+
|
23
|
+
# ISO/IEC 14443-4 select
|
24
|
+
def select
|
25
|
+
# Send RATS (Request for Answer To Select)
|
26
|
+
buffer = [CMD_RATS, 0x50 | @cid]
|
27
|
+
received_data = @pcd.picc_transceive(buffer)
|
28
|
+
|
29
|
+
dr, ds = process_ats(received_data)
|
30
|
+
|
31
|
+
# Send PPS (Protocol and Parameter Selection Request)
|
32
|
+
buffer = [CMD_PPS | @cid, 0x11, (ds << 2) | dr]
|
33
|
+
received_data = @pcd.picc_transceive(buffer)
|
34
|
+
raise UnexpectedDataError, 'Incorrect response' if received_data[0] != (0xD0 | @cid)
|
35
|
+
|
36
|
+
# Set PCD baud rate
|
37
|
+
@pcd.transceiver_baud_rate(:tx, dr)
|
38
|
+
@pcd.transceiver_baud_rate(:rx, ds)
|
39
|
+
|
40
|
+
@block_number = 0
|
41
|
+
@max_frame_size = [64, @fsc].min
|
42
|
+
@max_inf_size = @max_frame_size - 3 # PCB + CRC16
|
43
|
+
@max_inf_size -= 1 if @support_cid
|
44
|
+
@max_inf_size -= 1 if @support_nad
|
45
|
+
@selected = true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Send S(DESELECT)
|
49
|
+
def deselect
|
50
|
+
buffer = [CMD_DESELECT]
|
51
|
+
received_data = @pcd.picc_transceive(buffer)
|
52
|
+
|
53
|
+
if received_data[0] & 0xF7 == CMD_DESELECT
|
54
|
+
@selected = false
|
55
|
+
true
|
56
|
+
else
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Wrapper for handling ISO protocol
|
62
|
+
def transceive(send_data)
|
63
|
+
# Split data according to max buffer size
|
64
|
+
send_data = [send_data] unless send_data.is_a? Array
|
65
|
+
chained_data = send_data.each_slice(@max_inf_size).to_a
|
66
|
+
|
67
|
+
# Initialize I-block
|
68
|
+
pcb = 0x02
|
69
|
+
|
70
|
+
# Send chained data
|
71
|
+
until chained_data.empty?
|
72
|
+
pcb &= 0xEF # Reset chaining indicator
|
73
|
+
pcb |= 0x10 if chained_data.size > 1 # Set chaining
|
74
|
+
pcb |= @block_number # Set block number
|
75
|
+
data = chained_data.shift
|
76
|
+
|
77
|
+
buffer = [pcb] + data
|
78
|
+
|
79
|
+
finished = false
|
80
|
+
until finished
|
81
|
+
received_data = handle_wtx(buffer)
|
82
|
+
|
83
|
+
# Retreive response pcb from data
|
84
|
+
r_pcb = received_data[0]
|
85
|
+
|
86
|
+
# Received ACK
|
87
|
+
if r_pcb & 0xF6 == 0xA2
|
88
|
+
# If ACK matches current block number means success
|
89
|
+
# Otherwise transmit it again
|
90
|
+
if (pcb & 0x01) == (r_pcb & 0x01)
|
91
|
+
finished = true
|
92
|
+
end
|
93
|
+
else
|
94
|
+
finished = true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
@block_number ^= 1 # toggle block number for next frame
|
99
|
+
end
|
100
|
+
|
101
|
+
received_chained_data = [received_data]
|
102
|
+
|
103
|
+
# Receive chained data
|
104
|
+
while r_pcb & 0x10 != 0
|
105
|
+
ack = 0xA2 | @block_number # Set block number
|
106
|
+
received_data = handle_wtx([ack]) # Send ACK to receive next frame
|
107
|
+
|
108
|
+
r_pcb = received_data[0]
|
109
|
+
|
110
|
+
received_chained_data << received_data
|
111
|
+
|
112
|
+
@block_number ^= 1 # toggle block number for next frame
|
113
|
+
end
|
114
|
+
|
115
|
+
# Collect INF from chain
|
116
|
+
inf = []
|
117
|
+
received_chained_data.each do |data|
|
118
|
+
inf_position = 1
|
119
|
+
inf_position += 1 if data[0] & 0x08 != 0 # CID present
|
120
|
+
inf_position += 1 if data[0] & 0x04 != 0 # NAD present
|
121
|
+
|
122
|
+
inf.concat(data[inf_position..-1])
|
123
|
+
end
|
124
|
+
|
125
|
+
inf
|
126
|
+
end
|
127
|
+
|
128
|
+
def resume_communication
|
129
|
+
deselect rescue nil
|
130
|
+
super
|
131
|
+
end
|
132
|
+
|
133
|
+
def halt
|
134
|
+
deselect rescue nil
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def convert_iso_baud_rate_to_pcd_setting(value)
|
141
|
+
# ISO
|
142
|
+
# 0b000: 106kBd, 0b001: 212kBd, 0b010: 424kBd, 0b100: 848kBd
|
143
|
+
# MFRC522 register
|
144
|
+
# 0b000: 106kBd, 0b001: 212kBd, 0b010: 424kBd, 0b011: 848kBd
|
145
|
+
x = (value >> 2) & 0x01
|
146
|
+
y = (value >> 1) & 0x01
|
147
|
+
z = value & 0x01
|
148
|
+
|
149
|
+
((x | y) << 1) + (x | (~y & z))
|
150
|
+
end
|
151
|
+
|
152
|
+
# Gether information from ATS (Answer to Select)
|
153
|
+
def process_ats(ats)
|
154
|
+
position = 1
|
155
|
+
t0 = ats[position] # Format byte
|
156
|
+
|
157
|
+
fsci = t0 & 0x0F # PICC buffer size integer
|
158
|
+
y1 = (t0 >> 4) & 0x07 # Optional frame(TA, TB, TC) indicator
|
159
|
+
@fsc = FSCI_to_FSC[fsci] # Convert buffer size integer to bytes
|
160
|
+
dr = 0 # default baud rate 106kBd
|
161
|
+
ds = 0
|
162
|
+
|
163
|
+
# Frame: TA
|
164
|
+
if y1 & 0x01 != 0
|
165
|
+
position += 1
|
166
|
+
ta = ats[position]
|
167
|
+
|
168
|
+
dr = ta & 0x07 # PCD to PICC baud rate
|
169
|
+
ds = (ta >> 4) & 0x07 # PICC to PCD baud rate
|
170
|
+
|
171
|
+
# Convert fastest baud rate to PCD setting
|
172
|
+
dr = convert_iso_baud_rate_to_pcd_setting(dr)
|
173
|
+
ds = convert_iso_baud_rate_to_pcd_setting(ds)
|
174
|
+
|
175
|
+
dr = 0
|
176
|
+
ds = 0
|
177
|
+
end
|
178
|
+
|
179
|
+
# Frame: TB
|
180
|
+
if y1 & 0x02 != 0
|
181
|
+
position += 1
|
182
|
+
tb = ats[position]
|
183
|
+
|
184
|
+
fwi = (tb >> 4) & 0x0F # Frame wating integer
|
185
|
+
sgfi = tb & 0x0F # Start-up frame guard integer
|
186
|
+
|
187
|
+
# Convert integers to real time
|
188
|
+
@fwt = (1 << fwi)
|
189
|
+
sgft = (1 << sgfi)
|
190
|
+
|
191
|
+
# Set frame waiting time
|
192
|
+
@pcd.internal_timer(@fwt)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Get info about CID or NAD
|
196
|
+
if y1 & 0x04 != 0
|
197
|
+
position += 1
|
198
|
+
tc = ats[position]
|
199
|
+
|
200
|
+
@support_cid = true if tc & 0x02 != 0
|
201
|
+
@support_nad = true if tc & 0x01 != 0
|
202
|
+
end
|
203
|
+
|
204
|
+
# Start-up guard time
|
205
|
+
sleep 0.000302 * sgft
|
206
|
+
|
207
|
+
return dr, ds
|
208
|
+
end
|
209
|
+
|
210
|
+
def handle_wtx(data)
|
211
|
+
24.times do
|
212
|
+
begin
|
213
|
+
received_data = @pcd.picc_transceive(data)
|
214
|
+
rescue CommunicationError => e
|
215
|
+
raise e unless e.is_a? PICCTimeoutError
|
216
|
+
|
217
|
+
# Try sending NAK when timeout
|
218
|
+
nak = 0xB2 | @block_number
|
219
|
+
data = [nak]
|
220
|
+
next
|
221
|
+
end
|
222
|
+
|
223
|
+
pcb = received_data[0]
|
224
|
+
|
225
|
+
# WTX detected
|
226
|
+
if pcb & 0xF7 == 0xF2
|
227
|
+
inf_position = (pcb & 0x08 != 0) ? 2 : 1
|
228
|
+
wtxm = received_data[inf_position] & 0x3F
|
229
|
+
|
230
|
+
# Set temporary timer
|
231
|
+
@pcd.internal_timer(@fwt * wtxm)
|
232
|
+
|
233
|
+
# Set WTX response
|
234
|
+
data = [0xF2, wtxm]
|
235
|
+
else
|
236
|
+
# Set timer back to FWT
|
237
|
+
@pcd.internal_timer(@fwt)
|
238
|
+
|
239
|
+
return received_data
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
raise PICCTimeoutError
|
244
|
+
end
|
245
|
+
end
|
data/lib/mfrc522.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'pi_piper'
|
2
2
|
|
3
|
-
# For 3DES auth
|
4
3
|
require 'openssl'
|
5
4
|
require 'securerandom'
|
6
5
|
|
7
|
-
require '
|
6
|
+
require 'core_ext'
|
7
|
+
require 'exceptions'
|
8
|
+
|
9
|
+
require 'picc'
|
10
|
+
require 'iso144434'
|
11
|
+
|
12
|
+
require 'mifare/key'
|
8
13
|
require 'mifare/classic'
|
9
14
|
require 'mifare/ultralight'
|
10
15
|
require 'mifare/ultralight_c'
|
11
|
-
require 'mifare/plus'
|
12
16
|
require 'mifare/des_fire'
|
13
17
|
|
14
18
|
include PiPiper
|
@@ -16,31 +20,15 @@ include PiPiper
|
|
16
20
|
class MFRC522
|
17
21
|
|
18
22
|
# PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
|
19
|
-
PICC_REQA
|
20
|
-
PICC_WUPA
|
21
|
-
PICC_CT
|
22
|
-
PICC_SEL_CL1
|
23
|
-
PICC_SEL_CL2
|
24
|
-
PICC_SEL_CL3
|
25
|
-
PICC_HLTA
|
26
|
-
#
|
27
|
-
|
28
|
-
# The read/write commands can also be used for MIFARE Ultralight.
|
29
|
-
PICC_MF_AUTH_KEY_A = 0x60 # Perform authentication with Key A
|
30
|
-
PICC_MF_AUTH_KEY_B = 0x61 # Perform authentication with Key B
|
31
|
-
PICC_MF_READ = 0x30 # Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight.
|
32
|
-
PICC_MF_WRITE = 0xA0 # Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight.
|
33
|
-
PICC_MF_DECREMENT = 0xC0 # Decrements the contents of a block and stores the result in the internal data register.
|
34
|
-
PICC_MF_INCREMENT = 0xC1 # Increments the contents of a block and stores the result in the internal data register.
|
35
|
-
PICC_MF_RESTORE = 0xC2 # Reads the contents of a block into the internal data register.
|
36
|
-
PICC_MF_TRANSFER = 0xB0 # Writes the contents of the internal data register to a block.
|
37
|
-
# The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
|
38
|
-
# The PICC_MF_READ and PICC_MF_WRITE can also be used for MIFARE Ultralight.
|
39
|
-
PICC_UL_WRITE = 0xA2 # Writes one 4 byte page to the PICC.
|
40
|
-
# The commands here is for Ultralight 3DES Authentication
|
41
|
-
PICC_UL_3DES_AUTH = 0x1A
|
42
|
-
#
|
43
|
-
PICC_MF_ACK = 0x0A # Mifare Acknowledge
|
23
|
+
PICC_REQA = 0x26 # REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame.
|
24
|
+
PICC_WUPA = 0x52 # Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame.
|
25
|
+
PICC_CT = 0x88 # Cascade Tag. Not really a command, but used during anti collision.
|
26
|
+
PICC_SEL_CL1 = 0x93 # Anti collision/Select, Cascade Level 1
|
27
|
+
PICC_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2
|
28
|
+
PICC_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3
|
29
|
+
PICC_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
|
30
|
+
# Mifare Acknowledge
|
31
|
+
PICC_MF_ACK = 0x0A
|
44
32
|
|
45
33
|
# PCD commands
|
46
34
|
PCD_Idle = 0x00 # no action, cancels current command execution
|
@@ -112,14 +100,14 @@ class MFRC522
|
|
112
100
|
TestDAC2Reg = 0x3A # defines the test value for TestDAC2
|
113
101
|
TestADCReg = 0x3B # shows the value of ADC I and Q channels
|
114
102
|
|
115
|
-
|
116
|
-
def initialize(nrstpd = 24, chip = 0, spd = 8000000, timer = 50)
|
103
|
+
def initialize(nrstpd = 24, chip = 0, spd = 8000000, timer = 256)
|
117
104
|
chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0,
|
118
105
|
1 => PiPiper::Spi::CHIP_SELECT_1,
|
119
106
|
2 => PiPiper::Spi::CHIP_SELECT_BOTH,
|
120
107
|
3 => PiPiper::Spi::CHIP_SELECT_NONE }
|
121
108
|
@spi_chip = chip_option[chip]
|
122
109
|
@spi_spd = spd
|
110
|
+
@timer = timer
|
123
111
|
|
124
112
|
# Power it up
|
125
113
|
nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out)
|
@@ -128,13 +116,7 @@ class MFRC522
|
|
128
116
|
|
129
117
|
soft_reset # Perform software reset
|
130
118
|
|
131
|
-
|
132
|
-
write_spi(TPrescalerReg, 0x3E) # Set lower part of TPrescalerReg, and results in 2khz timer (f_timer = 13.56 MHz / (2*TPreScaler+1))
|
133
|
-
write_spi(TReloadRegH, (timer >> 8))
|
134
|
-
write_spi(TReloadRegL, (timer & 0xFF)) # 50 ticks @2khz defines 25ms per timer cycle
|
135
|
-
|
136
|
-
write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
|
137
|
-
write_spi(ModeReg, 0x3D) # Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
|
119
|
+
pcd_config_reset # Set default setting
|
138
120
|
|
139
121
|
antenna_on # Turn antenna on. They were disabled by the reset.
|
140
122
|
end
|
@@ -143,47 +125,48 @@ class MFRC522
|
|
143
125
|
def soft_reset
|
144
126
|
write_spi(CommandReg, PCD_SoftReset)
|
145
127
|
sleep 1.0 / 20.0 # wait 50ms
|
128
|
+
|
129
|
+
write_spi(TModeReg, 0x87) # Start timer by setting TAuto=1, and higher part of TPrescalerReg
|
130
|
+
write_spi(TPrescalerReg, 0xFF) # Set lower part of TPrescalerReg, and results in 302us timer (f_timer = 13.56 MHz / (2*TPreScaler+1))
|
131
|
+
|
132
|
+
write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
|
133
|
+
write_spi(ModeReg, 0x3D) # Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
|
146
134
|
end
|
147
135
|
|
148
|
-
#
|
149
|
-
def
|
150
|
-
|
151
|
-
|
152
|
-
spi.chip_select_active_low(true)
|
153
|
-
spi.bit_order Spi::MSBFIRST
|
154
|
-
spi.clock @spi_spd
|
136
|
+
# Reset PCD config to default
|
137
|
+
def pcd_config_reset
|
138
|
+
# Clear ValuesAfterColl bit
|
139
|
+
write_spi_clear_bitmask(CollReg, 0x80)
|
155
140
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
160
|
-
end
|
161
|
-
output
|
162
|
-
end
|
141
|
+
# Reset transceiver baud rate to 106 kBd
|
142
|
+
transceiver_baud_rate(:tx, 0)
|
143
|
+
transceiver_baud_rate(:rx, 0)
|
163
144
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
spi.chip_select_active_low(true)
|
168
|
-
spi.bit_order Spi::MSBFIRST
|
169
|
-
spi.clock @spi_spd
|
145
|
+
# Set PCD timer value for 302us default timer
|
146
|
+
internal_timer(@timer)
|
147
|
+
end
|
170
148
|
|
171
|
-
|
172
|
-
|
173
|
-
|
149
|
+
# Control transceive timeout value
|
150
|
+
def internal_timer(timer = nil)
|
151
|
+
if timer
|
152
|
+
write_spi(TReloadRegH, (timer >> 8) & 0xFF)
|
153
|
+
write_spi(TReloadRegL, (timer & 0xFF))
|
174
154
|
end
|
155
|
+
(read_spi(TReloadRegH) << 8) | read_spi(TReloadRegL)
|
175
156
|
end
|
176
157
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
end
|
158
|
+
# Control transceiver baud rate
|
159
|
+
# value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
|
160
|
+
def transceiver_baud_rate(direction, value = nil)
|
161
|
+
reg = {tx: TxModeReg, rx: RxModeReg}
|
182
162
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
163
|
+
if value
|
164
|
+
value <<= 4
|
165
|
+
value |= 0x80 if value != 0
|
166
|
+
write_spi(reg.fetch(direction), value)
|
167
|
+
end
|
168
|
+
|
169
|
+
(read_spi(reg.fetch(direction)) >> 4) & 0x07
|
187
170
|
end
|
188
171
|
|
189
172
|
# Turn antenna on
|
@@ -206,135 +189,25 @@ class MFRC522
|
|
206
189
|
(read_spi(RFCfgReg) & 0x70) >> 4
|
207
190
|
end
|
208
191
|
|
209
|
-
# Calculate CRC using MFRC522's built-in coprocessor
|
210
|
-
def calculate_crc(data)
|
211
|
-
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
212
|
-
write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
|
213
|
-
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
214
|
-
write_spi(FIFODataReg, data) # Write data to the FIFO
|
215
|
-
write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
|
216
|
-
|
217
|
-
# Wait for the command to complete
|
218
|
-
i = 5000
|
219
|
-
loop do
|
220
|
-
irq = read_spi(DivIrqReg)
|
221
|
-
break if (irq & 0x04) != 0
|
222
|
-
return :status_pcd_timeout if i == 0
|
223
|
-
i -= 1
|
224
|
-
end
|
225
|
-
|
226
|
-
write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
|
227
|
-
|
228
|
-
result = []
|
229
|
-
result << read_spi(CRCResultRegL)
|
230
|
-
result << read_spi(CRCResultRegH)
|
231
|
-
|
232
|
-
return :status_ok, result
|
233
|
-
end
|
234
|
-
|
235
|
-
# Calculate and append CRC to data
|
236
|
-
def append_crc(data)
|
237
|
-
status, crc = calculate_crc(data)
|
238
|
-
return status if status != :status_ok
|
239
|
-
data << crc[0] << crc[1]
|
240
|
-
|
241
|
-
return :status_ok, data
|
242
|
-
end
|
243
|
-
|
244
|
-
# Check CRC using MFRC522's built-in coprocessor
|
245
|
-
def check_crc(data)
|
246
|
-
status, crc = calculate_crc(data[0..-3])
|
247
|
-
return status if status != :status_ok
|
248
|
-
return :status_crc_error if data[-2] != crc[0] || data[-1] != crc[1]
|
249
|
-
|
250
|
-
return :status_ok
|
251
|
-
end
|
252
|
-
|
253
|
-
# PCD transceive helper
|
254
|
-
def communicate_with_picc(command, send_data, framing_bit = 0, check_crc = false)
|
255
|
-
wait_irq = 0x00
|
256
|
-
wait_irq = 0x10 if command == PCD_MFAuthent
|
257
|
-
wait_irq = 0x30 if command == PCD_Transceive
|
258
|
-
|
259
|
-
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
260
|
-
write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits
|
261
|
-
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
262
|
-
write_spi(FIFODataReg, send_data) # Write sendData to the FIFO
|
263
|
-
write_spi(BitFramingReg, framing_bit) # Bit adjustments
|
264
|
-
write_spi(CommandReg, command) # Execute the command
|
265
|
-
if command == PCD_Transceive
|
266
|
-
write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts
|
267
|
-
end
|
268
|
-
|
269
|
-
# Wait for the command to complete
|
270
|
-
i = 2000
|
271
|
-
loop do
|
272
|
-
irq = read_spi(ComIrqReg)
|
273
|
-
break if (irq & wait_irq) != 0
|
274
|
-
return :status_picc_timeout if (irq & 0x01) != 0
|
275
|
-
return :status_pcd_timeout if i == 0
|
276
|
-
i -= 1
|
277
|
-
end
|
278
|
-
|
279
|
-
# Check for error
|
280
|
-
error = read_spi(ErrorReg)
|
281
|
-
return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
|
282
|
-
|
283
|
-
# Receiving data
|
284
|
-
received_data = []
|
285
|
-
data_length = read_spi(FIFOLevelReg)
|
286
|
-
while data_length > 0 do
|
287
|
-
data = read_spi(FIFODataReg)
|
288
|
-
received_data << data
|
289
|
-
data_length -=1
|
290
|
-
end
|
291
|
-
valid_bits = read_spi(ControlReg) & 0x07
|
292
|
-
|
293
|
-
# Check CRC if requested
|
294
|
-
if !received_data.empty? && check_crc
|
295
|
-
return :status_mifare_nack if received_data.count == 1 && valid_bits == 4
|
296
|
-
return :status_crc_error if received_data.count < 2 || valid_bits != 0
|
297
|
-
|
298
|
-
status = check_crc(received_data)
|
299
|
-
return status if status != :status_ok
|
300
|
-
end
|
301
|
-
|
302
|
-
status = :status_ok
|
303
|
-
status = :status_collision if (error & 0x08) != 0 # CollErr
|
304
|
-
|
305
|
-
return status, received_data, valid_bits
|
306
|
-
end
|
307
|
-
|
308
192
|
# Wakes PICC from HALT or IDLE to ACTIVE state
|
309
|
-
#
|
310
193
|
# Accept PICC_REQA and PICC_WUPA command
|
311
194
|
def picc_request(picc_command)
|
312
|
-
|
195
|
+
pcd_config_reset
|
313
196
|
|
314
197
|
status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07)
|
315
198
|
|
316
|
-
|
317
|
-
return :status_unknown_data if valid_bits != 0 # REQA or WUPA command return 16 bits(full byte)
|
318
|
-
|
319
|
-
return :status_ok
|
199
|
+
status == :status_ok && valid_bits == 0 # REQA or WUPA command return 16 bits(full byte)
|
320
200
|
end
|
321
201
|
|
322
202
|
# Instruct PICC in ACTIVE state go to HALT state
|
323
203
|
def picc_halt
|
324
|
-
buffer = [PICC_HLTA, 0]
|
325
|
-
|
326
|
-
# Calculate CRC and append it into buffer
|
327
|
-
status, buffer = append_crc(buffer)
|
328
|
-
return status if status != :status_ok
|
204
|
+
buffer = append_crc([PICC_HLTA, 0])
|
329
205
|
|
330
206
|
status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer)
|
331
207
|
|
332
208
|
# PICC in HALT state will not respond
|
333
209
|
# If PICC sent reply, means it didn't acknowledge the command we sent
|
334
|
-
|
335
|
-
return :status_error if status == :status_ok
|
336
|
-
|
337
|
-
return status
|
210
|
+
status == :status_picc_timeout
|
338
211
|
end
|
339
212
|
|
340
213
|
# Select PICC for further communication
|
@@ -364,13 +237,14 @@ class MFRC522
|
|
364
237
|
# 10 bytes 1 CT uid0 uid1 uid2
|
365
238
|
# 2 CT uid3 uid4 uid5
|
366
239
|
# 3 uid6 uid7 uid8 uid9
|
240
|
+
pcd_config_reset
|
367
241
|
|
368
|
-
|
369
|
-
select_level = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3]
|
242
|
+
cascade_levels = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3]
|
370
243
|
uid = []
|
244
|
+
sak = 0
|
371
245
|
|
372
|
-
|
373
|
-
buffer = [
|
246
|
+
cascade_levels.each do |cascade_level|
|
247
|
+
buffer = [cascade_level]
|
374
248
|
current_level_known_bits = 0
|
375
249
|
received_data = []
|
376
250
|
valid_bits = 0
|
@@ -380,7 +254,7 @@ class MFRC522
|
|
380
254
|
# ensure there's nothing weird in buffer
|
381
255
|
if buffer.size != 6 && !buffer.select{|b| !buffer.is_a?(Fixnum)}.empty?
|
382
256
|
current_level_known_bits = 0
|
383
|
-
buffer = []
|
257
|
+
buffer = [cascade_level]
|
384
258
|
next
|
385
259
|
end
|
386
260
|
|
@@ -389,8 +263,7 @@ class MFRC522
|
|
389
263
|
buffer << (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
|
390
264
|
|
391
265
|
# Append CRC to buffer
|
392
|
-
|
393
|
-
return status if status != :status_ok
|
266
|
+
buffer = append_crc(buffer)
|
394
267
|
else
|
395
268
|
tx_last_bits = current_level_known_bits % 8
|
396
269
|
uid_full_byte = current_level_known_bits / 8
|
@@ -400,9 +273,8 @@ class MFRC522
|
|
400
273
|
|
401
274
|
framing_bit = (tx_last_bits << 4) + tx_last_bits
|
402
275
|
|
403
|
-
#
|
276
|
+
# Select it
|
404
277
|
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, buffer, framing_bit)
|
405
|
-
return status if status != :status_ok
|
406
278
|
|
407
279
|
# Append received UID into buffer if not doing full select
|
408
280
|
buffer = buffer[0...all_full_byte] + received_data[0..3] if current_level_known_bits < 32
|
@@ -411,12 +283,14 @@ class MFRC522
|
|
411
283
|
if status == :status_collision
|
412
284
|
collision = read_spi(CollReg)
|
413
285
|
|
414
|
-
|
286
|
+
# CollPosNotValid - We don't know where collision happened
|
287
|
+
raise CollisionError if (collision & 0x20) != 0
|
288
|
+
|
415
289
|
collision_position = collision & 0x1F
|
416
290
|
collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32
|
417
|
-
|
291
|
+
raise CollisionError if collision_position <= current_level_known_bits
|
418
292
|
|
419
|
-
# Mark the bit
|
293
|
+
# Mark the collision bit
|
420
294
|
current_level_known_bits = collision_position
|
421
295
|
uid_bit = (current_level_known_bits - 1) % 8
|
422
296
|
uid_byte = (current_level_known_bits / 8) + (uid_bit != 0 ? 1 : 0)
|
@@ -425,88 +299,42 @@ class MFRC522
|
|
425
299
|
break if current_level_known_bits >= 32
|
426
300
|
current_level_known_bits = 32 # We've already known all bits, loop again for a complete select
|
427
301
|
else
|
428
|
-
|
302
|
+
raise CommunicationError, status
|
429
303
|
end
|
430
304
|
end
|
431
305
|
|
432
306
|
# We've finished current cascade level
|
433
|
-
# Check and collect all uid in
|
307
|
+
# Check and collect all uid stored in buffer
|
434
308
|
|
435
309
|
# Append UID
|
436
310
|
uid << buffer[2] if buffer[2] != PICC_CT
|
437
311
|
uid << buffer[3] << buffer[4] << buffer[5]
|
438
312
|
|
439
313
|
# Check the result of full select
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
return status if status != :status_ok
|
314
|
+
# Select Acknowledge is 1 byte + CRC16
|
315
|
+
raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0
|
316
|
+
raise IncorrectCRCError unless check_crc(received_data)
|
444
317
|
|
445
318
|
sak = received_data[0]
|
446
|
-
|
447
|
-
break if (received_data[0] & 0x04) == 0 # No more cascade level
|
319
|
+
break if (sak & 0x04) == 0 # No more cascade level
|
448
320
|
end
|
449
321
|
|
450
|
-
return
|
322
|
+
return uid, sak
|
451
323
|
end
|
452
324
|
|
453
|
-
# Trying to
|
325
|
+
# Trying to restart picc
|
454
326
|
def reestablish_picc_communication(uid)
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
if uid == new_uid
|
465
|
-
return :status_ok
|
466
|
-
else
|
467
|
-
return :status_different_card_detected
|
327
|
+
picc_halt
|
328
|
+
picc_request(PICC_WUPA)
|
329
|
+
|
330
|
+
begin
|
331
|
+
new_uid, _new_sak = picc_select
|
332
|
+
status = true
|
333
|
+
rescue CommunicationError
|
334
|
+
status = false
|
468
335
|
end
|
469
|
-
end
|
470
336
|
|
471
|
-
|
472
|
-
def iso_select
|
473
|
-
|
474
|
-
end
|
475
|
-
|
476
|
-
# Lookup error message
|
477
|
-
def error_type(error)
|
478
|
-
case error
|
479
|
-
when :status_ok
|
480
|
-
'It worked'
|
481
|
-
when :status_pcd_timeout
|
482
|
-
'Reader did not responding'
|
483
|
-
when :status_picc_timeout
|
484
|
-
'Tag did not responding'
|
485
|
-
when :status_crc_error
|
486
|
-
'CRC check failed'
|
487
|
-
when :status_mifare_nack
|
488
|
-
'Tag sent negative acknowledge'
|
489
|
-
when :status_collision
|
490
|
-
'Multiple tags detected'
|
491
|
-
when :status_unknown_data
|
492
|
-
'Incorrect data received'
|
493
|
-
when :status_internal_error
|
494
|
-
'Something went wrong but it shouldnt happen'
|
495
|
-
when :status_sak_error
|
496
|
-
'Incorrect select acknowledge'
|
497
|
-
when :status_auth_failed
|
498
|
-
'Authentication failed'
|
499
|
-
when :status_data_length_error
|
500
|
-
'Incorrect input data length'
|
501
|
-
when :status_incorrect_input
|
502
|
-
'Incorrect input'
|
503
|
-
when :status_different_card_detected
|
504
|
-
'Different card detected while reselecting'
|
505
|
-
when :status_error
|
506
|
-
'Something went wrong'
|
507
|
-
else
|
508
|
-
'Unknown error type'
|
509
|
-
end
|
337
|
+
status && uid == new_uid
|
510
338
|
end
|
511
339
|
|
512
340
|
# Lookup PICC name using sak
|
@@ -557,7 +385,7 @@ class MFRC522
|
|
557
385
|
return :picc_unknown
|
558
386
|
end
|
559
387
|
|
560
|
-
# Start
|
388
|
+
# Start Crypto1 communication between reader and Mifare PICC
|
561
389
|
#
|
562
390
|
# PICC must be selected before calling for authentication
|
563
391
|
# Remember to deauthenticate after communication, or no new communication can be made
|
@@ -566,106 +394,170 @@ class MFRC522
|
|
566
394
|
# Checks datasheets for block address numbering of your PICC
|
567
395
|
#
|
568
396
|
def mifare_crypto1_authenticate(command, block_addr, sector_key, uid)
|
569
|
-
#
|
570
397
|
# Buffer[12]: {command, block_addr, sector_key[6], uid[4]}
|
571
|
-
#
|
572
398
|
buffer = [command, block_addr]
|
573
|
-
buffer
|
574
|
-
buffer
|
399
|
+
buffer.concat(sector_key[0..5])
|
400
|
+
buffer.concat(uid[0..3])
|
575
401
|
|
576
|
-
|
402
|
+
communicate_with_picc(PCD_MFAuthent, buffer)
|
577
403
|
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
return :status_ok
|
404
|
+
# Check MFCrypto1On bit
|
405
|
+
(read_spi(Status2Reg) & 0x08) != 0
|
582
406
|
end
|
583
407
|
|
584
|
-
# Stop
|
408
|
+
# Stop Crypto1 communication
|
585
409
|
def mifare_crypto1_deauthenticate
|
586
410
|
write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit
|
587
411
|
end
|
588
412
|
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
413
|
+
# Append CRC to buffer and check CRC or Mifare acknowledge
|
414
|
+
def picc_transceive(send_data, accept_timeout = false)
|
415
|
+
send_data = append_crc(send_data)
|
416
|
+
|
417
|
+
puts "Sending Data: #{send_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
|
418
|
+
|
419
|
+
# Transfer data
|
420
|
+
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
|
421
|
+
return [] if status == :status_picc_timeout && accept_timeout
|
422
|
+
raise PICCTimeoutError if status == :status_picc_timeout
|
423
|
+
raise CommunicationError, status if status != :status_ok
|
424
|
+
|
425
|
+
puts "Received Data: #{received_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
|
595
426
|
|
596
|
-
|
427
|
+
# Data exists, check CRC and return
|
428
|
+
if received_data.size > 1
|
429
|
+
raise IncorrectCRCError unless check_crc(received_data)
|
430
|
+
|
431
|
+
return received_data[0..-3]
|
432
|
+
end
|
433
|
+
|
434
|
+
raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
|
435
|
+
raise MifareNakError, received_data[0] if received_data[0] != PICC_MF_ACK
|
436
|
+
|
437
|
+
received_data
|
597
438
|
end
|
598
439
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
cipher.iv = "\x00"*8
|
614
|
-
challenge = received_data[1..8].pack('C*')
|
615
|
-
challenge = cipher.update(challenge) + cipher.final
|
616
|
-
challenge = challenge.bytes.rotate
|
617
|
-
|
618
|
-
# Generate 8 bytes random number and encrypt the response
|
619
|
-
random_number = SecureRandom.random_bytes(8)
|
620
|
-
cipher.encrypt
|
621
|
-
cipher.iv = next_iv.pack('C*')
|
622
|
-
response = cipher.update(random_number + challenge.pack('C*')) + cipher.final
|
623
|
-
response = response.bytes
|
624
|
-
|
625
|
-
# Receive verification
|
626
|
-
buffer = [0xAF] + response
|
627
|
-
status, received_data = mifare_transceive(buffer)
|
628
|
-
return status if status != :status_ok
|
629
|
-
return :status_unknown_data if received_data[0] != 0x00
|
630
|
-
|
631
|
-
# Check if verification matches random_number rotated by 8 bits
|
632
|
-
cipher.decrypt
|
633
|
-
cipher.iv = response[-8..-1].pack('C*')
|
634
|
-
verification = received_data[1..8].pack('C*')
|
635
|
-
verification = cipher.update(verification) + cipher.final
|
636
|
-
|
637
|
-
if random_number.bytes.rotate != verification.bytes
|
638
|
-
picc_halt
|
639
|
-
return :status_auth_failed
|
440
|
+
private
|
441
|
+
|
442
|
+
# Read from SPI communication
|
443
|
+
def read_spi(reg)
|
444
|
+
output = 0
|
445
|
+
PiPiper::Spi.begin do |spi|
|
446
|
+
spi.chip_select_active_low(true)
|
447
|
+
spi.bit_order Spi::MSBFIRST
|
448
|
+
spi.clock @spi_spd
|
449
|
+
|
450
|
+
spi.chip_select(@spi_chip) do
|
451
|
+
spi.write((reg << 1) & 0x7E | 0x80)
|
452
|
+
output = spi.read
|
453
|
+
end
|
640
454
|
end
|
455
|
+
output
|
456
|
+
end
|
457
|
+
|
458
|
+
# Write to SPI communication
|
459
|
+
def write_spi(reg, values)
|
460
|
+
PiPiper::Spi.begin do |spi|
|
461
|
+
spi.chip_select_active_low(true)
|
462
|
+
spi.bit_order Spi::MSBFIRST
|
463
|
+
spi.clock @spi_spd
|
641
464
|
|
642
|
-
|
465
|
+
spi.chip_select(@spi_chip) do
|
466
|
+
spi.write((reg << 1) & 0x7E, *values)
|
467
|
+
end
|
468
|
+
end
|
643
469
|
end
|
644
470
|
|
645
|
-
#
|
646
|
-
def
|
647
|
-
|
648
|
-
|
649
|
-
|
471
|
+
# Set bits by mask
|
472
|
+
def write_spi_set_bitmask(reg, mask)
|
473
|
+
value = read_spi(reg)
|
474
|
+
write_spi(reg, value | mask)
|
475
|
+
end
|
650
476
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
477
|
+
# Clear bits by mask
|
478
|
+
def write_spi_clear_bitmask(reg, mask)
|
479
|
+
value = read_spi(reg)
|
480
|
+
write_spi(reg, value & (~mask))
|
481
|
+
end
|
655
482
|
|
656
|
-
|
657
|
-
|
658
|
-
|
483
|
+
# PCD transceive helper
|
484
|
+
def communicate_with_picc(command, send_data, framing_bit = 0)
|
485
|
+
wait_irq = 0x00
|
486
|
+
wait_irq = 0x10 if command == PCD_MFAuthent
|
487
|
+
wait_irq = 0x30 if command == PCD_Transceive
|
488
|
+
|
489
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
490
|
+
write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits
|
491
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
492
|
+
write_spi(FIFODataReg, send_data) # Write sendData to the FIFO
|
493
|
+
write_spi(BitFramingReg, framing_bit) # Bit adjustments
|
494
|
+
write_spi(CommandReg, command) # Execute the command
|
495
|
+
if command == PCD_Transceive
|
496
|
+
write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts
|
497
|
+
end
|
498
|
+
|
499
|
+
# Wait for the command to complete
|
500
|
+
i = 2000
|
501
|
+
loop do
|
502
|
+
irq = read_spi(ComIrqReg)
|
503
|
+
break if (irq & wait_irq) != 0
|
504
|
+
return :status_picc_timeout if (irq & 0x01) != 0
|
505
|
+
return :status_pcd_timeout if i == 0
|
506
|
+
i -= 1
|
507
|
+
end
|
508
|
+
|
509
|
+
# Check for error
|
510
|
+
error = read_spi(ErrorReg)
|
511
|
+
return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
|
659
512
|
|
660
|
-
|
661
|
-
|
513
|
+
# Receiving data
|
514
|
+
received_data = []
|
515
|
+
data_length = read_spi(FIFOLevelReg)
|
516
|
+
while data_length > 0 do
|
517
|
+
data = read_spi(FIFODataReg)
|
518
|
+
received_data << data
|
519
|
+
data_length -=1
|
662
520
|
end
|
521
|
+
valid_bits = read_spi(ControlReg) & 0x07
|
522
|
+
|
523
|
+
status = :status_ok
|
524
|
+
status = :status_collision if (error & 0x08) != 0 # CollErr
|
525
|
+
|
526
|
+
return status, received_data, valid_bits
|
527
|
+
end
|
528
|
+
|
529
|
+
def calculate_crc(data)
|
530
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
531
|
+
write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
|
532
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
533
|
+
write_spi(FIFODataReg, data) # Write data to the FIFO
|
534
|
+
write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
|
535
|
+
|
536
|
+
# Wait for the command to complete
|
537
|
+
i = 5000
|
538
|
+
loop do
|
539
|
+
irq = read_spi(DivIrqReg)
|
540
|
+
break if (irq & 0x04) != 0
|
541
|
+
raise PCDTimeoutError, 'Error calculating CRC' if i == 0
|
542
|
+
i -= 1
|
543
|
+
end
|
544
|
+
|
545
|
+
write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
|
546
|
+
|
547
|
+
[read_spi(CRCResultRegL), read_spi(CRCResultRegH)]
|
548
|
+
end
|
549
|
+
|
550
|
+
def append_crc(data)
|
551
|
+
data + calculate_crc(data)
|
552
|
+
end
|
553
|
+
|
554
|
+
def check_crc(data)
|
555
|
+
raise UnexpectedDataError, 'Data too short for CRC check' if data.size < 3
|
663
556
|
|
664
|
-
|
665
|
-
|
666
|
-
return :status_mifare_nack if received_data[0] != PICC_MF_ACK
|
557
|
+
data = data.dup
|
558
|
+
crc = data.pop(2)
|
667
559
|
|
668
|
-
|
560
|
+
crc == calculate_crc(data)
|
669
561
|
end
|
670
562
|
|
671
563
|
end
|