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 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