mfrc522: reworked public/private API. (#349)

Fixed the wait for edge block, removed synchronization code.
Added concurrent execution protection via config.
Updated API docs.
pull/1/head
Eugene 8 years ago committed by M-A
parent 71c7a218dd
commit a8429b61f9

@ -0,0 +1,106 @@
// 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"
// BlockAccess defines the block access bits.
type BlockAccess byte
// SectorTrailerAccess defines the sector trailing block access bits.
type SectorTrailerAccess byte
// Access bits for the sector data.
const (
AnyKeyRWID BlockAccess = 0x0 // Any key (A or B) can read, write, increment and decrement block.
RAB_WN_IN_DN BlockAccess = 0x02 // Read (A or B), Write (None), Increment (None), Decrement (None)
RAB_WB_IN_DN BlockAccess = 0x04 // Read (A orB), Write (B), Increment (None), Decrement (None)
RAB_WB_IB_DAB BlockAccess = 0x06 // Read (A or B), Write (B), Icrement (B), Decrement (A or B)
RAB_WN_IN_DAB BlockAccess = 0x01 // Read (A or B), Write (None), Increment (None), Decrment (A or B)
RB_WB_IN_DN BlockAccess = 0x03 // Read (B), Write (B), Increment (None), Decrement (None)
RB_WN_IN_DN BlockAccess = 0x05 // Read (B), Write (None), Increment (None), Decrement (None)
RN_WN_IN_DN BlockAccess = 0x07 // Read (None), Write (None), Increment (None), Decrement (None)
)
// Access bits for the sector trail.
// Every trail sector has the options for controlling the access to the trailing sector bits.
// For example :
//
// KeyA_R[Key]_W[Key]_BITS_R[Key]_W[Key]_KeyB_R[Key]_W[Key]
//
// - KeyA
// - could be Read by providing [Key] ( where [Key] could be KeyA or KeyB )
// - could be Written by Providing [Key] ( where [Key] is KeyA or KeyB )
// - access bits for the sector data (see above)
// - could be Read by providing [Key] ( where [Key] could be KeyA or KeyB )
// - could be Written by Providing [Key] ( where [Key] is KeyA or KeyB )
// - KeyB
// - could be Read by providing [Key] ( where [Key] could be KeyA or KeyB )
// - could be Written by Providing [Key] ( where [Key] is KeyA or KeyB )
//
// example:
//
// KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA means
// - KeyA could not be read but could be overwriten if KeyA is provided
// - Access bits could be read and overwritten if KeyA is provided during the card authentication
// - KeyB could be read and overriten if KeyA is provided during the card authentication
// more on the matter: https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf
const (
KeyA_RN_WA_BITS_RA_WN_KeyB_RA_WA SectorTrailerAccess = 0x0
KeyA_RN_WN_BITS_RA_WN_KeyB_RA_WN SectorTrailerAccess = 0x02
KeyA_RN_WB_BITS_RAB_WN_KeyB_RN_WB SectorTrailerAccess = 0x04
KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN SectorTrailerAccess = 0x06
KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA SectorTrailerAccess = 0x01
KeyA_RN_WB_BITS_RAB_WB_KeyB_RN_WB SectorTrailerAccess = 0x03
KeyA_RN_WN_BITS_RAB_WB_KeyB_RN_WN SectorTrailerAccess = 0x05
KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN_EXTRA SectorTrailerAccess = 0x07
)
// 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
}
func (ba *BlocksAccess) String() string {
return fmt.Sprintf("B0: %d, B1: %d, B2: %d, B3: %d", ba.B0, ba.B1, ba.B2, ba.B3)
}
// serialize calculates the block access and stores it into the passed slice, that must be at least 4 bytes wide.
func (ba *BlocksAccess) serialize(dst []byte) error {
if len(dst) != 4 {
return wrapf("serialized array must be of size at least 4")
}
dst[0] = ((^ba.getBits(2) & 0x0F) << 4) | (^ba.getBits(1) & 0x0F)
dst[1] = ((ba.getBits(1) & 0x0F) << 4) | (^ba.getBits(3) & 0x0F)
dst[2] = ((ba.getBits(3) & 0x0F) << 4) | (ba.getBits(2) & 0x0F)
dst[3] = dst[0] ^ dst[1] ^ dst[2]
return nil
}
// Init parses the given byte array into the block access structure.
func (ba *BlocksAccess) Init(ad []byte) {
ba.B0 = BlockAccess(((ad[1] & 0x10) >> 2) | ((ad[2] & 0x01) << 1) | ((ad[2] & 0x10) >> 5))
ba.B1 = BlockAccess(((ad[1] & 0x20) >> 3) | (ad[2] & 0x02) | ((ad[2] & 0x20) >> 5))
ba.B2 = BlockAccess(((ad[1] & 0x40) >> 4) | ((ad[2] & 0x04) >> 1) | ((ad[2] & 0x40) >> 6))
ba.B3 = SectorTrailerAccess(((ad[1] & 0x80) >> 5) | ((ad[2] & 0x08) >> 2) | ((ad[2] & 0x80) >> 7))
}
func (ba *BlocksAccess) getBits(bitNum uint) byte {
shift := 3 - bitNum
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 calcBlockAddress(sector int, block int) byte {
return byte(sector*4 + block)
}
var _ fmt.Stringer = &BlocksAccess{}

@ -13,10 +13,6 @@ import (
"periph.io/x/periph/conn/spi" "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. // Card authentication status enum.
const ( const (
AuthOk AuthStatus = iota AuthOk AuthStatus = iota
@ -24,6 +20,19 @@ const (
AuthFailure AuthFailure
) )
// LowLevel is a low-level handler of a MFRC522 RFID reader.
type LowLevel struct {
resetPin gpio.PinOut
irqPin gpio.PinIn
spiDev spi.Conn
antennaGain int
stop chan struct{}
}
// AuthStatus indicates the authentication response, could be one of AuthOk,
// AuthReadFailure or AuthFailure
type AuthStatus byte
// NewLowLevelSPI creates and initializes the RFID card reader attached to SPI. // NewLowLevelSPI creates and initializes the RFID card reader attached to SPI.
// //
// spiPort - the SPI device to use. // spiPort - the SPI device to use.
@ -33,9 +42,6 @@ func NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (
if resetPin == nil { if resetPin == nil {
return nil, wrapf("reset pin is not set") 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) spiDev, err := spiPort.Connect(10*physic.MegaHertz, spi.Mode0, 8)
if err != nil { if err != nil {
return nil, err return nil, err
@ -43,24 +49,66 @@ func NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (
if err := resetPin.Out(gpio.High); err != nil { if err := resetPin.Out(gpio.High); err != nil {
return nil, err return nil, err
} }
if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil { if irqPin != nil {
return nil, err if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil {
return nil, err
}
} }
dev := &LowLevel{ dev := &LowLevel{
spiDev: spiDev, spiDev: spiDev,
irqPin: irqPin, irqPin: irqPin,
resetPin: resetPin, resetPin: resetPin,
antennaGain: 4,
stop: make(chan struct{}, 1),
} }
return dev, nil return dev, nil
} }
// LowLevel is a low-level handler of a MFRC522 RFID reader. // Reset resets the RFID chip to initial state.
type LowLevel struct { func (r *LowLevel) Reset() error {
resetPin gpio.PinOut return r.DevWrite(CommandReg, PCD_RESETPHASE)
irqPin gpio.PinIn }
spiDev spi.Conn
// SetAntennaGain sets the antenna gain for the driver.
// This method does not update the gain on the device itself.
// A subsequent call to SetAntenna is necessary to have an effect.
func (r *LowLevel) SetAntennaGain(gain int) {
r.antennaGain = gain
}
// Init initializes the RFID chip.
func (r *LowLevel) Init() error {
if err := r.Reset(); err != nil {
return err
}
if err := r.writeCommandSequence(sequenceCommands.init); err != nil {
return err
}
gain := byte(r.antennaGain) << 4
if err := r.DevWrite(int(RFCfgReg), gain); err != nil {
return err
}
return r.SetAntenna(true)
}
// setAntenna configures the antenna state, on/off.
func (r *LowLevel) SetAntenna(state bool) error {
if state {
current, err := r.DevRead(TxControlReg)
if err != nil {
return err
}
if current&0x03 != 0 {
return wrapf("can not set the bitmask for antenna")
}
return r.SetBitmask(TxControlReg, 0x03)
}
return r.ClearBitmask(TxControlReg, 0x03)
} }
// String implements conn.Resource. // String implements conn.Resource.
@ -145,9 +193,37 @@ func (r *LowLevel) StopCrypto() error {
return r.ClearBitmask(Status2Reg, 0x08) return r.ClearBitmask(Status2Reg, 0x08)
} }
// WaitForEdge waits for an IRQ pin to strobe. // WaitForEdge waits for an IRQ pin to strobe. If IRQ pin is not set, then always returns false immediately.
func (r *LowLevel) WaitForEdge(timeout time.Duration) bool { func (r *LowLevel) WaitForEdge(timeout time.Duration) error {
return r.irqPin.WaitForEdge(timeout) irqChannel := make(chan bool)
go func() {
defer close(irqChannel)
irqChannel <- r.irqPin.WaitForEdge(timeout)
}()
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 <-r.stop:
return wrapf("halt")
case irqResult := <-irqChannel:
if !irqResult {
return wrapf("timeout waiting for IRQ edge: %v", timeout)
}
return nil
case <-time.After(100 * time.Millisecond):
// do nothing
}
}
} }
// Auth authenticate the card fof the sector/block using the provided data. // Auth authenticate the card fof the sector/block using the provided data.
@ -288,6 +364,46 @@ func (r *LowLevel) CardWrite(command byte, data []byte) ([]byte, int, error) {
return backData, backLength, nil return backData, backLength, nil
} }
// Halt stops the card and cleans up resources.
func (r *LowLevel) Halt() error {
close(r.stop)
return r.DevWrite(CommandReg, 16)
}
func (r *LowLevel) writeCommandSequence(commands [][]byte) error {
for _, cmdData := range commands {
if err := r.DevWrite(int(cmdData[0]), cmdData[1]); err != nil {
return err
}
}
return nil
}
func wrapf(format string, a ...interface{}) error { func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("mfrc522: "+format, a...) return fmt.Errorf("mfrc522 lowlevel: "+format, a...)
}
// the command batches for card init and wait loop.
var sequenceCommands = struct {
init [][]byte
waitInit [][]byte
waitLoop [][]byte
}{
init: [][]byte{
{TModeReg, 0x8D},
{TPrescalerReg, 0x3E},
{TReloadRegL, 30},
{TReloadRegH, 0},
{TxAutoReg, 0x40},
{ModeReg, 0x3D},
},
waitInit: [][]byte{
{CommIrqReg, 0x00},
{CommIEnReg, 0xA0},
},
waitLoop: [][]byte{
{FIFODataReg, 0x26},
{CommandReg, 0x0C},
{BitFramingReg, 0x87},
},
} }

@ -67,7 +67,7 @@ func Example() {
for { for {
// Trying to read data from sector 1 block 0 // Trying to read data from sector 1 block 0
data, err := rfid.ReadCard(byte(commands.PICC_AUTHENT1B), 1, 0, key) data, err := rfid.ReadCard(10*time.Second, byte(commands.PICC_AUTHENT1B), 1, 0, key)
// If main thread timed out just exiting. // If main thread timed out just exiting.
if timedOut { if timedOut {

@ -20,107 +20,81 @@ import (
"periph.io/x/periph/experimental/devices/mfrc522/commands" "periph.io/x/periph/experimental/devices/mfrc522/commands"
) )
// BlockAccess defines the block access bits. // Dev is an handle to an MFRC522 RFID reader.
type BlockAccess byte type Dev struct {
LowLevel *commands.LowLevel
// SectorTrailerAccess defines the sector trailing block access bits. operationTimeout time.Duration
type SectorTrailerAccess byte beforeCall func()
afterCall func()
// 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
)
// 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
} }
func (ba *BlocksAccess) String() string { // Key is the access key that consists of 6 bytes. There could be two types of keys - keyA and keyB.
return fmt.Sprintf("B0: %d, B1: %d, B2: %d, B3: %d", ba.B0, ba.B1, ba.B2, ba.B3) // 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()
} }
// CalculateBlockAccess calculates the block access. type configF func(*config) *config
func CalculateBlockAccess(ba *BlocksAccess) []byte {
res := make([]byte, 4) // WithTimeout updates the default device-wide configuration timeout.
res[0] = ((^ba.getBits(2) & 0x0F) << 4) | (^ba.getBits(1) & 0x0F) func WithTimeout(timeout time.Duration) configF {
res[1] = ((ba.getBits(1) & 0x0F) << 4) | (^ba.getBits(3) & 0x0F) return func(c *config) *config {
res[2] = ((ba.getBits(3) & 0x0F) << 4) | (ba.getBits(2) & 0x0F) c.defaultTimeout = timeout
res[3] = res[0] ^ res[1] ^ res[2] return c
return res }
} }
// ParseBlockAccess parses the given byte array into the block access structure. // WithSyncsets the synchronization for the entire device, using internal mutex.
func ParseBlockAccess(ad []byte) *BlocksAccess { func WithSync() configF {
return &BlocksAccess{ var mu sync.Mutex
B0: BlockAccess(((ad[1] & 0x10) >> 2) | ((ad[2] & 0x01) << 1) | ((ad[2] & 0x10) >> 5)), return func(c *config) *config {
B1: BlockAccess(((ad[1] & 0x20) >> 3) | (ad[2] & 0x02) | ((ad[2] & 0x20) >> 5)), c.beforeCall = mu.Lock
B2: BlockAccess(((ad[1] & 0x40) >> 4) | ((ad[2] & 0x04) >> 1) | ((ad[2] & 0x40) >> 6)), c.afterCall = mu.Unlock
B3: SectorTrailerAccess(((ad[1] & 0x80) >> 5) | ((ad[2] & 0x08) >> 2) | ((ad[2] & 0x80) >> 7)), return c
} }
} }
// DefaultKey provides the default bytes for card authentication for method B. // noop does nothing
var DefaultKey = [...]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} func noop() {}
// NewSPI creates and initializes the RFID card reader attached to SPI. // NewSPI creates and initializes the RFID card reader attached to SPI.
// //
// spiPort - the SPI device to use. // spiPort the SPI device to use.
// resetPin - reset GPIO pin. // resetPin reset GPIO pin.
// irqPin - irq GPIO pin. // irqPin irq GPIO pin.
func NewSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (*Dev, error) { 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) raw, err := commands.NewLowLevelSPI(spiPort, resetPin, irqPin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := raw.Init(); err != nil {
return nil, err
}
dev := &Dev{ dev := &Dev{
operationTimeout: 30 * time.Second,
antennaGain: 4,
stop: make(chan struct{}, 1),
LowLevel: raw, LowLevel: raw,
} operationTimeout: cfg.defaultTimeout,
if err := dev.init(); err != nil { beforeCall: cfg.beforeCall,
return nil, err afterCall: cfg.afterCall,
} }
return dev, nil return dev, nil
} }
// Dev is an handle to an MFRC522 RFID reader.
type Dev struct {
LowLevel *commands.LowLevel
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. // String implements conn.Resource.
func (r *Dev) String() string { func (r *Dev) String() string {
return r.LowLevel.String() return r.LowLevel.String()
@ -130,65 +104,40 @@ func (r *Dev) String() string {
// //
// It soft-stops the chip - PowerDown bit set, command IDLE // It soft-stops the chip - PowerDown bit set, command IDLE
func (r *Dev) Halt() error { func (r *Dev) Halt() error {
r.mu.Lock() r.beforeCall()
defer r.mu.Unlock() defer r.afterCall()
return r.LowLevel.Halt()
if r.isWaiting {
select {
case <-r.stop:
default:
}
r.stop <- struct{}{}
}
return r.LowLevel.DevWrite(commands.CommandReg, 16)
}
// 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.oMu.Lock()
defer r.oMu.Unlock()
r.operationTimeout = timeout
} }
// SetAntennaGain configures antenna signal strength. // SetAntennaGain configures antenna signal strength.
// //
// gain - signal strength from 0 to 7. // gain signal strength from 0 to 7.
func (r *Dev) SetAntennaGain(gain int) error { func (r *Dev) SetAntennaGain(gain int) error {
r.beforeCall()
defer r.afterCall()
if gain < 0 || gain > 7 { if gain < 0 || gain > 7 {
return wrapf("gain must be in [0..7] interval") return wrapf("gain must be in [0..7] interval")
} }
r.LowLevel.SetAntennaGain(gain)
r.oMu.Lock()
defer r.oMu.Unlock()
r.antennaGain = gain
return nil return nil
} }
// ReadCard reads the card sector/block. // ReadCard reads the card sector/block with IRQ event timeout.
// //
// auth - the authentication mode. // timeout the operation timeout
// sector - the sector to authenticate on. // auth the authentication mode.
// block - the block within sector to authenticate. // sector the sector to authenticate on.
// key - the key to be used for accessing the sector data. // block the block within sector to authenticate.
func (r *Dev) ReadCard(auth byte, sector int, block int, key [6]byte) (data []byte, err error) { // key the key to be used for accessing the sector data.
if err = r.accessStarted(); err != nil { func (r *Dev) ReadCard(timeout time.Duration, auth byte, sector int, block int, key Key) (data []byte, err error) {
return r.beforeCall()
}
defer func() { defer func() {
r.accessFinished() r.afterCall()
if err == nil { if err == nil {
err = r.LowLevel.StopCrypto() err = r.LowLevel.StopCrypto()
} }
}() }()
uuid, err := r.selectCard() uuid, err := r.selectCard(timeout)
if err != nil { if err != nil {
return return
} }
@ -203,22 +152,21 @@ func (r *Dev) ReadCard(auth byte, sector int, block int, key [6]byte) (data []by
return r.readBlock(sector, block) return r.readBlock(sector, block)
} }
// ReadAuth - read the card authentication data. // ReadAuth reads the card authentication data with IRQ event timeout.
// //
// sector - the sector to authenticate on. // timeout the operation timeout
// key - the key to be used for accessing the sector data. // auth authentication type
func (r *Dev) ReadAuth(auth byte, sector int, key [6]byte) (data []byte, err error) { // sector the sector to authenticate on.
if err = r.accessStarted(); err != nil { // key the key to be used for accessing the sector data.
return func (r *Dev) ReadAuth(timeout time.Duration, auth byte, sector int, key Key) (data []byte, err error) {
} r.beforeCall()
defer func() { defer func() {
r.accessFinished() r.afterCall()
if err == nil { if err == nil {
err = r.LowLevel.StopCrypto() err = r.LowLevel.StopCrypto()
} }
}() }()
uuid, err := r.selectCard() uuid, err := r.selectCard(timeout)
if err != nil { if err != nil {
return return
} }
@ -234,25 +182,23 @@ func (r *Dev) ReadAuth(auth byte, sector int, key [6]byte) (data []byte, err err
return r.read(calcBlockAddress(sector, 3)) return r.read(calcBlockAddress(sector, 3))
} }
// WriteCard writes the data into the card block. // WriteCard writes the data into the card block with IRQ event timeout.
// //
// auth - the authentiction mode. // timeout the operation timeout
// sector - the sector on the card to write to. // auth the authentiction mode.
// block - the block within the sector to write into. // sector the sector on the card to write to.
// data - 16 bytes if data to write // block the block within the sector to write into.
// key - the key used to authenticate the card - depends on the used auth method. // data 16 bytes if data to write
func (r *Dev) WriteCard(auth byte, sector int, block int, data [16]byte, key [6]byte) (err error) { // key the key used to authenticate the card - depends on the used auth method.
if err = r.accessStarted(); err != nil { func (r *Dev) WriteCard(timeout time.Duration, auth byte, sector int, block int, data [16]byte, key Key) (err error) {
return r.beforeCall()
}
defer func() { defer func() {
r.accessFinished() r.afterCall()
if err == nil { if err == nil {
err = r.LowLevel.StopCrypto() err = r.LowLevel.StopCrypto()
} }
}() }()
uuid, err := r.selectCard() uuid, err := r.selectCard(timeout)
if err != nil { if err != nil {
return return
} }
@ -268,26 +214,24 @@ func (r *Dev) WriteCard(auth byte, sector int, block int, data [16]byte, key [6]
return r.write(calcBlockAddress(sector, block%3), data[:]) return r.write(calcBlockAddress(sector, block%3), data[:])
} }
// WriteSectorTrail writes the sector trail with sector access bits. // WriteSectorTrail writes the sector trail with sector access bits with IRQ event timeout.
// //
// auth - authentication mode. // timeout operation timeout
// sector - sector to set authentication. // auth authentication mode.
// keyA - the key used for AuthA authentication scheme. // sector sector to set authentication.
// keyB - the key used for AuthB authentication scheme. // keyA the key used for AuthA authentication scheme.
// access - the block access structure. // keyB the key used for AuthB authentication scheme.
// key - the current key used to authenticate the provided sector. // access the block access structure.
func (r *Dev) WriteSectorTrail(auth byte, sector int, keyA [6]byte, keyB [6]byte, access *BlocksAccess, key [6]byte) (err error) { // key the current key used to authenticate the provided sector.
if err = r.accessStarted(); err != nil { func (r *Dev) WriteSectorTrail(timeout time.Duration, auth byte, sector int, keyA Key, keyB Key, access *BlocksAccess, key Key) (err error) {
return r.beforeCall()
}
defer func() { defer func() {
r.accessFinished() r.afterCall()
if err == nil { if err == nil {
err = r.LowLevel.StopCrypto() err = r.LowLevel.StopCrypto()
} }
}() }()
uuid, err := r.selectCard() uuid, err := r.selectCard(timeout)
if err != nil { if err != nil {
return return
} }
@ -302,74 +246,16 @@ func (r *Dev) WriteSectorTrail(auth byte, sector int, keyA [6]byte, keyB [6]byte
var data [16]byte var data [16]byte
copy(data[:], keyA[:]) copy(data[:], keyA[:])
accessData := CalculateBlockAccess(access) var accessData [4]byte
copy(data[6:], accessData[:4]) if err := access.serialize(accessData[:]); err != nil {
copy(data[10:], keyB[:])
return r.write(calcBlockAddress(sector&0xFF, 3), data[:])
}
// MFRC522 SPI Dev private/helper functions
// Checks whether device is already in use.
func (r *Dev) accessStarted() error {
r.xMu.Lock()
defer r.xMu.Unlock()
if r.isAccessing {
return wrapf("concurrent access is forbidden")
}
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
}
r.oMu.Lock()
gain := byte(r.antennaGain) << 4
r.oMu.Unlock()
if err := r.LowLevel.DevWrite(int(commands.RFCfgReg), gain); err != nil {
return err return err
} }
copy(data[6:], accessData[:])
return r.setAntenna(true) copy(data[10:], keyB[:])
} return r.write(calcBlockAddress(sector&0xFF, 3), data[:])
// reset resets the RFID chip to initial state.
func (r *Dev) reset() error {
return r.LowLevel.DevWrite(commands.CommandReg, commands.PCD_RESETPHASE)
} }
// setAntenna configures the antenna state, on/off. // MFRC522 SPI Dev private/helper functions
func (r *Dev) setAntenna(state bool) error {
if state {
current, err := r.LowLevel.DevRead(commands.TxControlReg)
if err != nil {
return err
}
if current&0x03 != 0 {
return wrapf("can not set the bitmask for antenna")
}
return r.LowLevel.SetBitmask(commands.TxControlReg, 0x03)
}
return r.LowLevel.ClearBitmask(commands.TxControlReg, 0x03)
}
// request the card information. Returns number of blocks available on the card. // request the card information. Returns number of blocks available on the card.
func (r *Dev) request() (int, error) { func (r *Dev) request() (int, error) {
@ -387,69 +273,6 @@ func (r *Dev) request() (int, error) {
return backBits, nil return backBits, nil
} }
// 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 {
r.mu.Unlock()
return wrapf("concurrent access is forbidden")
}
r.isWaiting = true
r.mu.Unlock()
irqChannel := make(chan bool)
go func() {
r.oMu.Lock()
timeout := r.operationTimeout
r.oMu.Unlock()
result := r.LowLevel.WaitForEdge(timeout)
r.mu.Lock()
defer r.mu.Unlock()
if waitCancelled {
return
}
irqChannel <- result
}()
defer func() {
r.mu.Lock()
r.isWaiting = false
waitCancelled = true
r.mu.Unlock()
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 <-r.stop:
return wrapf("halt")
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. // antiColl performs the collision check for different cards.
func (r *Dev) antiColl() ([]byte, error) { func (r *Dev) antiColl() ([]byte, error) {
if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x00); err != nil { if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x00); err != nil {
@ -507,32 +330,18 @@ func (r *Dev) selectTag(serial []byte) (byte, error) {
// readBlock reads the block from the card. // readBlock reads the block from the card.
// //
// sector - card sector to read from // sector - card sector to read from
// block - the block within the sector (0-3 tor Mifare 4K) // 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)) return r.read(calcBlockAddress(sector, block%3))
} }
func (ba *BlocksAccess) getBits(bitNum uint) byte { // selectCard selects the card after the IRQ event was received.
shift := 3 - bitNum func (r *Dev) selectCard(timeout time.Duration) ([]byte, error) {
bit := byte(1 << shift) if err := r.LowLevel.WaitForEdge(timeout); err != nil {
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.LowLevel.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 return nil, err
} }
if err := r.init(); err != nil { if err := r.LowLevel.Init(); err != nil {
return nil, err return nil, err
} }
if _, err := r.request(); err != nil { if _, err := r.request(); err != nil {
@ -548,10 +357,10 @@ func (r *Dev) selectCard() ([]byte, error) {
return uuid, nil return uuid, nil
} }
func calcBlockAddress(sector int, block int) byte { // write writes the data block into the card at given block address.
return byte(sector*4 + block) //
} // blockAddr - the calculated block address
// data - the sector data bytes
func (r *Dev) write(blockAddr byte, data []byte) error { func (r *Dev) write(blockAddr byte, data []byte) error {
read, backLen, err := r.preAccess(blockAddr, commands.PICC_WRITE) read, backLen, err := r.preAccess(blockAddr, commands.PICC_WRITE)
if err != nil || backLen != 4 { if err != nil || backLen != 4 {
@ -578,6 +387,10 @@ func (r *Dev) write(blockAddr byte, data []byte) error {
return nil 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) { func (r *Dev) preAccess(blockAddr byte, cmd byte) ([]byte, int, error) {
send := make([]byte, 4) send := make([]byte, 4)
send[0] = cmd send[0] = cmd
@ -592,6 +405,9 @@ func (r *Dev) preAccess(blockAddr byte, cmd byte) ([]byte, int, error) {
return r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, send) 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) { func (r *Dev) read(blockAddr byte) ([]byte, error) {
data, _, err := r.preAccess(blockAddr, commands.PICC_READ) data, _, err := r.preAccess(blockAddr, commands.PICC_READ)
if err != nil { if err != nil {
@ -603,34 +419,8 @@ func (r *Dev) read(blockAddr byte) ([]byte, error) {
return data, nil 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},
},
}
func wrapf(format string, a ...interface{}) error { func wrapf(format string, a ...interface{}) error {
return fmt.Errorf("mfrc522: "+format, a...) return fmt.Errorf("mfrc522: "+format, a...)
} }
var _ conn.Resource = &Dev{} var _ conn.Resource = &Dev{}
var _ fmt.Stringer = &BlocksAccess{}

@ -40,7 +40,15 @@ func TestBitCalc(t *testing.T) {
B0: RAB_WB_IB_DAB, // 110 B0: RAB_WB_IB_DAB, // 110
} }
access := CalculateBlockAccess(&ba) var access [4]byte
if err := ba.serialize(access[:1]); err == nil {
t.Fatal("destination array should be reported as insufficient")
}
if err := ba.serialize(access[:]); err != nil {
t.Fatal(err)
}
if fromBitString(t, "1011") != ba.getBits(1) { if fromBitString(t, "1011") != ba.getBits(1) {
t.Fatalf("1011 is not equal to %d", ba.getBits(1)) t.Fatalf("1011 is not equal to %d", ba.getBits(1))
@ -55,14 +63,16 @@ func TestBitCalc(t *testing.T) {
expected[3] = expected[0] ^ expected[1] ^ expected[2] expected[3] = expected[0] ^ expected[1] ^ expected[2]
if !bytes.Equal(expected, access) { if !bytes.Equal(expected, access[:]) {
t.Fatalf("Access is incorrect: %v != %v", expected, access) t.Fatalf("Access is incorrect: %v != %v", expected, access)
} }
parsedAccess := ParseBlockAccess(access) var parsedAccess BlocksAccess
if !reflect.DeepEqual(ba, *parsedAccess) { parsedAccess.Init(access[:])
t.Fatalf("Parsed access mismatch %s != %s", ba.String(), (*parsedAccess).String())
if !reflect.DeepEqual(ba, parsedAccess) {
t.Fatalf("Parsed access mismatch %s != %s", ba.String(), (parsedAccess).String())
} }
} }
@ -91,8 +101,12 @@ func TestByteArrayDecipher(t *testing.T) {
B2: AnyKeyRWID, B2: AnyKeyRWID,
B3: KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA, B3: KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA,
} }
access := CalculateBlockAccess(&ba) var access [4]byte
if !reflect.DeepEqual(bitsData[:], access) {
if err := ba.serialize(access[:]); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(bitsData[:], access[:]) {
t.Fatalf("Wrong access calculation: %v != %v", bitsData, access) t.Fatalf("Wrong access calculation: %v != %v", bitsData, access)
} }
} }

Loading…
Cancel
Save