mfrc522 1.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ module MIFARE
2
+ module UltralightEV1
3
+ def auth(passwd)
4
+ passwd_bytes = [passwd].pack('H*').bytes
5
+ if passwd_bytes.size != 4
6
+ raise UsageError, "Expect 4 bytes password in hex, got: #{passwd_bytes.size} byte"
7
+ end
8
+
9
+ transceive([CMD_PWD_AUTH, *passwd_bytes])
10
+ end
11
+
12
+ def authed?
13
+ @authed || false
14
+ end
15
+
16
+ def fast_read(from, to)
17
+ if (to - from + 1) > @max_range
18
+ raise UsageError, "Reading from #{from} to #{to} exceeds PCD receive buffer"
19
+ end
20
+
21
+ transceive([CMD_FAST_READ, from, to])
22
+ end
23
+
24
+ def read_counter(counter)
25
+ transceive([CMD_READ_CNT, counter])
26
+ end
27
+
28
+ def increment_counter(counter)
29
+ transceive([CMD_INCR_CNT, counter])
30
+ end
31
+
32
+ def counter_torn?(counter)
33
+ transceive([CMD_CHECK_TEARING_EVENT, counter]) != 0xBD
34
+ end
35
+
36
+ def read_signature
37
+ transceive([CMD_READ_SIG, 0x00])
38
+ end
39
+ end
40
+ end
data/lib/picc.rb CHANGED
@@ -1,4 +1,11 @@
1
1
  class 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_ADDITIONAL_FRAME = 0xAF
8
+
2
9
  attr_reader :uid
3
10
  attr_reader :sak
4
11
 
@@ -7,22 +14,319 @@ class PICC
7
14
  @uid = uid
8
15
  @sak = sak
9
16
  @halted = false
17
+
18
+ ## ISO mode
19
+ @cid = 0x00 # We don't support CID, fix it to 0
20
+ @fsc = 16 # Assume PICC only supports 16 bytes frame
21
+ @fwt = 256 # 77.33ms(256 ticks) default frame waiting time
22
+ @picc_support_cid = false # PICC support for CID
23
+ @picc_support_nad = false # PICC support for NAD
24
+ @historical_byte = []
25
+ @block_number = 0 # ISO frame block number
26
+ @iso_selected = false # If card is in iso mode
10
27
  end
11
28
 
12
- def resume_communication
13
- if @pcd.reestablish_picc_communication(@uid)
14
- @halted = false
15
- true
29
+ def picc_transceive(send_data, accept_timeout = false, need_bits = false)
30
+ received_data, valid_bits = @pcd.picc_transceive(send_data, accept_timeout)
31
+ if need_bits
32
+ return received_data, valid_bits
16
33
  else
17
- false
34
+ return received_data
35
+ end
36
+ end
37
+
38
+ # Wrapper for handling ISO protocol
39
+ def iso_transceive(send_data)
40
+ # Split data according to max buffer size
41
+ send_data = [send_data] unless send_data.is_a? Array
42
+ chained_data = send_data.each_slice(@max_inf_size).to_a
43
+
44
+ # Initialize I-block
45
+ pcb = 0x02
46
+
47
+ # Send chained data
48
+ until chained_data.empty?
49
+ pcb &= 0xEF # Reset chaining indicator
50
+ pcb |= 0x10 if chained_data.size > 1 # Set chaining
51
+ pcb |= @block_number # Set block number
52
+ data = chained_data.shift
53
+
54
+ buffer = [pcb] + data
55
+
56
+ finished = false
57
+ until finished
58
+ received_data = handle_wtx(buffer)
59
+
60
+ # Retreive response pcb from data
61
+ r_pcb = received_data[0]
62
+
63
+ # Received ACK
64
+ if r_pcb & 0xF6 == 0xA2
65
+ # If ACK matches current block number means success
66
+ # Otherwise transmit it again
67
+ if (pcb & 0x01) == (r_pcb & 0x01)
68
+ finished = true
69
+ end
70
+ else
71
+ finished = true
72
+ end
73
+ end
74
+
75
+ @block_number ^= 1 # toggle block number for next frame
76
+ end
77
+
78
+ received_chained_data = [received_data]
79
+
80
+ # Receive chained data
81
+ while r_pcb & 0x10 != 0
82
+ ack = 0xA2 | @block_number # Set block number
83
+ received_data = handle_wtx([ack]) # Send ACK to receive next frame
84
+
85
+ r_pcb = received_data[0]
86
+
87
+ received_chained_data << received_data
88
+
89
+ @block_number ^= 1 # toggle block number for next frame
90
+ end
91
+
92
+ # Collect INF from chain
93
+ inf = []
94
+ received_chained_data.each do |data|
95
+ flag = data.shift
96
+ data.shift if flag & 0x08 != 0 # CID present
97
+ data.shift if flag & 0x04 != 0 # NAD present
98
+
99
+ inf.concat(data)
100
+ end
101
+ inf
102
+ end
103
+
104
+ # ISO/IEC 14443-4 select
105
+ def iso_select
106
+ # Send RATS (Request for Answer To Select)
107
+ buffer = [CMD_RATS, 0x50 | @cid]
108
+ received_data = picc_transceive(buffer)
109
+
110
+ process_ats(received_data)
111
+
112
+ # Send PPS (Protocol and Parameter Selection Request)
113
+ buffer = [CMD_PPS | @cid, 0x11, (@dsi << 2) | @dri]
114
+ received_data = picc_transceive(buffer)
115
+ raise UnexpectedDataError, 'Incorrect response' if received_data[0] != (0xD0 | @cid)
116
+
117
+ # Set PCD baud rate
118
+ @pcd.transceiver_baud_rate(:tx, @dri)
119
+ @pcd.transceiver_baud_rate(:rx, @dsi)
120
+
121
+ @block_number = 0
122
+ @max_frame_size = [@pcd.buffer_size, @fsc].min
123
+ @max_inf_size = @max_frame_size - 3 # PCB + CRC16
124
+ @max_inf_size -= 1 if @picc_support_cid
125
+ @max_inf_size -= 1 if @picc_support_nad
126
+ @iso_selected = true
127
+ end
128
+
129
+ # Send S(DESELECT)
130
+ def iso_deselect
131
+ buffer = [CMD_DESELECT]
132
+ received_data = picc_transceive(buffer)
133
+
134
+ result = received_data[0] & 0xF7 == CMD_DESELECT
135
+ @iso_selected = !result
136
+ result
137
+ end
138
+
139
+ def restart_communication
140
+ picc_was_in_iso_mode = @iso_selected
141
+ iso_deselect if picc_was_in_iso_mode
142
+ unless @pcd.reestablish_picc_communication(@uid)
143
+ halt
144
+ raise CommunicationError, 'Unable to resume communication or wrong card was presented. Halting cards in the field.'
18
145
  end
146
+ iso_select if picc_was_in_iso_mode
19
147
  end
20
148
 
21
149
  def halt
22
- if @pcd.picc_halt
23
- @halted = true
24
- else
25
- @halted = false
150
+ iso_deselect if @iso_selected
151
+ @halted = @pcd.picc_halt
152
+ end
153
+
154
+ def self.identify_model(sak)
155
+ # SAK coding separation reference:
156
+ # https://www.nxp.com/docs/en/application-note/AN10833.pdf
157
+ # https://www.nxp.com/docs/en/application-note/AN10834.pdf
158
+ if sak & 0x04 != 0
159
+ return :picc_uid_not_complete
160
+ end
161
+
162
+ if sak & 0x02 != 0
163
+ return :picc_reserved_future_use
164
+ end
165
+
166
+ if sak & 0x08 != 0
167
+ if sak & 0x10 != 0
168
+ return :picc_mifare_4k
169
+ end
170
+
171
+ if sak & 0x01 != 0
172
+ return :picc_mifare_mini
173
+ end
174
+
175
+ return :picc_mifare_1k
176
+ end
177
+
178
+ if sak & 0x10 != 0
179
+ if sak & 0x01 != 0
180
+ return :picc_mifare_plus_4k_sl2
181
+ end
182
+
183
+ return :picc_mifare_plus_2k_sl2
26
184
  end
185
+
186
+ if sak == 0x00
187
+ return :picc_mifare_ultralight
188
+ end
189
+
190
+ if sak & 0x20 != 0
191
+ return :picc_iso_14443_4
192
+ end
193
+
194
+ if sak & 0x40 != 0
195
+ return :picc_iso_18092
196
+ end
197
+
198
+ return :picc_unknown
199
+ end
200
+
201
+ protected
202
+
203
+ def crc32(*datas)
204
+ crc = 0xFFFFFFFF
205
+
206
+ datas.each do |data|
207
+ data = [data] unless data.is_a? Array
208
+ data.each do |byte|
209
+ crc ^= byte
210
+ 8.times do
211
+ flag = crc & 0x01 > 0
212
+ crc >>= 1
213
+ crc ^= 0xEDB88320 if flag
214
+ end
215
+ end
216
+ end
217
+ crc
218
+ end
219
+
220
+ private
221
+
222
+ def choose_d(value)
223
+ # ISO DS/DR
224
+ # 0b000: 106kBd, 0b001: 212kBd, 0b010: 424kBd, 0b100: 848kBd
225
+ # MFRC522 register & ISO DSI/DRI
226
+ # 0b000: 106kBd, 0b001: 212kBd, 0b010: 424kBd, 0b011: 848kBd
227
+ # Find largest bit(fastest baud rate)
228
+ x = (value >> 2) & 0x01
229
+ y = (value >> 1) & 0x01
230
+ z = value & 0x01
231
+
232
+ ((x | y) << 1) + (x | (~y & z))
233
+ end
234
+
235
+ # Gether information from ATS (Answer to Select)
236
+ def process_ats(ats)
237
+ position = 1
238
+ t0 = ats[position] # Format byte
239
+
240
+ fsci = t0 & 0x0F # PICC buffer size integer
241
+ y1 = (t0 >> 4) & 0x07 # Optional frame(TA, TB, TC) indicator
242
+ @fsc = FSCI_to_FSC.fetch(fsci) # Convert buffer size integer to bytes
243
+
244
+ # Frame: TA
245
+ if y1 & 0x01 != 0
246
+ position += 1
247
+ ta = ats[position]
248
+
249
+ dr = ta & 0x07 # PCD to PICC baud rate
250
+ ds = (ta >> 4) & 0x07 # PICC to PCD baud rate
251
+ same_d = (ta >> 7) & 0x01
252
+
253
+ if same_d != 0
254
+ dr &= ds
255
+ ds &= dr
256
+ end
257
+
258
+ @dri = choose_d(dr)
259
+ @dsi = choose_d(ds)
260
+ end
261
+
262
+ # Frame: TB
263
+ if y1 & 0x02 != 0
264
+ position += 1
265
+ tb = ats[position]
266
+
267
+ fwi = (tb >> 4) & 0x0F # Frame wating integer
268
+ sgfi = tb & 0x0F # Start-up frame guard integer
269
+
270
+ # Convert integers to real time
271
+ @fwt = (1 << fwi)
272
+ sgft = (1 << sgfi)
273
+
274
+ # Set frame waiting time
275
+ @pcd.internal_timer(@fwt)
276
+ end
277
+
278
+ # Get info about CID or NAD
279
+ if y1 & 0x04 != 0
280
+ position += 1
281
+ tc = ats[position]
282
+
283
+ @picc_support_cid = true if tc & 0x02 != 0
284
+ @picc_support_nad = true if tc & 0x01 != 0
285
+ end
286
+
287
+ position += 1
288
+
289
+ if ats.size - position > 0
290
+ @historical_byte = ats[position..-1]
291
+ end
292
+
293
+ # Start-up guard time
294
+ sleep 0.000302 * sgft
295
+ end
296
+
297
+ def handle_wtx(data)
298
+ 24.times do
299
+ begin
300
+ received_data = picc_transceive(data)
301
+ rescue CommunicationError => e
302
+ raise e unless e.is_a? PICCTimeoutError
303
+
304
+ # Try sending NAK when timeout
305
+ nak = 0xB2 | @block_number
306
+ data = [nak]
307
+ next
308
+ end
309
+
310
+ pcb = received_data[0]
311
+
312
+ # WTX detected
313
+ if pcb & 0xF7 == 0xF2
314
+ inf_position = (pcb & 0x08 != 0) ? 2 : 1
315
+ wtxm = received_data[inf_position] & 0x3F
316
+
317
+ # Set temporary timer
318
+ @pcd.internal_timer(@fwt * wtxm)
319
+
320
+ # Set WTX response
321
+ data = [0xF2, wtxm]
322
+ else
323
+ # Set timer back to FWT
324
+ @pcd.internal_timer(@fwt)
325
+
326
+ return received_data
327
+ end
328
+ end
329
+
330
+ raise PICCTimeoutError, 'Timeout while handling WTX frame.'
27
331
  end
28
332
  end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mfrc522
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - atitan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-25 00:00:00.000000000 Z
11
+ date: 2021-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pi_piper
14
+ name: fubuki
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 2.0.0
19
+ version: 1.0.1
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 2.0.0
30
- - - ">="
24
+ - - '='
31
25
  - !ruby/object:Gem::Version
32
- version: 2.0.0
26
+ version: 1.0.1
33
27
  description:
34
28
  email: commit@atifans.net
35
29
  executables: []
@@ -38,13 +32,17 @@ extra_rdoc_files: []
38
32
  files:
39
33
  - lib/core_ext.rb
40
34
  - lib/exceptions.rb
41
- - lib/iso144434.rb
42
35
  - lib/mfrc522.rb
43
36
  - lib/mifare/classic.rb
44
37
  - lib/mifare/des_fire.rb
45
38
  - lib/mifare/key.rb
39
+ - lib/mifare/plus.rb
40
+ - lib/mifare/plus_sl0.rb
41
+ - lib/mifare/plus_sl1.rb
42
+ - lib/mifare/plus_sl3.rb
46
43
  - lib/mifare/ultralight.rb
47
44
  - lib/mifare/ultralight_c.rb
45
+ - lib/mifare/ultralight_ev1.rb
48
46
  - lib/picc.rb
49
47
  homepage: https://github.com/atitan/MFRC522_Ruby
50
48
  licenses:
@@ -66,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
64
  version: '0'
67
65
  requirements: []
68
66
  rubyforge_project:
69
- rubygems_version: 2.5.1
67
+ rubygems_version: 2.7.6.2
70
68
  signing_key:
71
69
  specification_version: 4
72
70
  summary: MFRC522 RFID Reader Library for RaspberryPi