tamashii-mfrc522 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/core_ext.rb +35 -0
- data/lib/exceptions.rb +14 -0
- data/lib/iso144434.rb +247 -0
- data/lib/mfrc522.rb +616 -0
- data/lib/mifare/classic.rb +132 -0
- data/lib/mifare/des_fire.rb +687 -0
- data/lib/mifare/key.rb +180 -0
- data/lib/mifare/ultralight.rb +58 -0
- data/lib/mifare/ultralight_c.rb +86 -0
- data/lib/picc.rb +28 -0
- metadata +76 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 440eac7bf93b99e1fdcca04eadc90bb01f325b49
|
|
4
|
+
data.tar.gz: 25602072e19601347e17e7b58e30f7919ae35628
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 3e476988e2fcd4bfa7c501ffa37132711b52c0af1a15cb39ca45257e181934e718dcccfe6ac892be8ed01ec8cfeee6495afab514c4181be6775da4c3f2cd3cb2
|
|
7
|
+
data.tar.gz: 88575cc6eb0330cba4bdfa7776e8bdde6da11693a03c9963c9bd838c900ca57924a5595169c08feaaf845cbecea48d4faa2514666f2ad6527f95dfacd09ca467
|
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,247 @@
|
|
|
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.fetch(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
|
+
# FIXME: baud rate fixed to 106kBd
|
|
176
|
+
# until author can confirm negotiation works
|
|
177
|
+
dr = 0
|
|
178
|
+
ds = 0
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Frame: TB
|
|
182
|
+
if y1 & 0x02 != 0
|
|
183
|
+
position += 1
|
|
184
|
+
tb = ats[position]
|
|
185
|
+
|
|
186
|
+
fwi = (tb >> 4) & 0x0F # Frame wating integer
|
|
187
|
+
sgfi = tb & 0x0F # Start-up frame guard integer
|
|
188
|
+
|
|
189
|
+
# Convert integers to real time
|
|
190
|
+
@fwt = (1 << fwi)
|
|
191
|
+
sgft = (1 << sgfi)
|
|
192
|
+
|
|
193
|
+
# Set frame waiting time
|
|
194
|
+
@pcd.internal_timer(@fwt)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Get info about CID or NAD
|
|
198
|
+
if y1 & 0x04 != 0
|
|
199
|
+
position += 1
|
|
200
|
+
tc = ats[position]
|
|
201
|
+
|
|
202
|
+
@support_cid = true if tc & 0x02 != 0
|
|
203
|
+
@support_nad = true if tc & 0x01 != 0
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Start-up guard time
|
|
207
|
+
sleep 0.000302 * sgft
|
|
208
|
+
|
|
209
|
+
return dr, ds
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def handle_wtx(data)
|
|
213
|
+
24.times do
|
|
214
|
+
begin
|
|
215
|
+
received_data = @pcd.picc_transceive(data)
|
|
216
|
+
rescue CommunicationError => e
|
|
217
|
+
raise e unless e.is_a? PICCTimeoutError
|
|
218
|
+
|
|
219
|
+
# Try sending NAK when timeout
|
|
220
|
+
nak = 0xB2 | @block_number
|
|
221
|
+
data = [nak]
|
|
222
|
+
next
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
pcb = received_data[0]
|
|
226
|
+
|
|
227
|
+
# WTX detected
|
|
228
|
+
if pcb & 0xF7 == 0xF2
|
|
229
|
+
inf_position = (pcb & 0x08 != 0) ? 2 : 1
|
|
230
|
+
wtxm = received_data[inf_position] & 0x3F
|
|
231
|
+
|
|
232
|
+
# Set temporary timer
|
|
233
|
+
@pcd.internal_timer(@fwt * wtxm)
|
|
234
|
+
|
|
235
|
+
# Set WTX response
|
|
236
|
+
data = [0xF2, wtxm]
|
|
237
|
+
else
|
|
238
|
+
# Set timer back to FWT
|
|
239
|
+
@pcd.internal_timer(@fwt)
|
|
240
|
+
|
|
241
|
+
return received_data
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
raise PICCTimeoutError
|
|
246
|
+
end
|
|
247
|
+
end
|
data/lib/mfrc522.rb
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
require 'pi_piper'
|
|
2
|
+
|
|
3
|
+
require 'openssl'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
require 'core_ext'
|
|
7
|
+
require 'exceptions'
|
|
8
|
+
|
|
9
|
+
require 'picc'
|
|
10
|
+
require 'iso144434'
|
|
11
|
+
|
|
12
|
+
require 'mifare/key'
|
|
13
|
+
require 'mifare/classic'
|
|
14
|
+
require 'mifare/ultralight'
|
|
15
|
+
require 'mifare/ultralight_c'
|
|
16
|
+
require 'mifare/des_fire'
|
|
17
|
+
|
|
18
|
+
include PiPiper
|
|
19
|
+
|
|
20
|
+
class MFRC522
|
|
21
|
+
|
|
22
|
+
# PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
|
|
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
|
|
32
|
+
|
|
33
|
+
# PCD commands
|
|
34
|
+
PCD_Idle = 0x00 # no action, cancels current command execution
|
|
35
|
+
PCD_Mem = 0x01 # stores 25 bytes into the internal buffer
|
|
36
|
+
PCD_GenRandomID = 0x02 # generates a 10-byte random ID number
|
|
37
|
+
PCD_CalcCRC = 0x03 # activates the CRC coprocessor or performs a self test
|
|
38
|
+
PCD_Transmit = 0x04 # transmits data from the FIFO buffer
|
|
39
|
+
PCD_NoCmdChange = 0x07 # no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit
|
|
40
|
+
PCD_Receive = 0x08 # activates the receiver circuits
|
|
41
|
+
PCD_Transceive = 0x0C # transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission
|
|
42
|
+
PCD_MFAuthent = 0x0E # performs the MIFARE standard authentication as a reader
|
|
43
|
+
PCD_SoftReset = 0x0F # resets the MFRC522
|
|
44
|
+
|
|
45
|
+
# PCD Command and Status Registers
|
|
46
|
+
CommandReg = 0x01 # starts and stops command execution
|
|
47
|
+
ComIEnReg = 0x02 # enable and disable interrupt request control bits
|
|
48
|
+
DivIEnReg = 0x03 # enable and disable interrupt request control bits
|
|
49
|
+
ComIrqReg = 0x04 # interrupt request bits
|
|
50
|
+
DivIrqReg = 0x05 # interrupt request bits
|
|
51
|
+
ErrorReg = 0x06 # error bits showing the error status of the last command executed
|
|
52
|
+
Status1Reg = 0x07 # communication status bits
|
|
53
|
+
Status2Reg = 0x08 # receiver and transmitter status bits
|
|
54
|
+
FIFODataReg = 0x09 # input and output of 64 byte FIFO buffer
|
|
55
|
+
FIFOLevelReg = 0x0A # number of bytes stored in the FIFO buffer
|
|
56
|
+
WaterLevelReg = 0x0B # level for FIFO underflow and overflow warning
|
|
57
|
+
ControlReg = 0x0C # miscellaneous control registers
|
|
58
|
+
BitFramingReg = 0x0D # adjustments for bit-oriented frames
|
|
59
|
+
CollReg = 0x0E # bit position of the first bit-collision detected on the RF interface
|
|
60
|
+
|
|
61
|
+
# PCD Command Registers
|
|
62
|
+
ModeReg = 0x11 # defines general modes for transmitting and receiving
|
|
63
|
+
TxModeReg = 0x12 # defines transmission data rate and framing
|
|
64
|
+
RxModeReg = 0x13 # defines reception data rate and framing
|
|
65
|
+
TxControlReg = 0x14 # controls the logical behavior of the antenna driver pins TX1 and TX2
|
|
66
|
+
TxASKReg = 0x15 # controls the setting of the transmission modulation
|
|
67
|
+
TxSelReg = 0x16 # selects the internal sources for the antenna driver
|
|
68
|
+
RxSelReg = 0x17 # selects internal receiver settings
|
|
69
|
+
RxThresholdReg = 0x18 # selects thresholds for the bit decoder
|
|
70
|
+
DemodReg = 0x19 # defines demodulator settings
|
|
71
|
+
MfTxReg = 0x1C # controls some MIFARE communication transmit parameters
|
|
72
|
+
MfRxReg = 0x1D # controls some MIFARE communication receive parameters
|
|
73
|
+
SerialSpeedReg = 0x1F # selects the speed of the serial UART interface
|
|
74
|
+
|
|
75
|
+
# PCD Configuration Registers
|
|
76
|
+
CRCResultRegH = 0x21 # shows the MSB and LSB values of the CRC calculation
|
|
77
|
+
CRCResultRegL = 0x22
|
|
78
|
+
ModWidthReg = 0x24 # controls the ModWidth setting?
|
|
79
|
+
RFCfgReg = 0x26 # configures the receiver gain
|
|
80
|
+
GsNReg = 0x27 # selects the conductance of the antenna driver pins TX1 and TX2 for modulation
|
|
81
|
+
CWGsPReg = 0x28 # defines the conductance of the p-driver output during periods of no modulation
|
|
82
|
+
ModGsPReg = 0x29 # defines the conductance of the p-driver output during periods of modulation
|
|
83
|
+
TModeReg = 0x2A # defines settings for the internal timer
|
|
84
|
+
TPrescalerReg = 0x2B # the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
|
|
85
|
+
TReloadRegH = 0x2C # defines the 16-bit timer reload value
|
|
86
|
+
TReloadRegL = 0x2D
|
|
87
|
+
TCounterValueRegH = 0x2E # shows the 16-bit timer value
|
|
88
|
+
TCounterValueRegL = 0x2F
|
|
89
|
+
|
|
90
|
+
# PCD Test Registers
|
|
91
|
+
TestSel1Reg = 0x31 # general test signal configuration
|
|
92
|
+
TestSel2Reg = 0x32 # general test signal configuration
|
|
93
|
+
TestPinEnReg = 0x33 # enables pin output driver on pins D1 to D7
|
|
94
|
+
TestPinValueReg = 0x34 # defines the values for D1 to D7 when it is used as an I/O bus
|
|
95
|
+
TestBusReg = 0x35 # shows the status of the internal test bus
|
|
96
|
+
AutoTestReg = 0x36 # controls the digital self test
|
|
97
|
+
VersionReg = 0x37 # shows the software version
|
|
98
|
+
AnalogTestReg = 0x38 # controls the pins AUX1 and AUX2
|
|
99
|
+
TestDAC1Reg = 0x39 # defines the test value for TestDAC1
|
|
100
|
+
TestDAC2Reg = 0x3A # defines the test value for TestDAC2
|
|
101
|
+
TestADCReg = 0x3B # shows the value of ADC I and Q channels
|
|
102
|
+
|
|
103
|
+
def initialize(nrstpd = 24, chip = 0, spd = 1000000, timer = 256)
|
|
104
|
+
chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0,
|
|
105
|
+
1 => PiPiper::Spi::CHIP_SELECT_1,
|
|
106
|
+
2 => PiPiper::Spi::CHIP_SELECT_BOTH,
|
|
107
|
+
3 => PiPiper::Spi::CHIP_SELECT_NONE }
|
|
108
|
+
@spi_chip = chip_option[chip]
|
|
109
|
+
@spi_spd = spd
|
|
110
|
+
@timer = timer
|
|
111
|
+
|
|
112
|
+
# Power it up
|
|
113
|
+
@nrstpd = nrstpd
|
|
114
|
+
nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out)
|
|
115
|
+
nrstpd_pin.on
|
|
116
|
+
sleep 1.0 / 20.0 # Wait 50ms
|
|
117
|
+
|
|
118
|
+
soft_reset # Perform software reset
|
|
119
|
+
|
|
120
|
+
pcd_config_reset # Set default setting
|
|
121
|
+
|
|
122
|
+
antenna_on # Turn antenna on. They were disabled by the reset.
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Shutdown
|
|
126
|
+
def shutdown
|
|
127
|
+
antenna_off
|
|
128
|
+
# TODO: handle other platform?
|
|
129
|
+
if PiPiper::Platform.driver == PiPiper::Bcm2835
|
|
130
|
+
PiPiper::Platform.driver.unexport_pin(@nrstpd)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# PCD software reset
|
|
135
|
+
def soft_reset
|
|
136
|
+
write_spi(CommandReg, PCD_SoftReset)
|
|
137
|
+
sleep 1.0 / 20.0 # wait 50ms
|
|
138
|
+
|
|
139
|
+
write_spi(TModeReg, 0x87) # Start timer by setting TAuto=1, and higher part of TPrescalerReg
|
|
140
|
+
write_spi(TPrescalerReg, 0xFF) # Set lower part of TPrescalerReg, and results in 302us timer (f_timer = 13.56 MHz / (2*TPreScaler+1))
|
|
141
|
+
|
|
142
|
+
write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
|
|
143
|
+
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)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Reset PCD config to default
|
|
147
|
+
def pcd_config_reset
|
|
148
|
+
# Clear ValuesAfterColl bit
|
|
149
|
+
write_spi_clear_bitmask(CollReg, 0x80)
|
|
150
|
+
|
|
151
|
+
# Reset transceiver baud rate to 106 kBd
|
|
152
|
+
transceiver_baud_rate(:tx, 0)
|
|
153
|
+
transceiver_baud_rate(:rx, 0)
|
|
154
|
+
|
|
155
|
+
# Set PCD timer value for 302us default timer
|
|
156
|
+
internal_timer(@timer)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Control transceive timeout value
|
|
160
|
+
def internal_timer(timer = nil)
|
|
161
|
+
if timer
|
|
162
|
+
write_spi(TReloadRegH, (timer >> 8) & 0xFF)
|
|
163
|
+
write_spi(TReloadRegL, (timer & 0xFF))
|
|
164
|
+
end
|
|
165
|
+
(read_spi(TReloadRegH) << 8) | read_spi(TReloadRegL)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Control transceiver baud rate
|
|
169
|
+
# value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
|
|
170
|
+
def transceiver_baud_rate(direction, value = nil)
|
|
171
|
+
reg = {tx: TxModeReg, rx: RxModeReg}
|
|
172
|
+
|
|
173
|
+
if value
|
|
174
|
+
value <<= 4
|
|
175
|
+
value |= 0x80 if value != 0
|
|
176
|
+
write_spi(reg.fetch(direction), value)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
(read_spi(reg.fetch(direction)) >> 4) & 0x07
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Turn antenna on
|
|
183
|
+
def antenna_on
|
|
184
|
+
write_spi_set_bitmask(TxControlReg, 0x03)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Turn antenna off
|
|
188
|
+
def antenna_off
|
|
189
|
+
write_spi_clear_bitmask(TxControlReg, 0x03)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Modify and show antenna gain level
|
|
193
|
+
# level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB
|
|
194
|
+
def antenna_gain(level = nil)
|
|
195
|
+
unless level.nil?
|
|
196
|
+
level = 1 if level > 6 || level < 1
|
|
197
|
+
write_spi_set_bitmask(RFCfgReg, ((level + 1) << 4))
|
|
198
|
+
end
|
|
199
|
+
(read_spi(RFCfgReg) & 0x70) >> 4
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Wakes PICC from HALT or IDLE to ACTIVE state
|
|
203
|
+
# Accept PICC_REQA and PICC_WUPA command
|
|
204
|
+
def picc_request(picc_command)
|
|
205
|
+
pcd_config_reset
|
|
206
|
+
|
|
207
|
+
status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07)
|
|
208
|
+
|
|
209
|
+
status == :status_ok && valid_bits == 0 # REQA or WUPA command return 16 bits(full byte)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Instruct PICC in ACTIVE state go to HALT state
|
|
213
|
+
def picc_halt
|
|
214
|
+
buffer = append_crc([PICC_HLTA, 0])
|
|
215
|
+
|
|
216
|
+
status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer)
|
|
217
|
+
|
|
218
|
+
# PICC in HALT state will not respond
|
|
219
|
+
# If PICC sent reply, means it didn't acknowledge the command we sent
|
|
220
|
+
status == :status_picc_timeout
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Select PICC for further communication
|
|
224
|
+
#
|
|
225
|
+
# PICC must be in state ACTIVE
|
|
226
|
+
def picc_select
|
|
227
|
+
# Description of buffer structure:
|
|
228
|
+
#
|
|
229
|
+
# Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
|
|
230
|
+
# Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits.
|
|
231
|
+
# Byte 2: UID-data or Cascade Tag
|
|
232
|
+
# Byte 3: UID-data
|
|
233
|
+
# Byte 4: UID-data
|
|
234
|
+
# Byte 5: UID-data
|
|
235
|
+
# Byte 6: Block Check Character - XOR of bytes 2-5
|
|
236
|
+
# Byte 7: CRC_A
|
|
237
|
+
# Byte 8: CRC_A
|
|
238
|
+
# The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level.
|
|
239
|
+
#
|
|
240
|
+
# Description of bytes 2-5
|
|
241
|
+
#
|
|
242
|
+
# UID size Cascade level Byte2 Byte3 Byte4 Byte5
|
|
243
|
+
# ======== ============= ===== ===== ===== =====
|
|
244
|
+
# 4 bytes 1 uid0 uid1 uid2 uid3
|
|
245
|
+
# 7 bytes 1 CT uid0 uid1 uid2
|
|
246
|
+
# 2 uid3 uid4 uid5 uid6
|
|
247
|
+
# 10 bytes 1 CT uid0 uid1 uid2
|
|
248
|
+
# 2 CT uid3 uid4 uid5
|
|
249
|
+
# 3 uid6 uid7 uid8 uid9
|
|
250
|
+
pcd_config_reset
|
|
251
|
+
|
|
252
|
+
cascade_levels = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3]
|
|
253
|
+
uid = []
|
|
254
|
+
sak = 0
|
|
255
|
+
|
|
256
|
+
cascade_levels.each do |cascade_level|
|
|
257
|
+
buffer = [cascade_level]
|
|
258
|
+
current_level_known_bits = 0
|
|
259
|
+
received_data = []
|
|
260
|
+
valid_bits = 0
|
|
261
|
+
timeout = true
|
|
262
|
+
|
|
263
|
+
# Maxmimum loop count is defined in ISO spec
|
|
264
|
+
32.times do
|
|
265
|
+
if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything
|
|
266
|
+
# Validate buffer content against non-numeric classes and incorrect size
|
|
267
|
+
buffer = buffer[0..5]
|
|
268
|
+
dirty_buffer = buffer.size != 6
|
|
269
|
+
dirty_buffer ||= buffer.any? do |byte|
|
|
270
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
|
|
271
|
+
!byte.is_a?(Numeric)
|
|
272
|
+
else
|
|
273
|
+
!byte.is_a?(Fixnum)
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Retry reading UID when buffer is dirty, but don't reset loop count to prevent infinite loop
|
|
278
|
+
if dirty_buffer
|
|
279
|
+
# Reinitialize all variables
|
|
280
|
+
buffer = [cascade_level]
|
|
281
|
+
current_level_known_bits = 0
|
|
282
|
+
received_data = []
|
|
283
|
+
valid_bits = 0
|
|
284
|
+
|
|
285
|
+
# Continue to next loop
|
|
286
|
+
next
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
tx_last_bits = 0
|
|
290
|
+
buffer[1] = 0x70 # NVB - We're sending full length byte[0..6]
|
|
291
|
+
buffer[6] = (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
|
|
292
|
+
|
|
293
|
+
# Append CRC to buffer
|
|
294
|
+
buffer = append_crc(buffer)
|
|
295
|
+
else
|
|
296
|
+
tx_last_bits = current_level_known_bits % 8
|
|
297
|
+
uid_full_byte = current_level_known_bits / 8
|
|
298
|
+
all_full_byte = 2 + uid_full_byte # length of SEL + NVB + UID
|
|
299
|
+
buffer[1] = (all_full_byte << 4) + tx_last_bits # NVB
|
|
300
|
+
|
|
301
|
+
buffer_length = all_full_byte + (tx_last_bits > 0 ? 1 : 0)
|
|
302
|
+
buffer = buffer[0...buffer_length]
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
framing_bit = (tx_last_bits << 4) + tx_last_bits
|
|
306
|
+
|
|
307
|
+
# Select it
|
|
308
|
+
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, buffer, framing_bit)
|
|
309
|
+
|
|
310
|
+
if status != :status_ok && status != :status_collision
|
|
311
|
+
raise CommunicationError, status
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
if received_data.empty?
|
|
315
|
+
raise UnexpectedDataError, 'Received empty UID data'
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Append received UID into buffer if not doing full select
|
|
319
|
+
if current_level_known_bits < 32
|
|
320
|
+
# Check for last collision
|
|
321
|
+
if tx_last_bits != 0
|
|
322
|
+
buffer[-1] |= received_data.shift
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
buffer += received_data
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Handle collision
|
|
329
|
+
if status == :status_collision
|
|
330
|
+
collision = read_spi(CollReg)
|
|
331
|
+
|
|
332
|
+
# CollPosNotValid - We don't know where collision happened
|
|
333
|
+
raise CollisionError if (collision & 0x20) != 0
|
|
334
|
+
|
|
335
|
+
collision_position = collision & 0x1F
|
|
336
|
+
collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32
|
|
337
|
+
raise CollisionError if collision_position <= current_level_known_bits
|
|
338
|
+
|
|
339
|
+
# Calculate positioin
|
|
340
|
+
current_level_known_bits = collision_position
|
|
341
|
+
uid_bit = (current_level_known_bits - 1) % 8
|
|
342
|
+
|
|
343
|
+
# Mark the collision bit
|
|
344
|
+
buffer[-1] |= (1 << uid_bit)
|
|
345
|
+
else
|
|
346
|
+
if current_level_known_bits >= 32
|
|
347
|
+
timeout = false
|
|
348
|
+
break
|
|
349
|
+
end
|
|
350
|
+
current_level_known_bits = 32 # We've already known all bits, loop again for a complete select
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Handle timeout after 32 loops
|
|
355
|
+
if timeout
|
|
356
|
+
raise UnexpectedDataError, 'Keep receiving incomplete UID until timeout'
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# We've finished current cascade level
|
|
360
|
+
# Check and collect all uid stored in buffer
|
|
361
|
+
|
|
362
|
+
# Append UID
|
|
363
|
+
uid << buffer[2] if buffer[2] != PICC_CT
|
|
364
|
+
uid << buffer[3] << buffer[4] << buffer[5]
|
|
365
|
+
|
|
366
|
+
# Check the result of full select
|
|
367
|
+
# Select Acknowledge is 1 byte + CRC16
|
|
368
|
+
raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0
|
|
369
|
+
raise IncorrectCRCError unless check_crc(received_data)
|
|
370
|
+
|
|
371
|
+
sak = received_data[0]
|
|
372
|
+
break if (sak & 0x04) == 0 # No more cascade level
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
return uid, sak
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Trying to restart picc
|
|
379
|
+
def reestablish_picc_communication(uid)
|
|
380
|
+
picc_halt
|
|
381
|
+
picc_request(PICC_WUPA)
|
|
382
|
+
|
|
383
|
+
begin
|
|
384
|
+
new_uid, _new_sak = picc_select
|
|
385
|
+
status = true
|
|
386
|
+
rescue CommunicationError
|
|
387
|
+
status = false
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
status && uid == new_uid
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Lookup PICC name using sak
|
|
394
|
+
def identify_model(sak)
|
|
395
|
+
# SAK coding separation reference:
|
|
396
|
+
# http://cache.nxp.com/documents/application_note/AN10833.pdf
|
|
397
|
+
# http://www.nxp.com/documents/application_note/130830.pdf
|
|
398
|
+
if sak & 0x04 != 0
|
|
399
|
+
return :picc_uid_not_complete
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
if sak & 0x02 != 0
|
|
403
|
+
return :picc_reserved_future_use
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
if sak & 0x08 != 0
|
|
407
|
+
if sak & 0x10 != 0
|
|
408
|
+
return :picc_mifare_4k
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
if sak & 0x01 != 0
|
|
412
|
+
return :picc_mifare_mini
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
return :picc_mifare_1k
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
if sak & 0x10 != 0
|
|
419
|
+
if sak & 0x01 != 0
|
|
420
|
+
return :picc_mifare_plus_4k_sl2
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
return :picc_mifare_plus_2k_sl2
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
if sak == 0x00
|
|
427
|
+
return :picc_mifare_ultralight
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
if sak & 0x20 != 0
|
|
431
|
+
return :picc_iso_14443_4
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
if sak & 0x40 != 0
|
|
435
|
+
return :picc_iso_18092
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
return :picc_unknown
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Start Crypto1 communication between reader and Mifare PICC
|
|
442
|
+
#
|
|
443
|
+
# PICC must be selected before calling for authentication
|
|
444
|
+
# Remember to deauthenticate after communication, or no new communication can be made
|
|
445
|
+
#
|
|
446
|
+
# Accept PICC_MF_AUTH_KEY_A or PICC_MF_AUTH_KEY_B command
|
|
447
|
+
# Checks datasheets for block address numbering of your PICC
|
|
448
|
+
#
|
|
449
|
+
def mifare_crypto1_authenticate(command, block_addr, sector_key, uid)
|
|
450
|
+
# Buffer[12]: {command, block_addr, sector_key[6], uid[4]}
|
|
451
|
+
buffer = [command, block_addr]
|
|
452
|
+
buffer.concat(sector_key[0..5])
|
|
453
|
+
buffer.concat(uid[0..3])
|
|
454
|
+
|
|
455
|
+
communicate_with_picc(PCD_MFAuthent, buffer)
|
|
456
|
+
|
|
457
|
+
# Check MFCrypto1On bit
|
|
458
|
+
(read_spi(Status2Reg) & 0x08) != 0
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
# Stop Crypto1 communication
|
|
462
|
+
def mifare_crypto1_deauthenticate
|
|
463
|
+
write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Append CRC to buffer and check CRC or Mifare acknowledge
|
|
467
|
+
def picc_transceive(send_data, accept_timeout = false)
|
|
468
|
+
send_data = append_crc(send_data)
|
|
469
|
+
|
|
470
|
+
puts "Sending Data: #{send_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
|
|
471
|
+
|
|
472
|
+
# Transfer data
|
|
473
|
+
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
|
|
474
|
+
return [] if status == :status_picc_timeout && accept_timeout
|
|
475
|
+
raise PICCTimeoutError if status == :status_picc_timeout
|
|
476
|
+
raise CommunicationError, status if status != :status_ok
|
|
477
|
+
|
|
478
|
+
puts "Received Data: #{received_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
|
|
479
|
+
|
|
480
|
+
# Data exists, check CRC and return
|
|
481
|
+
if received_data.size > 1
|
|
482
|
+
raise IncorrectCRCError unless check_crc(received_data)
|
|
483
|
+
|
|
484
|
+
return received_data[0..-3]
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
|
|
488
|
+
raise MifareNakError, received_data[0] if received_data[0] != PICC_MF_ACK
|
|
489
|
+
|
|
490
|
+
received_data
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
private
|
|
494
|
+
|
|
495
|
+
# Read from SPI communication
|
|
496
|
+
def read_spi(reg)
|
|
497
|
+
output = 0
|
|
498
|
+
PiPiper::Spi.begin do |spi|
|
|
499
|
+
spi.chip_select_active_low(true)
|
|
500
|
+
spi.bit_order Spi::MSBFIRST
|
|
501
|
+
spi.clock @spi_spd
|
|
502
|
+
|
|
503
|
+
spi.chip_select(@spi_chip) do
|
|
504
|
+
spi.write((reg << 1) & 0x7E | 0x80)
|
|
505
|
+
output = spi.read
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
output
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
# Write to SPI communication
|
|
512
|
+
def write_spi(reg, values)
|
|
513
|
+
PiPiper::Spi.begin do |spi|
|
|
514
|
+
spi.chip_select_active_low(true)
|
|
515
|
+
spi.bit_order Spi::MSBFIRST
|
|
516
|
+
spi.clock @spi_spd
|
|
517
|
+
|
|
518
|
+
spi.chip_select(@spi_chip) do
|
|
519
|
+
spi.write((reg << 1) & 0x7E, *values)
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Set bits by mask
|
|
525
|
+
def write_spi_set_bitmask(reg, mask)
|
|
526
|
+
value = read_spi(reg)
|
|
527
|
+
write_spi(reg, value | mask)
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Clear bits by mask
|
|
531
|
+
def write_spi_clear_bitmask(reg, mask)
|
|
532
|
+
value = read_spi(reg)
|
|
533
|
+
write_spi(reg, value & (~mask))
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# PCD transceive helper
|
|
537
|
+
def communicate_with_picc(command, send_data, framing_bit = 0)
|
|
538
|
+
wait_irq = 0x00
|
|
539
|
+
wait_irq = 0x10 if command == PCD_MFAuthent
|
|
540
|
+
wait_irq = 0x30 if command == PCD_Transceive
|
|
541
|
+
|
|
542
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
|
543
|
+
write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits
|
|
544
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
|
545
|
+
write_spi(FIFODataReg, send_data) # Write sendData to the FIFO
|
|
546
|
+
write_spi(BitFramingReg, framing_bit) # Bit adjustments
|
|
547
|
+
write_spi(CommandReg, command) # Execute the command
|
|
548
|
+
if command == PCD_Transceive
|
|
549
|
+
write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
# Wait for the command to complete
|
|
553
|
+
i = 2000
|
|
554
|
+
loop do
|
|
555
|
+
irq = read_spi(ComIrqReg)
|
|
556
|
+
break if (irq & wait_irq) != 0
|
|
557
|
+
return :status_picc_timeout if (irq & 0x01) != 0
|
|
558
|
+
return :status_pcd_timeout if i == 0
|
|
559
|
+
i -= 1
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Check for error
|
|
563
|
+
error = read_spi(ErrorReg)
|
|
564
|
+
return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
|
|
565
|
+
|
|
566
|
+
# Receiving data
|
|
567
|
+
received_data = []
|
|
568
|
+
data_length = read_spi(FIFOLevelReg)
|
|
569
|
+
while data_length > 0 do
|
|
570
|
+
data = read_spi(FIFODataReg)
|
|
571
|
+
received_data << data
|
|
572
|
+
data_length -=1
|
|
573
|
+
end
|
|
574
|
+
valid_bits = read_spi(ControlReg) & 0x07
|
|
575
|
+
|
|
576
|
+
status = :status_ok
|
|
577
|
+
status = :status_collision if (error & 0x08) != 0 # CollErr
|
|
578
|
+
|
|
579
|
+
return status, received_data, valid_bits
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def calculate_crc(data)
|
|
583
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
|
584
|
+
write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
|
|
585
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
|
586
|
+
write_spi(FIFODataReg, data) # Write data to the FIFO
|
|
587
|
+
write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
|
|
588
|
+
|
|
589
|
+
# Wait for the command to complete
|
|
590
|
+
i = 5000
|
|
591
|
+
loop do
|
|
592
|
+
irq = read_spi(DivIrqReg)
|
|
593
|
+
break if (irq & 0x04) != 0
|
|
594
|
+
raise PCDTimeoutError, 'Error calculating CRC' if i == 0
|
|
595
|
+
i -= 1
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
|
|
599
|
+
|
|
600
|
+
[read_spi(CRCResultRegL), read_spi(CRCResultRegH)]
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def append_crc(data)
|
|
604
|
+
data + calculate_crc(data)
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def check_crc(data)
|
|
608
|
+
raise UnexpectedDataError, 'Data too short for CRC check' if data.size < 3
|
|
609
|
+
|
|
610
|
+
data = data.dup
|
|
611
|
+
crc = data.pop(2)
|
|
612
|
+
|
|
613
|
+
crc == calculate_crc(data)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
end
|