Class: PICC
- Inherits:
-
Object
- Object
- PICC
- Defined in:
- lib/picc.rb
Direct Known Subclasses
MIFARE::Classic, MIFARE::DESFire, MIFARE::Plus, MIFARE::Ultralight
Constant Summary collapse
- FSCI_to_FSC =
[16, 24, 32, 40, 48, 64, 96, 128, 256]
- CMD_RATS =
0xE0
- CMD_PPS =
0xD0
- CMD_DESELECT =
0xC2
- CMD_ADDITIONAL_FRAME =
0xAF
Instance Attribute Summary collapse
-
#sak ⇒ Object
readonly
Returns the value of attribute sak.
-
#uid ⇒ Object
readonly
Returns the value of attribute uid.
Class Method Summary collapse
Instance Method Summary collapse
- #halt ⇒ Object
-
#initialize(pcd, uid, sak) ⇒ PICC
constructor
A new instance of PICC.
-
#iso_deselect ⇒ Object
Send S(DESELECT).
-
#iso_select ⇒ Object
ISO/IEC 14443-4 select.
-
#iso_transceive(send_data) ⇒ Object
Wrapper for handling ISO protocol.
- #picc_transceive(send_data, accept_timeout = false, need_bits = false) ⇒ Object
- #restart_communication ⇒ Object
Constructor Details
#initialize(pcd, uid, sak) ⇒ PICC
Returns a new instance of PICC.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/picc.rb', line 12 def initialize(pcd, uid, sak) @pcd = pcd @uid = uid @sak = sak @halted = false ## ISO mode @cid = 0x00 # We don't support CID, fix it to 0 @fsc = 16 # Assume PICC only supports 16 bytes frame @fwt = 256 # 77.33ms(256 ticks) default frame waiting time @picc_support_cid = false # PICC support for CID @picc_support_nad = false # PICC support for NAD @historical_byte = [] @block_number = 0 # ISO frame block number @iso_selected = false # If card is in iso mode end |
Instance Attribute Details
#sak ⇒ Object (readonly)
Returns the value of attribute sak.
10 11 12 |
# File 'lib/picc.rb', line 10 def sak @sak end |
#uid ⇒ Object (readonly)
Returns the value of attribute uid.
9 10 11 |
# File 'lib/picc.rb', line 9 def uid @uid end |
Class Method Details
.identify_model(sak) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/picc.rb', line 154 def self.identify_model(sak) # SAK coding separation reference: # https://www.nxp.com/docs/en/application-note/AN10833.pdf # https://www.nxp.com/docs/en/application-note/AN10834.pdf if sak & 0x04 != 0 return :picc_uid_not_complete end if sak & 0x02 != 0 return :picc_reserved_future_use end if sak & 0x08 != 0 if sak & 0x10 != 0 return :picc_mifare_4k end if sak & 0x01 != 0 return :picc_mifare_mini end return :picc_mifare_1k end if sak & 0x10 != 0 if sak & 0x01 != 0 return :picc_mifare_plus_4k_sl2 end return :picc_mifare_plus_2k_sl2 end if sak == 0x00 return :picc_mifare_ultralight end if sak & 0x20 != 0 return :picc_iso_14443_4 end if sak & 0x40 != 0 return :picc_iso_18092 end return :picc_unknown end |
Instance Method Details
#halt ⇒ Object
149 150 151 152 |
# File 'lib/picc.rb', line 149 def halt iso_deselect if @iso_selected @halted = @pcd.picc_halt end |
#iso_deselect ⇒ Object
Send S(DESELECT)
130 131 132 133 134 135 136 137 |
# File 'lib/picc.rb', line 130 def iso_deselect buffer = [CMD_DESELECT] received_data = picc_transceive(buffer) result = received_data[0] & 0xF7 == CMD_DESELECT @iso_selected = !result result end |
#iso_select ⇒ Object
ISO/IEC 14443-4 select
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/picc.rb', line 105 def iso_select # Send RATS (Request for Answer To Select) buffer = [CMD_RATS, 0x50 | @cid] received_data = picc_transceive(buffer) process_ats(received_data) # Send PPS (Protocol and Parameter Selection Request) buffer = [CMD_PPS | @cid, 0x11, (@dsi << 2) | @dri] received_data = picc_transceive(buffer) raise UnexpectedDataError, 'Incorrect response' if received_data[0] != (0xD0 | @cid) # Set PCD baud rate @pcd.transceiver_baud_rate(:tx, @dri) @pcd.transceiver_baud_rate(:rx, @dsi) @block_number = 0 @max_frame_size = [@pcd.buffer_size, @fsc].min @max_inf_size = @max_frame_size - 3 # PCB + CRC16 @max_inf_size -= 1 if @picc_support_cid @max_inf_size -= 1 if @picc_support_nad @iso_selected = true end |
#iso_transceive(send_data) ⇒ Object
Wrapper for handling ISO protocol
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/picc.rb', line 39 def iso_transceive(send_data) # Split data according to max buffer size send_data = [send_data] unless send_data.is_a? Array chained_data = send_data.each_slice(@max_inf_size).to_a # Initialize I-block pcb = 0x02 # Send chained data until chained_data.empty? pcb &= 0xEF # Reset chaining indicator pcb |= 0x10 if chained_data.size > 1 # Set chaining pcb |= @block_number # Set block number data = chained_data.shift buffer = [pcb] + data finished = false until finished received_data = handle_wtx(buffer) # Retreive response pcb from data r_pcb = received_data[0] # Received ACK if r_pcb & 0xF6 == 0xA2 # If ACK matches current block number means success # Otherwise transmit it again if (pcb & 0x01) == (r_pcb & 0x01) finished = true end else finished = true end end @block_number ^= 1 # toggle block number for next frame end received_chained_data = [received_data] # Receive chained data while r_pcb & 0x10 != 0 ack = 0xA2 | @block_number # Set block number received_data = handle_wtx([ack]) # Send ACK to receive next frame r_pcb = received_data[0] received_chained_data << received_data @block_number ^= 1 # toggle block number for next frame end # Collect INF from chain inf = [] received_chained_data.each do |data| flag = data.shift data.shift if flag & 0x08 != 0 # CID present data.shift if flag & 0x04 != 0 # NAD present inf.concat(data) end inf end |
#picc_transceive(send_data, accept_timeout = false, need_bits = false) ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/picc.rb', line 29 def picc_transceive(send_data, accept_timeout = false, need_bits = false) received_data, valid_bits = @pcd.picc_transceive(send_data, accept_timeout) if need_bits return received_data, valid_bits else return received_data end end |
#restart_communication ⇒ Object
139 140 141 142 143 144 145 146 147 |
# File 'lib/picc.rb', line 139 def restart_communication picc_was_in_iso_mode = @iso_selected iso_deselect if picc_was_in_iso_mode unless @pcd.reestablish_picc_communication(@uid) halt raise CommunicationError, 'Unable to resume communication or wrong card was presented. Halting cards in the field.' end iso_select if picc_was_in_iso_mode end |