tamashii-mfrc522 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
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