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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ae695036f039f29942ea9a3e46cb365fb1e4fe7
4
- data.tar.gz: c144d7dcf96074d047a3eba55523a27f2b3a74fb
3
+ metadata.gz: ec95f072c266803e40c9178fba2435c8f113d5ab
4
+ data.tar.gz: 98eb6c503409371dcb7e25f7e61f02494c5c13b4
5
5
  SHA512:
6
- metadata.gz: ae5877e53851c774fdf68ac3ccb65fe3623d6a446e42e39f1ac843b0ecc8bcb7aaac68bf657ce632154d0e6876ee5786e884acebb9d9c53156818f6c9f1a8ecf
7
- data.tar.gz: b8e8e5df57870a7da924c7a8dd207d2fa6ba89be281116c1b7ba5da5327bd3eb35b1e402bc80d61e06bce57293c8325f46dad25aca5409da129668c6f52e4ee7
6
+ metadata.gz: 23304b26becaed10dc250a013bc5a098894e9ce03f688986ca9fd403897bdf4472603aba2f7bc216bb26732a70c515c5f302c656e559a4fc2e6e51f5a8ec68a3
7
+ data.tar.gz: b8afd661c9ed6e8e898716e5c91bbfee19d373bb1287822f12fa8f42890d42e1cce1fc844312e312a52724a1f97ca58c5a53ad85215e12ac7931e12cd797cf88
@@ -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
@@ -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
@@ -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
@@ -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 'mifare/base'
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 = 0x26 # REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame.
20
- 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.
21
- PICC_CT = 0x88 # Cascade Tag. Not really a command, but used during anti collision.
22
- PICC_SEL_CL1 = 0x93 # Anti collision/Select, Cascade Level 1
23
- PICC_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2
24
- PICC_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3
25
- PICC_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
26
- # The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9)
27
- # Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector.
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
- # Constructor
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
- write_spi(TModeReg, 0x8D) # Start timer by setting TAuto=1, and higher part of TPrescalerReg
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
- # Read from SPI communication
149
- def read_spi(reg)
150
- output = 0
151
- PiPiper::Spi.begin do |spi|
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
- spi.chip_select(@spi_chip) do
157
- spi.write((reg << 1) & 0x7E | 0x80)
158
- output = spi.read
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
- # Write to SPI communication
165
- def write_spi(reg, values)
166
- PiPiper::Spi.begin do |spi|
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
- spi.chip_select(@spi_chip) do
172
- spi.write((reg << 1) & 0x7E, *values)
173
- end
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
- # Helper for setting bits by mask
178
- def write_spi_set_bitmask(reg, mask)
179
- value = read_spi(reg)
180
- write_spi(reg, value | mask)
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
- # Helper for clearing bits by mask
184
- def write_spi_clear_bitmask(reg, mask)
185
- value = read_spi(reg)
186
- write_spi(reg, value & (~mask))
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
- write_spi_clear_bitmask(CollReg, 0x80) # ValuesAfterColl=1 => Bits received after collision are cleared.
195
+ pcd_config_reset
313
196
 
314
197
  status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07)
315
198
 
316
- return status if status != :status_ok
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
- return :status_ok if status == :status_picc_timeout
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
- write_spi_clear_bitmask(CollReg, 0x80) # ValuesAfterColl=1 => Bits received after collision are cleared.
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
- for current_cascade_level in 0..2
373
- buffer = [select_level[current_cascade_level]]
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
- status, buffer = append_crc(buffer)
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
- # Try to fetch UID
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
- return :status_collision if (collision & 0x20) != 0 # CollPosNotValid - We don't know where collision happened
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
- return :status_internal_error if collision_position <= current_level_known_bits
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
- return status
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 this level
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
- return :status_sak_error if received_data.count != 3 || valid_bits != 0 # Select Acknowledge is 1 byte + CRC_A
441
-
442
- status = check_crc(received_data)
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 :status_ok, uid, sak
322
+ return uid, sak
451
323
  end
452
324
 
453
- # Trying to wake it up again
325
+ # Trying to restart picc
454
326
  def reestablish_picc_communication(uid)
455
- status = picc_halt
456
- return status if status != :status_ok
457
-
458
- status = picc_request(PICC_WUPA)
459
- return status if status != :status_ok
460
-
461
- status, new_uid, new_sak = picc_select
462
- return status if status != :status_ok
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
- # ISO/IEC 14443-4 select
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 encrypted Crypto1 communication between reader and Mifare PICC
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 += sector_key[0..5]
574
- buffer += uid[0..3]
399
+ buffer.concat(sector_key[0..5])
400
+ buffer.concat(uid[0..3])
575
401
 
576
- status, _received_data, _valid_bits = communicate_with_picc(PCD_MFAuthent, buffer)
402
+ communicate_with_picc(PCD_MFAuthent, buffer)
577
403
 
578
- return status if status != :status_ok
579
- return :status_auth_failed if (read_spi(Status2Reg) & 0x08) == 0
580
-
581
- return :status_ok
404
+ # Check MFCrypto1On bit
405
+ (read_spi(Status2Reg) & 0x08) != 0
582
406
  end
583
407
 
584
- # Stop Mifare encrypted communication
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
- def mifare_ultralight_3des_check
590
- # Ask for authentication
591
- buffer = [PICC_UL_3DES_AUTH, 0x00]
592
- status, received_data = mifare_transceive(buffer)
593
- return status if status != :status_ok
594
- return :status_unknown_data if received_data[0] != 0xAF
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
- return :status_ok, received_data
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
- def mifare_ultralight_3des_authenticate(des_key)
600
- status, received_data = mifare_ultralight_3des_check
601
- return status if status != :status_ok
602
-
603
- # Use received data as IV for next transmission
604
- next_iv = received_data[1..8]
605
-
606
- # Cipher
607
- cipher = OpenSSL::Cipher.new 'des-ede3-cbc'
608
- cipher.key = [des_key*2].pack('H*')
609
- cipher.padding = 0
610
-
611
- # Decrypt challenge random number and rotate it by 8 bits
612
- cipher.decrypt
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
- return :status_ok
465
+ spi.chip_select(@spi_chip) do
466
+ spi.write((reg << 1) & 0x7E, *values)
467
+ end
468
+ end
643
469
  end
644
470
 
645
- # Helper that append CRC to buffer and check CRC or Mifare acknowledge
646
- def mifare_transceive(send_data, accept_timeout = false)
647
- # Append CRC
648
- status, send_data = append_crc(send_data)
649
- return status if status != :status_ok
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
- # Transfer data
652
- status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
653
- return :status_ok if status == :status_picc_timeout && accept_timeout
654
- return status if status != :status_ok
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
- # Data exists, check CRC and return
657
- if received_data.size > 1
658
- return :status_crc_error if received_data.size < 3 || valid_bits != 0
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
- status = check_crc(received_data)
661
- return status, received_data[0..-3]
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
- # Data doesn't exist, check mifare acknowledge
665
- return :status_error if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
666
- return :status_mifare_nack if received_data[0] != PICC_MF_ACK
557
+ data = data.dup
558
+ crc = data.pop(2)
667
559
 
668
- return :status_ok
560
+ crc == calculate_crc(data)
669
561
  end
670
562
 
671
563
  end