mfrc522: make concurrent-safe (#299)

Moved low-level implementation to commands package.
Fixed example_test.
pull/1/head
Vlad Korniev 8 years ago committed by M-A
parent 2a2fa553a3
commit 540371861e

@ -0,0 +1,293 @@
// 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
import (
"fmt"
"time"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/conn/spi"
)
// 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
)
// NewLowLevelSPI 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 NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (*LowLevel, 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(10*physic.MegaHertz, spi.Mode0, 8)
if err != nil {
return nil, err
}
if err := resetPin.Out(gpio.High); err != nil {
return nil, err
}
if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil {
return nil, err
}
dev := &LowLevel{
spiDev: spiDev,
irqPin: irqPin,
resetPin: resetPin,
}
return dev, nil
}
// LowLevel is a low-level handler of a MFRC522 RFID reader.
type LowLevel struct {
resetPin gpio.PinOut
irqPin gpio.PinIn
spiDev spi.Conn
}
// String implements conn.Resource.
func (r *LowLevel) String() string {
return fmt.Sprintf("Mifare MFRC522 [bus: %v, reset pin: %s, irq pin: %s]",
r.spiDev, r.resetPin.Name(), r.irqPin.Name())
}
// DevWrite sends data to a device.
func (r *LowLevel) DevWrite(address int, data byte) error {
newData := []byte{(byte(address) << 1) & 0x7E, data}
return r.spiDev.Tx(newData, nil)
}
// DevRead gets data from a device.
func (r *LowLevel) 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
}
// CRC calculates the CRC of the data using the card chip.
func (r *LowLevel) CRC(inData []byte) ([]byte, error) {
if err := r.ClearBitmask(DivIrqReg, 0x04); err != nil {
return nil, err
}
if err := r.SetBitmask(FIFOLevelReg, 0x80); err != nil {
return nil, err
}
for _, v := range inData {
if err := r.DevWrite(FIFODataReg, v); err != nil {
return nil, err
}
}
if err := r.DevWrite(CommandReg, PCD_CALCCRC); err != nil {
return nil, err
}
for i := byte(0xFF); i > 0; i-- {
n, err := r.DevRead(DivIrqReg)
if err != nil {
return nil, err
}
if n&0x04 > 0 {
break
}
}
lsb, err := r.DevRead(CRCResultRegL)
if err != nil {
return nil, err
}
msb, err := r.DevRead(CRCResultRegM)
if err != nil {
return nil, err
}
return []byte{lsb, msb}, nil
}
// SetBitmask sets register bit.
func (r *LowLevel) SetBitmask(address, mask int) error {
current, err := r.DevRead(address)
if err != nil {
return err
}
return r.DevWrite(address, current|byte(mask))
}
// ClearBitmask clears register bit.
func (r *LowLevel) ClearBitmask(address, mask int) error {
current, err := r.DevRead(address)
if err != nil {
return err
}
return r.DevWrite(address, current&^byte(mask))
}
// StopCrypto stops the crypto chip.
func (r *LowLevel) StopCrypto() error {
return r.ClearBitmask(Status2Reg, 0x08)
}
// WaitForEdge waits for an IRQ pin to strobe.
func (r *LowLevel) WaitForEdge(timeout time.Duration) bool {
return r.irqPin.WaitForEdge(timeout)
}
// 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 *LowLevel) Auth(mode byte, blockAddress byte, sectorKey [6]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(PCD_AUTHENT, buffer)
if err != nil {
return AuthReadFailure, err
}
if n, err := r.DevRead(Status2Reg); err != nil || n&0x08 == 0 {
return AuthFailure, err
}
return AuthOk, nil
}
// 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 *LowLevel) CardWrite(command byte, data []byte) ([]byte, int, error) {
var backData []byte
backLength := -1
irqEn := byte(0x00)
irqWait := byte(0x00)
switch command {
case PCD_AUTHENT:
irqEn = 0x12
irqWait = 0x10
case PCD_TRANSCEIVE:
irqEn = 0x77
irqWait = 0x30
}
if err := r.DevWrite(CommIEnReg, irqEn|0x80); err != nil {
return nil, -1, err
}
if err := r.ClearBitmask(CommIrqReg, 0x80); err != nil {
return nil, -1, err
}
if err := r.SetBitmask(FIFOLevelReg, 0x80); err != nil {
return nil, -1, err
}
if err := r.DevWrite(CommandReg, PCD_IDLE); err != nil {
return nil, -1, err
}
for _, v := range data {
if err := r.DevWrite(FIFODataReg, v); err != nil {
return nil, -1, err
}
}
if err := r.DevWrite(CommandReg, command); err != nil {
return nil, -1, err
}
if command == PCD_TRANSCEIVE {
if err := r.SetBitmask(BitFramingReg, 0x80); err != nil {
return nil, -1, err
}
}
i := 2000
n := byte(0)
for ; i > 0; i-- {
n, err := r.DevRead(CommIrqReg)
if err != nil {
return nil, -1, err
}
if n&(irqWait|1) != 0 {
break
}
}
if err := r.ClearBitmask(BitFramingReg, 0x80); err != nil {
return nil, -1, err
}
if i == 0 {
return nil, -1, wrapf("can't read data after 2000 loops")
}
if d, err := r.DevRead(ErrorReg); err != nil || d&0x1B != 0 {
return nil, -1, err
}
if n&irqEn&0x01 == 1 {
return nil, -1, wrapf("IRQ error")
}
if command == PCD_TRANSCEIVE {
n, err := r.DevRead(FIFOLevelReg)
if err != nil {
return nil, -1, err
}
lastBits, err := r.DevRead(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(FIFODataReg)
if err != nil {
return nil, -1, err
}
backData[i] = byteVal
}
}
return backData, backLength, nil
}
func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("mfrc522: "+format, a...)
}

@ -0,0 +1,104 @@
// 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_test
import (
"encoding/hex"
"log"
"reflect"
"time"
"periph.io/x/periph/conn/spi/spireg"
"periph.io/x/periph/experimental/devices/mfrc522"
"periph.io/x/periph/experimental/devices/mfrc522/commands"
"periph.io/x/periph/host"
"periph.io/x/periph/host/rpi"
)
func Example() {
// Make sure periph is initialized.
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
// Using SPI as an example. See package ./spi/spireg for more details.
p, err := spireg.Open("")
if err != nil {
log.Fatal(err)
}
defer p.Close()
rfid, err := mfrc522.NewSPI(p, rpi.P1_13, rpi.P1_11)
if err != nil {
log.Fatal(err)
}
// Idling device on exit.
defer rfid.Halt()
// Setting the antenna signal strength.
rfid.SetAntennaGain(5)
// Converting access key.
// This value corresponds to first pi "numbers": 3 14 15 92 65 35.
hexKey, _ := hex.DecodeString("030e0f5c4123")
var key [6]byte
copy(key[:], hexKey)
// Converting expected data.
// This value corresponds to string "@~>f=Um[X{LRwA3}".
expected, _ := hex.DecodeString("407e3e663d556d5b587b4c527741337d")
timedOut := false
cb := make(chan []byte)
timer := time.NewTimer(10 * time.Second)
// Stopping timer, flagging reader thread as timed out
defer func() {
timer.Stop()
timedOut = true
close(cb)
}()
go func() {
log.Printf("Started %s", rfid.String())
for {
// Trying to read data from sector 1 block 0
data, err := rfid.ReadCard(byte(commands.PICC_AUTHENT1B), 1, 0, key)
// If main thread timed out just exiting.
if timedOut {
return
}
// Some devices tend to send wrong data while RFID chip is already detected
// but still "too far" from a receiver.
// Especially some cheap CN clones which you can find on GearBest, AliExpress, etc.
// This will suppress such errors.
if err != nil {
continue
}
cb <- data
}
}()
for {
select {
case <-timer.C:
log.Fatal("Didn't receive device data")
return
case data := <-cb:
if !reflect.DeepEqual(data, expected) {
log.Fatal("Received data is incorrect")
} else {
log.Println("Received data is correct")
}
return
}
}
}

@ -16,7 +16,6 @@ import (
"periph.io/x/periph/conn"
"periph.io/x/periph/conn/gpio"
"periph.io/x/periph/conn/physic"
"periph.io/x/periph/conn/spi"
"periph.io/x/periph/experimental/devices/mfrc522/commands"
)
@ -48,17 +47,6 @@ const (
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 {
@ -99,31 +87,18 @@ var DefaultKey = [...]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
// 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(10*physic.MegaHertz, spi.Mode0, 8)
raw, err := commands.NewLowLevelSPI(spiPort, resetPin, irqPin)
if err != nil {
return nil, err
}
if err := resetPin.Out(gpio.High); err != nil {
return nil, err
}
if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil {
return nil, err
}
dev := &Dev{
spiDev: spiDev,
operationTimeout: 30 * time.Second,
irqPin: irqPin,
resetPin: resetPin,
antennaGain: 4,
stop: make(chan struct{}, 1),
LowLevel: raw,
}
if err := dev.Init(); err != nil {
if err := dev.init(); err != nil {
return nil, err
}
return dev, nil
@ -131,23 +106,24 @@ func NewSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (*Dev, er
// Dev is an handle to an MFRC522 RFID reader.
type Dev struct {
resetPin gpio.PinOut
irqPin gpio.PinIn
operationTimeout time.Duration
spiDev spi.Conn
stop chan struct{}
LowLevel *commands.LowLevel
aMu sync.Mutex
antennaGain int
stop chan struct{}
oMu sync.Mutex
operationTimeout time.Duration
antennaGain int
mu sync.Mutex
isWaiting bool
xMu sync.Mutex
isAccessing bool
}
// String implements conn.Resource.
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())
return r.LowLevel.String()
}
// Halt implements conn.Resource.
@ -166,7 +142,7 @@ func (r *Dev) Halt() error {
r.stop <- struct{}{}
}
return r.devWrite(commands.CommandReg, 16)
return r.LowLevel.DevWrite(commands.CommandReg, 16)
}
// SetOperationTimeout updates the device timeout for card operations.
@ -174,185 +150,234 @@ func (r *Dev) Halt() error {
// 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.
// timeout the duration to wait for IRQ strobe.
func (r *Dev) SetOperationTimeout(timeout time.Duration) {
r.oMu.Lock()
defer r.oMu.Unlock()
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
}
r.aMu.Lock()
gain := byte(r.antennaGain) << 4
r.aMu.Unlock()
if err := r.devWrite(int(commands.RFCfgReg), gain); err != nil {
return err
// SetAntennaGain configures antenna signal strength.
//
// gain - signal strength from 0 to 7.
func (r *Dev) SetAntennaGain(gain int) error {
if gain < 0 || gain > 7 {
return wrapf("gain must be in [0..7] interval")
}
return r.SetAntenna(true)
r.oMu.Lock()
defer r.oMu.Unlock()
r.antennaGain = gain
return nil
}
// Reset resets the RFID chip to initial state.
func (r *Dev) Reset() error {
return r.devWrite(commands.CommandReg, commands.PCD_RESETPHASE)
}
// 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 [6]byte) (data []byte, err error) {
if err = r.accessStarted(); err != nil {
return
}
// 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")
defer func() {
r.accessFinished()
if err == nil {
err = r.LowLevel.StopCrypto()
}
return r.setBitmask(commands.TxControlReg, 0x03)
}()
uuid, err := r.selectCard()
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.clearBitmask(commands.TxControlReg, 0x03)
return r.readBlock(sector, block)
}
// SetAntennaGain configures antenna signal strength.
// ReadAuth - read the card authentication data.
//
// gain - signal strength from 0 to 7.
func (r *Dev) SetAntennaGain(gain int) {
r.aMu.Lock()
defer r.aMu.Unlock()
// 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 [6]byte) (data []byte, err error) {
if err = r.accessStarted(); err != nil {
return
}
defer func() {
r.accessFinished()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard()
if err != nil {
return
}
if 0 <= gain && gain <= 7 {
r.antennaGain = gain
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))
}
// CardWrite the low-level interface to write some raw commands to the card.
// WriteCard writes the data into the card block.
//
// 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
}
if err := r.devWrite(commands.CommIEnReg, irqEn|0x80); err != nil {
return nil, -1, err
// 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(auth byte, sector int, block int, data [16]byte, key [6]byte) (err error) {
if err = r.accessStarted(); err != nil {
return
}
if err := r.clearBitmask(commands.CommIrqReg, 0x80); err != nil {
return nil, -1, err
defer func() {
r.accessFinished()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard()
if err != nil {
return
}
if err := r.setBitmask(commands.FIFOLevelReg, 0x80); err != nil {
return nil, -1, err
state, err := r.LowLevel.Auth(auth, calcBlockAddress(sector, 3), key, uuid)
if err != nil {
return
}
if err := r.devWrite(commands.CommandReg, commands.PCD_IDLE); err != nil {
return nil, -1, err
if state != commands.AuthOk {
err = wrapf("authentication failed")
return
}
for _, v := range data {
if err := r.devWrite(commands.FIFODataReg, v); err != nil {
return nil, -1, err
}
}
return r.write(calcBlockAddress(sector, block%3), data[:])
}
if err := r.devWrite(commands.CommandReg, command); err != nil {
return nil, -1, err
// WriteSectorTrail writes the sector trail 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 scheme.
// 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 [6]byte) (err error) {
if err = r.accessStarted(); err != nil {
return
}
if command == commands.PCD_TRANSCEIVE {
if err := r.setBitmask(commands.BitFramingReg, 0x80); err != nil {
return nil, -1, err
defer func() {
r.accessFinished()
if err == nil {
err = r.LowLevel.StopCrypto()
}
}()
uuid, err := r.selectCard()
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
}
i := 2000
n := byte(0)
var data [16]byte
copy(data[:], keyA[:])
accessData := CalculateBlockAccess(access)
copy(data[6:], accessData[:4])
copy(data[10:], keyB[:])
return r.write(calcBlockAddress(sector&0xFF, 3), data[:])
}
for ; i > 0; i-- {
n, err := r.devRead(commands.CommIrqReg)
if err != nil {
return nil, -1, err
}
if n&(irqWait|1) != 0 {
break
}
}
// MFRC522 SPI Dev private/helper functions
if err := r.clearBitmask(commands.BitFramingReg, 0x80); err != nil {
return nil, -1, err
}
// Checks whether device is already in use.
func (r *Dev) accessStarted() error {
r.xMu.Lock()
defer r.xMu.Unlock()
if i == 0 {
return nil, -1, wrapf("can't read data after 2000 loops")
if r.isAccessing {
return wrapf("concurrent access is forbidden")
}
if d, err := r.devRead(commands.ErrorReg); err != nil || d&0x1B != 0 {
return nil, -1, err
r.isAccessing = true
return nil
}
// Marks device as free.
func (r *Dev) accessFinished() {
r.xMu.Lock()
defer r.xMu.Unlock()
r.isAccessing = false
}
// 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
}
if n&irqEn&0x01 == 1 {
return nil, -1, wrapf("IRQ error")
r.oMu.Lock()
gain := byte(r.antennaGain) << 4
r.oMu.Unlock()
if err := r.LowLevel.DevWrite(int(commands.RFCfgReg), gain); err != nil {
return err
}
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
}
return r.setAntenna(true)
}
if n == 0 {
n = 1
}
// reset resets the RFID chip to initial state.
func (r *Dev) reset() error {
return r.LowLevel.DevWrite(commands.CommandReg, commands.PCD_RESETPHASE)
}
if n > 16 {
n = 16
// setAntenna configures the antenna state, on/off.
func (r *Dev) setAntenna(state bool) error {
if state {
current, err := r.LowLevel.DevRead(commands.TxControlReg)
if err != nil {
return err
}
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
if current&0x03 != 0 {
return wrapf("can not set the bitmask for antenna")
}
return r.LowLevel.SetBitmask(commands.TxControlReg, 0x03)
}
return backData, backLength, nil
return r.LowLevel.ClearBitmask(commands.TxControlReg, 0x03)
}
// Request the card information. Returns number of blocks available on the card.
func (r *Dev) Request() (int, error) {
// 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 {
if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x07); err != nil {
return backBits, err
}
_, backBits, err := r.CardWrite(commands.PCD_TRANSCEIVE, []byte{0x26})
_, backBits, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, []byte{0x26})
if err != nil {
return -1, err
}
@ -362,8 +387,8 @@ func (r *Dev) Request() (int, error) {
return backBits, nil
}
// Wait wait for IRQ to strobe on the IRQ pin when the card is detected.
func (r *Dev) Wait() error {
// wait wait for IRQ to strobe on the IRQ pin when the card is detected.
func (r *Dev) wait() error {
r.mu.Lock()
waitCancelled := false
if r.isWaiting {
@ -377,7 +402,11 @@ func (r *Dev) Wait() error {
irqChannel := make(chan bool)
go func() {
result := r.irqPin.WaitForEdge(r.operationTimeout)
r.oMu.Lock()
timeout := r.operationTimeout
r.oMu.Unlock()
result := r.LowLevel.WaitForEdge(timeout)
r.mu.Lock()
defer r.mu.Unlock()
if waitCancelled {
@ -396,7 +425,7 @@ func (r *Dev) Wait() error {
close(irqChannel)
}()
if err := r.Init(); err != nil {
if err := r.init(); err != nil {
return err
}
if err := r.writeCommandSequence(sequenceCommands.waitInit); err != nil {
@ -421,14 +450,13 @@ func (r *Dev) Wait() error {
}
}
// AntiColl performs the collision check for different cards.
func (r *Dev) AntiColl() ([]byte, error) {
if err := r.devWrite(commands.BitFramingReg, 0x00); err != 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.CardWrite(commands.PCD_TRANSCEIVE, []byte{commands.PICC_ANTICOLL, 0x20}[:])
backData, _, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, []byte{commands.PICC_ANTICOLL, 0x20}[:])
if err != nil {
return nil, err
@ -451,55 +479,18 @@ func (r *Dev) AntiColl() ([]byte, error) {
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) {
// 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)
crc, err := r.LowLevel.CRC(dataBuf)
if err != nil {
return 0, err
}
dataBuf = append(dataBuf, crc[0], crc[1])
backData, backLen, err := r.CardWrite(commands.PCD_TRANSCEIVE, dataBuf)
backData, backLen, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, dataBuf)
if err != nil {
return 0, err
}
@ -514,156 +505,14 @@ func (r *Dev) SelectTag(serial []byte) (byte, error) {
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.
// 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) {
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 [6]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 [6]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
}
var data [16]byte
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 [6]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 [6]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 [6]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))
}
// MFRC522 SPI Dev private/helper functions
func (ba *BlocksAccess) getBits(bitNum uint) byte {
shift := 3 - bitNum
bit := byte(1 << shift)
@ -672,7 +521,7 @@ func (ba *BlocksAccess) getBits(bitNum uint) byte {
func (r *Dev) writeCommandSequence(commands [][]byte) error {
for _, cmdData := range commands {
if err := r.devWrite(int(cmdData[0]), cmdData[1]); err != nil {
if err := r.LowLevel.DevWrite(int(cmdData[0]), cmdData[1]); err != nil {
return err
}
}
@ -680,20 +529,20 @@ func (r *Dev) writeCommandSequence(commands [][]byte) error {
}
func (r *Dev) selectCard() ([]byte, error) {
if err := r.Wait(); err != nil {
if err := r.wait(); err != nil {
return nil, err
}
if err := r.Init(); err != nil {
if err := r.init(); err != nil {
return nil, err
}
if _, err := r.Request(); err != nil {
if _, err := r.request(); err != nil {
return nil, err
}
uuid, err := r.AntiColl()
uuid, err := r.antiColl()
if err != nil {
return nil, err
}
if _, err := r.SelectTag(uuid); err != nil {
if _, err := r.selectTag(uuid); err != nil {
return nil, err
}
return uuid, nil
@ -713,13 +562,13 @@ func (r *Dev) write(blockAddr byte, data []byte) error {
}
var newData [18]byte
copy(newData[:], data[:16])
crc, err := r.CRC(newData[: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.CardWrite(commands.PCD_TRANSCEIVE, newData[:])
read, backLen, err = r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, newData[:])
if err != nil {
return err
}
@ -729,64 +578,18 @@ func (r *Dev) write(blockAddr byte, data []byte) error {
return nil
}
func (r *Dev) auth(mode byte, blockAddress byte, sectorKey [6]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])
crc, err := r.LowLevel.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)
return r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, send)
}
func (r *Dev) read(blockAddr byte) ([]byte, error) {

Loading…
Cancel
Save