mirror of https://github.com/periph/devices
Initial support for the Mifare MFRC522 RFID Reader (#220)
Signed-off-by: Eugene Dzhurinsky <jdevelop@gmail.com>pull/1/head
parent
1f328237e7
commit
ddf7ca2519
@ -0,0 +1,30 @@
|
||||
// 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 commands
|
||||
|
||||
// Operation constants
|
||||
const (
|
||||
PCD_IDLE = 0x00
|
||||
PCD_AUTHENT = 0x0E
|
||||
PCD_RECEIVE = 0x08
|
||||
PCD_TRANSMIT = 0x04
|
||||
PCD_TRANSCEIVE = 0x0C
|
||||
PCD_RESETPHASE = 0x0F
|
||||
PCD_CALCCRC = 0x03
|
||||
|
||||
PICC_REQIDL = 0x26
|
||||
PICC_REQALL = 0x52
|
||||
PICC_ANTICOLL = 0x93
|
||||
PICC_SElECTTAG = 0x93
|
||||
PICC_AUTHENT1A = 0x60
|
||||
PICC_AUTHENT1B = 0x61
|
||||
PICC_READ = 0x30
|
||||
PICC_WRITE = 0xA0
|
||||
PICC_DECREMENT = 0xC0
|
||||
PICC_INCREMENT = 0xC1
|
||||
PICC_RESTORE = 0xC2
|
||||
PICC_TRANSFER = 0xB0
|
||||
PICC_HALT = 0x50
|
||||
)
|
||||
@ -0,0 +1,61 @@
|
||||
// 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 commands
|
||||
|
||||
// Command register constants
|
||||
const (
|
||||
CommandReg = 0x01
|
||||
CommIEnReg = 0x02
|
||||
DivlEnReg = 0x03
|
||||
CommIrqReg = 0x04
|
||||
DivIrqReg = 0x05
|
||||
ErrorReg = 0x06
|
||||
Status1Reg = 0x07
|
||||
Status2Reg = 0x08
|
||||
FIFODataReg = 0x09
|
||||
FIFOLevelReg = 0x0A
|
||||
WaterLevelReg = 0x0B
|
||||
ControlReg = 0x0C
|
||||
BitFramingReg = 0x0D
|
||||
CollReg = 0x0E
|
||||
|
||||
ModeReg = 0x11
|
||||
TxModeReg = 0x12
|
||||
RxModeReg = 0x13
|
||||
TxControlReg = 0x14
|
||||
TxAutoReg = 0x15
|
||||
TxSelReg = 0x16
|
||||
RxSelReg = 0x17
|
||||
RxThresholdReg = 0x18
|
||||
DemodReg = 0x19
|
||||
MifareReg = 0x1C
|
||||
SerialSpeedReg = 0x1F
|
||||
|
||||
CRCResultRegM = 0x21
|
||||
CRCResultRegL = 0x22
|
||||
ModWidthReg = 0x24
|
||||
RFCfgReg = 0x26
|
||||
GsNReg = 0x27
|
||||
CWGsPReg = 0x28
|
||||
ModGsPReg = 0x29
|
||||
TModeReg = 0x2A
|
||||
TPrescalerReg = 0x2B
|
||||
TReloadRegH = 0x2C
|
||||
TReloadRegL = 0x2D
|
||||
TCounterValueRegH = 0x2E
|
||||
TCounterValueRegL = 0x2F
|
||||
|
||||
TestSel1Reg = 0x31
|
||||
TestSel2Reg = 0x32
|
||||
TestPinEnReg = 0x33
|
||||
TestPinValueReg = 0x34
|
||||
TestBusReg = 0x35
|
||||
AutoTestReg = 0x36
|
||||
VersionReg = 0x37
|
||||
AnalogTestReg = 0x38
|
||||
TestDAC1Reg = 0x39
|
||||
TestDAC2Reg = 0x3A
|
||||
TestADCReg = 0x3B
|
||||
)
|
||||
@ -0,0 +1,757 @@
|
||||
// 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"
|
||||
"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"
|
||||
)
|
||||
|
||||
// BlockAccess defines the block access bits.
|
||||
type BlockAccess byte
|
||||
|
||||
// SectorTrailerAccess defines the sector trailing block access bits.
|
||||
type SectorTrailerAccess byte
|
||||
|
||||
// Access bits.
|
||||
const (
|
||||
AnyKeyRWID BlockAccess = iota
|
||||
RAB_WN_IN_DN = 0x02 // Read (A|B), Write (None), Increment (None), Decrement(None)
|
||||
RAB_WB_IN_DN = 0x04
|
||||
RAB_WB_IB_DAB = 0x06
|
||||
RAB_WN_IN_DAB = 0x01
|
||||
RB_WB_IN_DN = 0x03
|
||||
RB_WN_IN_DN = 0x05
|
||||
RN_WN_IN_DN = 0x07
|
||||
|
||||
KeyA_RN_WA_BITS_RA_WN_KeyB_RA_WA SectorTrailerAccess = iota
|
||||
KeyA_RN_WN_BITS_RA_WN_KeyB_RA_WN = 0x02
|
||||
KeyA_RN_WB_BITS_RAB_WN_KeyB_RN_WB = 0x04
|
||||
KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN = 0x06
|
||||
KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA = 0x01
|
||||
KeyA_RN_WB_BITS_RAB_WB_KeyB_RN_WB = 0x03
|
||||
KeyA_RN_WN_BITS_RAB_WB_KeyB_RN_WN = 0x05
|
||||
KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN_EXTRA = 0x07
|
||||
)
|
||||
|
||||
// AuthStatus indicates the authentication response, could be one of AuthOk, AuthReadFailure or AuthFailure
|
||||
type AuthStatus byte
|
||||
|
||||
// Card authentication status enum.
|
||||
const (
|
||||
AuthOk AuthStatus = iota
|
||||
AuthReadFailure
|
||||
AuthFailure
|
||||
)
|
||||
|
||||
// BlocksAccess defines the access structure for first 3 blocks of the sector and the access bits for the
|
||||
// sector trail.
|
||||
type BlocksAccess struct {
|
||||
B0, B1, B2 BlockAccess
|
||||
B3 SectorTrailerAccess
|
||||
}
|
||||
|
||||
// DefaultKey provides the default bytes for card authentication for method B.
|
||||
var DefaultKey = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||
|
||||
// Dev is an handle to an MFRC522 RFID reader.
|
||||
type Dev struct {
|
||||
resetPin gpio.PinOut
|
||||
irqPin gpio.PinIn
|
||||
operationTimeout time.Duration
|
||||
spiDev spi.Conn
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MFRC522 SPI Dev public API
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 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) (*Dev, error) {
|
||||
|
||||
if resetPin == nil {
|
||||
return nil, wrapf("reset pin is not set")
|
||||
}
|
||||
if irqPin == nil {
|
||||
return nil, wrapf("IRQ pin is not set")
|
||||
}
|
||||
|
||||
spiDev, err := spiPort.Connect(10000000, spi.Mode0, 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dev := &Dev{
|
||||
spiDev: spiDev,
|
||||
operationTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
if err := resetPin.Out(gpio.High); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dev.resetPin = resetPin
|
||||
|
||||
if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dev.irqPin = irqPin
|
||||
|
||||
if err := dev.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dev, nil
|
||||
}
|
||||
|
||||
// SetOperationtimeout updates the device timeout for card operations.
|
||||
// Effectively that sets the maximum time the RFID device will wait for IRQ from the proximity card detection.
|
||||
//
|
||||
// timeout the duration to wait for IRQ strobe.
|
||||
func (r *Dev) SetOperationtimeout(timeout time.Duration) {
|
||||
r.operationTimeout = timeout
|
||||
}
|
||||
|
||||
// Init initializes the RFID chip.
|
||||
func (r *Dev) Init() error {
|
||||
if err := r.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.writeCommandSequence(sequenceCommands.init); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.SetAntenna(true)
|
||||
}
|
||||
|
||||
// Reset resets the RFID chip to initial state.
|
||||
func (r *Dev) Reset() error {
|
||||
return r.devWrite(commands.CommandReg, commands.PCD_RESETPHASE)
|
||||
}
|
||||
|
||||
// Halt soft-stops the chip - PowerDown bit set, command IDLE
|
||||
func (r *Dev) Halt() error {
|
||||
return r.devWrite(commands.CommandReg, 16)
|
||||
}
|
||||
|
||||
// SetAntenna configures the antenna state, on/off.
|
||||
func (r *Dev) SetAntenna(state bool) error {
|
||||
if state {
|
||||
current, err := r.devRead(commands.TxControlReg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current&0x03 != 0 {
|
||||
return wrapf("can not set the bitmask for antenna")
|
||||
}
|
||||
return r.setBitmask(commands.TxControlReg, 0x03)
|
||||
}
|
||||
return r.clearBitmask(commands.TxControlReg, 0x03)
|
||||
}
|
||||
|
||||
// CardWrite the low-level interface to write some raw commands to the card.
|
||||
//
|
||||
// command - the command register
|
||||
// data - the data to write out to the card using the authenticated sector.
|
||||
func (r *Dev) CardWrite(command byte, data []byte) ([]byte, int, error) {
|
||||
var backData []byte
|
||||
backLength := -1
|
||||
irqEn := byte(0x00)
|
||||
irqWait := byte(0x00)
|
||||
|
||||
switch command {
|
||||
case commands.PCD_AUTHENT:
|
||||
irqEn = 0x12
|
||||
irqWait = 0x10
|
||||
case commands.PCD_TRANSCEIVE:
|
||||
irqEn = 0x77
|
||||
irqWait = 0x30
|
||||
}
|
||||
|
||||
r.devWrite(commands.CommIEnReg, irqEn|0x80)
|
||||
r.clearBitmask(commands.CommIrqReg, 0x80)
|
||||
r.setBitmask(commands.FIFOLevelReg, 0x80)
|
||||
r.devWrite(commands.CommandReg, commands.PCD_IDLE)
|
||||
|
||||
for _, v := range data {
|
||||
r.devWrite(commands.FIFODataReg, v)
|
||||
}
|
||||
|
||||
r.devWrite(commands.CommandReg, command)
|
||||
|
||||
if command == commands.PCD_TRANSCEIVE {
|
||||
r.setBitmask(commands.BitFramingReg, 0x80)
|
||||
}
|
||||
|
||||
i := 2000
|
||||
n := byte(0)
|
||||
|
||||
for ; i > 0; i-- {
|
||||
n, err := r.devRead(commands.CommIrqReg)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if n&(irqWait|1) != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r.clearBitmask(commands.BitFramingReg, 0x80)
|
||||
|
||||
if i == 0 {
|
||||
return nil, -1, wrapf("can't read data after 2000 loops")
|
||||
}
|
||||
|
||||
if d, err := r.devRead(commands.ErrorReg); err != nil || d&0x1B != 0 {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
if n&irqEn&0x01 == 1 {
|
||||
return nil, -1, wrapf("IRQ error")
|
||||
}
|
||||
|
||||
if command == commands.PCD_TRANSCEIVE {
|
||||
n, err := r.devRead(commands.FIFOLevelReg)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
lastBits, err := r.devRead(commands.ControlReg)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
lastBits = lastBits & 0x07
|
||||
if lastBits != 0 {
|
||||
backLength = (int(n)-1)*8 + int(lastBits)
|
||||
} else {
|
||||
backLength = int(n) * 8
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
n = 1
|
||||
}
|
||||
|
||||
if n > 16 {
|
||||
n = 16
|
||||
}
|
||||
|
||||
backData = make([]byte, n)
|
||||
for i := byte(0); i < n; i++ {
|
||||
byteVal, err := r.devRead(commands.FIFODataReg)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
backData[i] = byteVal
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return backData, backLength, nil
|
||||
}
|
||||
|
||||
// Request the card information. Returns number of blocks available on the card.
|
||||
func (r *Dev) Request() (int, error) {
|
||||
backBits := -1
|
||||
if err := r.devWrite(commands.BitFramingReg, 0x07); err != nil {
|
||||
return backBits, err
|
||||
}
|
||||
_, backBits, err := r.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
|
||||
}
|
||||
|
||||
// Wait wait for IRQ to strobe on the IRQ pin when the card is detected.
|
||||
func (r *Dev) Wait() error {
|
||||
irqChannel := make(chan bool)
|
||||
go func() {
|
||||
irqChannel <- r.irqPin.WaitForEdge(r.operationTimeout)
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
close(irqChannel)
|
||||
}()
|
||||
|
||||
if err := r.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.writeCommandSequence(sequenceCommands.waitInit); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
if err := r.writeCommandSequence(sequenceCommands.waitLoop); err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case irqResult := <-irqChannel:
|
||||
if !irqResult {
|
||||
return wrapf("timeout waitinf for IRQ edge: %v", r.operationTimeout)
|
||||
}
|
||||
return nil
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AntiColl performs the collision check for different cards.
|
||||
func (r *Dev) AntiColl() ([]byte, error) {
|
||||
|
||||
if err := r.devWrite(commands.BitFramingReg, 0x00); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backData, _, err := r.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
|
||||
}
|
||||
|
||||
// CRC calculates the CRC of the data using the card chip.
|
||||
func (r *Dev) CRC(inData []byte) ([]byte, error) {
|
||||
if err := r.clearBitmask(commands.DivIrqReg, 0x04); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.setBitmask(commands.FIFOLevelReg, 0x80); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range inData {
|
||||
if err := r.devWrite(commands.FIFODataReg, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := r.devWrite(commands.CommandReg, commands.PCD_CALCCRC); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := byte(0xFF); i > 0; i-- {
|
||||
n, err := r.devRead(commands.DivIrqReg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n&0x04 > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
lsb, err := r.devRead(commands.CRCResultRegL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msb, err := r.devRead(commands.CRCResultRegM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte{lsb, msb}, 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.CRC(dataBuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dataBuf = append(dataBuf, crc[0], crc[1])
|
||||
backData, backLen, err := r.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
|
||||
}
|
||||
|
||||
// StopCrypto stops the crypto chip.
|
||||
func (r *Dev) StopCrypto() error {
|
||||
return r.clearBitmask(commands.Status2Reg, 0x08)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// WriteBlock writes the data into the card block.
|
||||
//
|
||||
// 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) WriteBlock(auth byte, sector int, block int, data [16]byte, key []byte) (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = r.StopCrypto()
|
||||
}
|
||||
}()
|
||||
uuid, err := r.selectCard()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
state, err := r.Auth(auth, sector, 3, key, uuid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if state != AuthOk {
|
||||
err = wrapf("authentication failed")
|
||||
return
|
||||
}
|
||||
|
||||
return r.write(calcBlockAddress(sector, block%3), data[:])
|
||||
}
|
||||
|
||||
/*
|
||||
ReadSectorTrail reads the sector trail (the last sector that contains the sector access bits)
|
||||
sector - the sector number to read the data from.
|
||||
*/
|
||||
func (r *Dev) ReadSectorTrail(sector int) ([]byte, error) {
|
||||
return r.read(calcBlockAddress(sector&0xFF, 3))
|
||||
}
|
||||
|
||||
// WriteSectorTrail writes the sector trait with sector access bits.
|
||||
//
|
||||
// auth - authentication mode.
|
||||
// sector - sector to set authentication.
|
||||
// keyA - the key used for AuthA authentication scheme.
|
||||
// keyB - the key used for AuthB authentication schemd.
|
||||
// access - the block access structure.
|
||||
// key - the current key used to authenticate the provided sector.
|
||||
func (r *Dev) WriteSectorTrail(auth byte, sector int, keyA [6]byte, keyB [6]byte, access *BlocksAccess, key []byte) (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = r.StopCrypto()
|
||||
}
|
||||
}()
|
||||
uuid, err := r.selectCard()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
state, err := r.Auth(auth, sector, 3, key, uuid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if state != AuthOk {
|
||||
err = wrapf("failed to authenticate")
|
||||
return
|
||||
}
|
||||
|
||||
data := make([]byte, 16)
|
||||
copy(data, keyA[:])
|
||||
accessData := CalculateBlockAccess(access)
|
||||
copy(data[6:], accessData[:4])
|
||||
copy(data[10:], keyB[:])
|
||||
return r.write(calcBlockAddress(sector&0xFF, 3), data)
|
||||
}
|
||||
|
||||
// Auth authenticate the card fof the sector/block using the provided data.
|
||||
//
|
||||
// mode - the authentication mode.
|
||||
// sector - the sector to authenticate on.
|
||||
// block - the block within sector to authenticate.
|
||||
// sectorKey - the key to be used for accessing the sector data.
|
||||
// serial - the serial of the card.
|
||||
func (r *Dev) Auth(mode byte, sector, block int, sectorKey []byte, serial []byte) (AuthStatus, error) {
|
||||
return r.auth(mode, calcBlockAddress(sector, block), sectorKey, serial)
|
||||
}
|
||||
|
||||
// ReadCard reads the card sector/block.
|
||||
//
|
||||
// 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(auth byte, sector int, block int, key []byte) (data []byte, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = r.StopCrypto()
|
||||
}
|
||||
}()
|
||||
uuid, err := r.selectCard()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
state, err := r.Auth(auth, sector, block, key, uuid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if state != AuthOk {
|
||||
err = wrapf("can not authenticate")
|
||||
return
|
||||
}
|
||||
return r.ReadBlock(sector, block)
|
||||
}
|
||||
|
||||
// ReadAuth - read the card authentication data.
|
||||
//
|
||||
// sector - the sector to authenticate on.
|
||||
// key - the key to be used for accessing the sector data.
|
||||
func (r *Dev) ReadAuth(auth byte, sector int, key []byte) (data []byte, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = r.StopCrypto()
|
||||
}
|
||||
}()
|
||||
uuid, err := r.selectCard()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
state, err := r.Auth(auth, sector, 3, key, uuid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if state != AuthOk {
|
||||
return nil, wrapf("can not authenticate")
|
||||
}
|
||||
|
||||
return r.read(calcBlockAddress(sector, 3))
|
||||
}
|
||||
|
||||
// CalculateBlockAccess calculates the block access.
|
||||
func CalculateBlockAccess(ba *BlocksAccess) []byte {
|
||||
res := make([]byte, 4)
|
||||
res[0] = (^ba.getBits(1) & 0x0F) | ((^ba.getBits(2) & 0x0F) << 4)
|
||||
res[1] = (^ba.getBits(3) & 0x0F) | (ba.getBits(1) & 0x0F << 4)
|
||||
res[2] = (ba.getBits(2) & 0x0F) | (ba.getBits(3) & 0x0F << 4)
|
||||
res[3] = res[0] ^ res[1] ^ res[2]
|
||||
return res
|
||||
}
|
||||
|
||||
// ParseBlockAccess parses the given byte array into the block access structure.
|
||||
func ParseBlockAccess(ad []byte) *BlocksAccess {
|
||||
ba := new(BlocksAccess)
|
||||
ba.B0 = BlockAccess(ad[1]&0x10>>4 | ad[2]&0x01<<1 | ad[2]&0x10>>2)
|
||||
ba.B1 = BlockAccess(ad[1]&0x20>>5 | ad[2]&0x02 | ad[2]&0x20>>3)
|
||||
ba.B2 = BlockAccess(ad[1]&0x40>>6 | ad[2]&0x04>>1 | ad[2]&0x40>>4)
|
||||
ba.B3 = SectorTrailerAccess(ad[1]&0x80>>7 | ad[2]&0x08>>2 | ad[2]&0x80>>5)
|
||||
return ba
|
||||
}
|
||||
|
||||
func (r *Dev) String() string {
|
||||
return fmt.Sprintf("Mifare MFRC522 [bus: %v, reset pin: %s, irq pin: %s]",
|
||||
r.spiDev, r.resetPin.Name(), r.irqPin.Name())
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// MFRC522 SPI Dev private/helper functions
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func (ba *BlocksAccess) getBits(bitNum uint) byte {
|
||||
shift := bitNum - 1
|
||||
bit := byte(1 << shift)
|
||||
return (byte(ba.B0)&bit)>>shift | ((byte(ba.B1)&bit)>>shift)<<1 | ((byte(ba.B2)&bit)>>shift)<<2 | ((byte(ba.B3)&bit)>>shift)<<3
|
||||
}
|
||||
|
||||
func (r *Dev) writeCommandSequence(commands [][]byte) error {
|
||||
for _, cmdData := range commands {
|
||||
if err := r.devWrite(int(cmdData[0]), cmdData[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Dev) selectCard() ([]byte, error) {
|
||||
if err := r.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.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
|
||||
}
|
||||
|
||||
func calcBlockAddress(sector int, block int) byte {
|
||||
return byte(sector*4 + block)
|
||||
}
|
||||
|
||||
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.CRC(newData[:16])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newData[16] = crc[0]
|
||||
newData[17] = crc[1]
|
||||
read, backLen, err = r.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
|
||||
}
|
||||
|
||||
func (r *Dev) auth(mode byte, blockAddress byte, sectorKey []byte, serial []byte) (AuthStatus, error) {
|
||||
buffer := make([]byte, 2)
|
||||
buffer[0] = mode
|
||||
buffer[1] = blockAddress
|
||||
buffer = append(buffer, sectorKey...)
|
||||
buffer = append(buffer, serial[:4]...)
|
||||
_, _, err := r.CardWrite(commands.PCD_AUTHENT, buffer)
|
||||
if err != nil {
|
||||
return AuthReadFailure, err
|
||||
}
|
||||
if n, err := r.devRead(commands.Status2Reg); err != nil || n&0x08 == 0 {
|
||||
return AuthFailure, err
|
||||
}
|
||||
return AuthOk, nil
|
||||
}
|
||||
|
||||
func (r *Dev) devWrite(address int, data byte) error {
|
||||
newData := []byte{(byte(address) << 1) & 0x7E, data}
|
||||
return r.spiDev.Tx(newData, nil)
|
||||
}
|
||||
|
||||
func (r *Dev) devRead(address int) (byte, error) {
|
||||
data := []byte{((byte(address) << 1) & 0x7E) | 0x80, 0}
|
||||
out := make([]byte, len(data))
|
||||
if err := r.spiDev.Tx(data, out); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return out[1], nil
|
||||
}
|
||||
|
||||
func (r *Dev) setBitmask(address, mask int) error {
|
||||
current, err := r.devRead(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.devWrite(address, current|byte(mask))
|
||||
}
|
||||
|
||||
func (r *Dev) clearBitmask(address, mask int) error {
|
||||
current, err := r.devRead(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.devWrite(address, current&^byte(mask))
|
||||
}
|
||||
|
||||
func (r *Dev) preAccess(blockAddr byte, cmd byte) ([]byte, int, error) {
|
||||
send := make([]byte, 4)
|
||||
send[0] = cmd
|
||||
send[1] = blockAddr
|
||||
|
||||
crc, err := r.CRC(send[:2])
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
send[2] = crc[0]
|
||||
send[3] = crc[1]
|
||||
return r.CardWrite(commands.PCD_TRANSCEIVE, send)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// the command batches for card init and wait loop.
|
||||
var sequenceCommands = struct {
|
||||
init [][]byte
|
||||
waitInit [][]byte
|
||||
waitLoop [][]byte
|
||||
}{
|
||||
init: [][]byte{
|
||||
{commands.TModeReg, 0x8D},
|
||||
{commands.TPrescalerReg, 0x3E},
|
||||
{commands.TReloadRegL, 30},
|
||||
{commands.TReloadRegH, 0},
|
||||
{commands.TxAutoReg, 0x40},
|
||||
{commands.ModeReg, 0x3D},
|
||||
},
|
||||
waitInit: [][]byte{
|
||||
{commands.CommIrqReg, 0x00},
|
||||
{commands.CommIEnReg, 0xA0},
|
||||
},
|
||||
waitLoop: [][]byte{
|
||||
{commands.FIFODataReg, 0x26},
|
||||
{commands.CommandReg, 0x0C},
|
||||
{commands.BitFramingReg, 0x87},
|
||||
},
|
||||
}
|
||||
|
||||
var _ conn.Resource = &Dev{}
|
||||
var _ fmt.Stringer = &Dev{}
|
||||
|
||||
func wrapf(format string, a ...interface{}) error {
|
||||
return fmt.Errorf("mfrc522: "+format, a...)
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBitCalc(t *testing.T) {
|
||||
|
||||
ba := BlocksAccess{
|
||||
B3: KeyA_RN_WB_BITS_RAB_WN_KeyB_RN_WB, // 100
|
||||
B2: RAB_WN_IN_DAB, // 001
|
||||
B1: RB_WN_IN_DN, // 101
|
||||
B0: RAB_WB_IB_DAB, // 110
|
||||
}
|
||||
|
||||
access := CalculateBlockAccess(&ba)
|
||||
|
||||
reader := func(s string) (res byte) {
|
||||
d, err := strconv.ParseUint(s, 2, 8)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res = byte(d & 0xFF)
|
||||
return
|
||||
}
|
||||
|
||||
if reader("0110") != ba.getBits(1) {
|
||||
t.Fatalf("0110 is not equal to %d", ba.getBits(1))
|
||||
}
|
||||
|
||||
expected := []byte{
|
||||
reader("11101001"),
|
||||
reader("01100100"),
|
||||
reader("10110001"),
|
||||
0,
|
||||
}
|
||||
|
||||
expected[3] = expected[0] ^ expected[1] ^ expected[2]
|
||||
|
||||
if !bytes.Equal(expected, access) {
|
||||
t.Fatal("Access is incorrect")
|
||||
}
|
||||
|
||||
parsedAccess := ParseBlockAccess(access)
|
||||
|
||||
if !reflect.DeepEqual(ba, *parsedAccess) {
|
||||
t.Fatal("Parsed access mismatch")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue