forked from google/periph
/
mfrc522.go
426 lines (381 loc) · 10.8 KB
/
mfrc522.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
// Copyright 2018 The Periph Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
// Package mfrc522 controls a Mifare RFID card reader.
//
// Datasheet
//
// https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf
package mfrc522
import (
"fmt"
"sync"
"time"
"periph.io/x/periph/conn"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/spi"
"periph.io/x/periph/experimental/devices/mfrc522/commands"
)
// Dev is an handle to an MFRC522 RFID reader.
type Dev struct {
LowLevel *commands.LowLevel
operationTimeout time.Duration
beforeCall func()
afterCall func()
}
// Key is the access key that consists of 6 bytes. There could be two types of keys - keyA and keyB.
// KeyA and KeyB correspond to the different sector trail and data access. Refer to the datasheet for more details.
type Key [6]byte
// DefaultKey provides the default bytes for card authentication for method B.
var DefaultKey = Key{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
type config struct {
defaultTimeout time.Duration
beforeCall func()
afterCall func()
}
type configF func(*config) *config
// WithTimeout updates the default device-wide configuration timeout.
func WithTimeout(timeout time.Duration) configF {
return func(c *config) *config {
c.defaultTimeout = timeout
return c
}
}
// WithSyncsets the synchronization for the entire device, using internal mutex.
func WithSync() configF {
var mu sync.Mutex
return func(c *config) *config {
c.beforeCall = mu.Lock
c.afterCall = mu.Unlock
return c
}
}
// noop does nothing
func noop() {}
// NewSPI creates and initializes the RFID card reader attached to SPI.
//
// spiPort the SPI device to use.
// resetPin reset GPIO pin.
// irqPin irq GPIO pin.
func NewSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn, configs ...configF) (*Dev, error) {
cfg := &config{
defaultTimeout: 30 * time.Second,
beforeCall: noop,
afterCall: noop,
}
for _, cf := range configs {
cfg = cf(cfg)
}
raw, err := commands.NewLowLevelSPI(spiPort, resetPin, irqPin)
if err != nil {
return nil, err
}
if err := raw.Init(); err != nil {
return nil, err
}
dev := &Dev{
LowLevel: raw,
operationTimeout: cfg.defaultTimeout,
beforeCall: cfg.beforeCall,
afterCall: cfg.afterCall,
}
return dev, nil
}
// String implements conn.Resource.
func (r *Dev) String() string {
return r.LowLevel.String()
}
// Halt implements conn.Resource.
//
// It soft-stops the chip - PowerDown bit set, command IDLE
func (r *Dev) Halt() error {
r.beforeCall()
defer r.afterCall()
return r.LowLevel.Halt()
}
// SetAntennaGain configures antenna signal strength.
//
// gain signal strength from 0 to 7.
func (r *Dev) SetAntennaGain(gain int) error {
r.beforeCall()
defer r.afterCall()
if gain < 0 || gain > 7 {
return wrapf("gain must be in [0..7] interval")
}
r.LowLevel.SetAntennaGain(gain)
return nil
}
// ReadCard reads the card sector/block with IRQ event timeout.
//
// timeout the operation timeout
// auth the authentication mode.
// sector the sector to authenticate on.
// block the block within sector to authenticate.
// key the key to be used for accessing the sector data.
func (r *Dev) ReadCard(timeout time.Duration, auth byte, sector int, block int, key Key) (data []byte, err error) {
r.beforeCall()
defer func() {
r.afterCall()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard(timeout)
if err != nil {
return
}
state, err := r.LowLevel.Auth(auth, calcBlockAddress(sector, block), key, uuid)
if err != nil {
return
}
if state != commands.AuthOk {
err = wrapf("can not authenticate")
return
}
return r.readBlock(sector, block)
}
// ReadAuth reads the card authentication data with IRQ event timeout.
//
// timeout the operation timeout
// auth authentication type
// sector the sector to authenticate on.
// key the key to be used for accessing the sector data.
func (r *Dev) ReadAuth(timeout time.Duration, auth byte, sector int, key Key) (data []byte, err error) {
r.beforeCall()
defer func() {
r.afterCall()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard(timeout)
if err != nil {
return
}
state, err := r.LowLevel.Auth(auth, calcBlockAddress(sector, 3), key, uuid)
if err != nil {
return
}
if state != commands.AuthOk {
return nil, wrapf("can not authenticate")
}
return r.read(calcBlockAddress(sector, 3))
}
// WriteCard writes the data into the card block with IRQ event timeout.
//
// timeout the operation timeout
// auth the authentiction mode.
// sector the sector on the card to write to.
// block the block within the sector to write into.
// data 16 bytes if data to write
// key the key used to authenticate the card - depends on the used auth method.
func (r *Dev) WriteCard(timeout time.Duration, auth byte, sector int, block int, data [16]byte, key Key) (err error) {
r.beforeCall()
defer func() {
r.afterCall()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard(timeout)
if err != nil {
return
}
state, err := r.LowLevel.Auth(auth, calcBlockAddress(sector, 3), key, uuid)
if err != nil {
return
}
if state != commands.AuthOk {
err = wrapf("authentication failed")
return
}
return r.write(calcBlockAddress(sector, block%3), data[:])
}
// WriteSectorTrail writes the sector trail with sector access bits with IRQ event timeout.
//
// timeout operation timeout
// auth authentication mode.
// sector sector to set authentication.
// keyA the key used for AuthA authentication scheme.
// keyB the key used for AuthB authentication scheme.
// access the block access structure.
// key the current key used to authenticate the provided sector.
func (r *Dev) WriteSectorTrail(timeout time.Duration, auth byte, sector int, keyA Key, keyB Key, access *BlocksAccess, key Key) (err error) {
r.beforeCall()
defer func() {
r.afterCall()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard(timeout)
if err != nil {
return
}
state, err := r.LowLevel.Auth(auth, calcBlockAddress(sector, 3), key, uuid)
if err != nil {
return
}
if state != commands.AuthOk {
err = wrapf("failed to authenticate")
return
}
var data [16]byte
copy(data[:], keyA[:])
var accessData [4]byte
if err := access.serialize(accessData[:]); err != nil {
return err
}
copy(data[6:], accessData[:])
copy(data[10:], keyB[:])
return r.write(calcBlockAddress(sector&0xFF, 3), data[:])
}
// MFRC522 SPI Dev private/helper functions
// request the card information. Returns number of blocks available on the card.
func (r *Dev) request() (int, error) {
backBits := -1
if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x07); err != nil {
return backBits, err
}
_, backBits, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, []byte{0x26})
if err != nil {
return -1, err
}
if backBits != 0x10 {
return -1, wrapf("wrong number of bits %d", backBits)
}
return backBits, nil
}
// antiColl performs the collision check for different cards.
func (r *Dev) antiColl() ([]byte, error) {
if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x00); err != nil {
return nil, err
}
backData, _, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, []byte{commands.PICC_ANTICOLL, 0x20}[:])
if err != nil {
return nil, err
}
if len(backData) != 5 {
return nil, wrapf("back data expected 5, actual %d", len(backData))
}
crc := byte(0)
for _, v := range backData[:4] {
crc = crc ^ v
}
if crc != backData[4] {
return nil, wrapf("CRC mismatch, expected %02x actual %02x", crc, backData[4])
}
return backData, nil
}
// selectTag selects the FOB device by device UUID.
func (r *Dev) selectTag(serial []byte) (byte, error) {
dataBuf := make([]byte, len(serial)+2)
dataBuf[0] = commands.PICC_SElECTTAG
dataBuf[1] = 0x70
copy(dataBuf[2:], serial)
crc, err := r.LowLevel.CRC(dataBuf)
if err != nil {
return 0, err
}
dataBuf = append(dataBuf, crc[0], crc[1])
backData, backLen, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, dataBuf)
if err != nil {
return 0, err
}
var blocks byte
if backLen == 0x18 {
blocks = backData[0]
} else {
blocks = 0
}
return blocks, nil
}
// readBlock reads the block from the card.
//
// sector - card sector to read from
// block - the block within the sector (0-3 tor Mifare 4K)
func (r *Dev) readBlock(sector int, block int) ([]byte, error) {
return r.read(calcBlockAddress(sector, block%3))
}
// selectCard selects the card after the IRQ event was received.
func (r *Dev) selectCard(timeout time.Duration) ([]byte, error) {
if err := r.LowLevel.WaitForEdge(timeout); err != nil {
return nil, err
}
if err := r.LowLevel.Init(); err != nil {
return nil, err
}
if _, err := r.request(); err != nil {
return nil, err
}
uuid, err := r.antiColl()
if err != nil {
return nil, err
}
if _, err := r.selectTag(uuid); err != nil {
return nil, err
}
return uuid, nil
}
// write writes the data block into the card at given block address.
//
// blockAddr - the calculated block address
// data - the sector data bytes
func (r *Dev) write(blockAddr byte, data []byte) error {
read, backLen, err := r.preAccess(blockAddr, commands.PICC_WRITE)
if err != nil || backLen != 4 {
return err
}
if read[0]&0x0F != 0x0A {
return wrapf("can't authorize write")
}
var newData [18]byte
copy(newData[:], data[:16])
crc, err := r.LowLevel.CRC(newData[:16])
if err != nil {
return err
}
newData[16] = crc[0]
newData[17] = crc[1]
read, backLen, err = r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, newData[:])
if err != nil {
return err
}
if backLen != 4 || read[0]&0x0F != 0x0A {
err = wrapf("can't write data")
}
return nil
}
// preAccess calculates CRC of the block address to be accessed and sends it to the device for verification.
//
// blockAddr - the block address to access.
// cmd - command code to perform on the given block,
func (r *Dev) preAccess(blockAddr byte, cmd byte) ([]byte, int, error) {
send := make([]byte, 4)
send[0] = cmd
send[1] = blockAddr
crc, err := r.LowLevel.CRC(send[:2])
if err != nil {
return nil, -1, err
}
send[2] = crc[0]
send[3] = crc[1]
return r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, send)
}
// read reads the block
//
// blockAddr the address to read from the card.
func (r *Dev) read(blockAddr byte) ([]byte, error) {
data, _, err := r.preAccess(blockAddr, commands.PICC_READ)
if err != nil {
return nil, err
}
if len(data) != 16 {
return nil, wrapf("expected 16 bytes, actual %d", len(data))
}
return data, nil
}
func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("mfrc522: "+format, a...)
}
var _ conn.Resource = &Dev{}