mfrc522 1.0.2 → 3.0.0

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