From a8429b61f9e81d2af3b74946d8e8bf297807ad5b Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 8 Dec 2018 14:17:31 -0500 Subject: [PATCH] mfrc522: reworked public/private API. (#349) Fixed the wait for edge block, removed synchronization code. Added concurrent execution protection via config. Updated API docs. --- experimental/devices/mfrc522/blockaccess.go | 106 ++++ .../devices/mfrc522/commands/low_level.go | 158 +++++- experimental/devices/mfrc522/example_test.go | 2 +- experimental/devices/mfrc522/mfrc522.go | 458 +++++------------- experimental/devices/mfrc522/mfrc522_test.go | 28 +- 5 files changed, 389 insertions(+), 363 deletions(-) create mode 100644 experimental/devices/mfrc522/blockaccess.go diff --git a/experimental/devices/mfrc522/blockaccess.go b/experimental/devices/mfrc522/blockaccess.go new file mode 100644 index 0000000..417ed5e --- /dev/null +++ b/experimental/devices/mfrc522/blockaccess.go @@ -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{} diff --git a/experimental/devices/mfrc522/commands/low_level.go b/experimental/devices/mfrc522/commands/low_level.go index f3b864e..290c75a 100644 --- a/experimental/devices/mfrc522/commands/low_level.go +++ b/experimental/devices/mfrc522/commands/low_level.go @@ -13,10 +13,6 @@ import ( "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 @@ -24,6 +20,19 @@ const ( 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. // // spiPort - the SPI device to use. @@ -33,9 +42,6 @@ func NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) ( 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 @@ -43,24 +49,66 @@ func NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) ( if err := resetPin.Out(gpio.High); err != nil { return nil, err } - if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil { - return nil, err + if irqPin != nil { + if err := irqPin.In(gpio.PullUp, gpio.FallingEdge); err != nil { + return nil, err + } } dev := &LowLevel{ - spiDev: spiDev, - irqPin: irqPin, - resetPin: resetPin, + spiDev: spiDev, + irqPin: irqPin, + resetPin: resetPin, + antennaGain: 4, + stop: make(chan struct{}, 1), } 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 +// Reset resets the RFID chip to initial state. +func (r *LowLevel) Reset() error { + return r.DevWrite(CommandReg, PCD_RESETPHASE) +} + +// 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. @@ -145,9 +193,37 @@ 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) +// 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) error { + 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. @@ -288,6 +364,46 @@ func (r *LowLevel) CardWrite(command byte, data []byte) ([]byte, int, error) { 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 { - 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}, + }, } diff --git a/experimental/devices/mfrc522/example_test.go b/experimental/devices/mfrc522/example_test.go index 7d597e1..4fd65d2 100644 --- a/experimental/devices/mfrc522/example_test.go +++ b/experimental/devices/mfrc522/example_test.go @@ -67,7 +67,7 @@ func Example() { for { // 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 timedOut { diff --git a/experimental/devices/mfrc522/mfrc522.go b/experimental/devices/mfrc522/mfrc522.go index d70ae63..59f6b53 100644 --- a/experimental/devices/mfrc522/mfrc522.go +++ b/experimental/devices/mfrc522/mfrc522.go @@ -20,107 +20,81 @@ import ( "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 -) - -// 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 +// Dev is an handle to an MFRC522 RFID reader. +type Dev struct { + LowLevel *commands.LowLevel + operationTimeout time.Duration + beforeCall func() + afterCall func() } -func (ba *BlocksAccess) String() string { - return fmt.Sprintf("B0: %d, B1: %d, B2: %d, B3: %d", ba.B0, ba.B1, ba.B2, ba.B3) +// 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() } -// CalculateBlockAccess calculates the block access. -func CalculateBlockAccess(ba *BlocksAccess) []byte { - res := make([]byte, 4) - res[0] = ((^ba.getBits(2) & 0x0F) << 4) | (^ba.getBits(1) & 0x0F) - res[1] = ((ba.getBits(1) & 0x0F) << 4) | (^ba.getBits(3) & 0x0F) - res[2] = ((ba.getBits(3) & 0x0F) << 4) | (ba.getBits(2) & 0x0F) - res[3] = res[0] ^ res[1] ^ res[2] - return res +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 + } } -// ParseBlockAccess parses the given byte array into the block access structure. -func ParseBlockAccess(ad []byte) *BlocksAccess { - return &BlocksAccess{ - B0: BlockAccess(((ad[1] & 0x10) >> 2) | ((ad[2] & 0x01) << 1) | ((ad[2] & 0x10) >> 5)), - B1: BlockAccess(((ad[1] & 0x20) >> 3) | (ad[2] & 0x02) | ((ad[2] & 0x20) >> 5)), - B2: BlockAccess(((ad[1] & 0x40) >> 4) | ((ad[2] & 0x04) >> 1) | ((ad[2] & 0x40) >> 6)), - B3: SectorTrailerAccess(((ad[1] & 0x80) >> 5) | ((ad[2] & 0x08) >> 2) | ((ad[2] & 0x80) >> 7)), +// 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 } } -// DefaultKey provides the default bytes for card authentication for method B. -var DefaultKey = [...]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} +// 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) (*Dev, error) { +// 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{ - operationTimeout: 30 * time.Second, - antennaGain: 4, - stop: make(chan struct{}, 1), LowLevel: raw, - } - if err := dev.init(); err != nil { - return nil, err + operationTimeout: cfg.defaultTimeout, + beforeCall: cfg.beforeCall, + afterCall: cfg.afterCall, } 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. func (r *Dev) String() string { return r.LowLevel.String() @@ -130,65 +104,40 @@ func (r *Dev) String() string { // // It soft-stops the chip - PowerDown bit set, command IDLE func (r *Dev) Halt() error { - r.mu.Lock() - defer r.mu.Unlock() - - 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 + r.beforeCall() + defer r.afterCall() + return r.LowLevel.Halt() } // 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 { + r.beforeCall() + defer r.afterCall() if gain < 0 || gain > 7 { return wrapf("gain must be in [0..7] interval") } - - r.oMu.Lock() - defer r.oMu.Unlock() - r.antennaGain = gain + r.LowLevel.SetAntennaGain(gain) return nil } -// ReadCard reads the card sector/block. +// ReadCard reads the card sector/block with IRQ event 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(auth byte, sector int, block int, key [6]byte) (data []byte, err error) { - if err = r.accessStarted(); err != nil { - return - } - +// 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.accessFinished() + r.afterCall() if err == nil { err = r.LowLevel.StopCrypto() } }() - uuid, err := r.selectCard() + uuid, err := r.selectCard(timeout) if err != nil { 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) } -// ReadAuth - read the card authentication data. +// ReadAuth reads the card authentication data with IRQ event timeout. // -// 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 - } - +// 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.accessFinished() + r.afterCall() if err == nil { err = r.LowLevel.StopCrypto() } }() - uuid, err := r.selectCard() + uuid, err := r.selectCard(timeout) if err != nil { 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)) } -// WriteCard writes the data into the card block. +// WriteCard writes the data into the card block with IRQ event 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(auth byte, sector int, block int, data [16]byte, key [6]byte) (err error) { - if err = r.accessStarted(); err != nil { - return - } - +// 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.accessFinished() + r.afterCall() if err == nil { err = r.LowLevel.StopCrypto() } }() - uuid, err := r.selectCard() + uuid, err := r.selectCard(timeout) if err != nil { 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[:]) } -// 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. -// 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 - } - +// 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.accessFinished() + r.afterCall() if err == nil { err = r.LowLevel.StopCrypto() } }() - uuid, err := r.selectCard() + uuid, err := r.selectCard(timeout) if err != nil { return } @@ -302,74 +246,16 @@ func (r *Dev) WriteSectorTrail(auth byte, sector int, keyA [6]byte, keyB [6]byte 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[:]) -} - -// 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 { + var accessData [4]byte + if err := access.serialize(accessData[:]); err != nil { return err } - - return r.setAntenna(true) -} - -// reset resets the RFID chip to initial state. -func (r *Dev) reset() error { - return r.LowLevel.DevWrite(commands.CommandReg, commands.PCD_RESETPHASE) + copy(data[6:], accessData[:]) + copy(data[10:], keyB[:]) + return r.write(calcBlockAddress(sector&0xFF, 3), data[:]) } -// 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 - } - 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) -} +// MFRC522 SPI Dev private/helper functions // request the card information. Returns number of blocks available on the card. func (r *Dev) request() (int, error) { @@ -387,69 +273,6 @@ 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 { - 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. func (r *Dev) antiColl() ([]byte, error) { 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. // -// sector - card sector to read from -// block - the block within the sector (0-3 tor Mifare 4K) +// 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)) } -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 (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 { +// 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.init(); err != nil { + if err := r.LowLevel.Init(); err != nil { return nil, err } if _, err := r.request(); err != nil { @@ -548,10 +357,10 @@ func (r *Dev) selectCard() ([]byte, error) { return uuid, nil } -func calcBlockAddress(sector int, block int) byte { - return byte(sector*4 + block) -} - +// 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 { @@ -578,6 +387,10 @@ func (r *Dev) write(blockAddr byte, data []byte) error { 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 @@ -592,6 +405,9 @@ func (r *Dev) preAccess(blockAddr byte, cmd byte) ([]byte, int, error) { 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 { @@ -603,34 +419,8 @@ func (r *Dev) read(blockAddr byte) ([]byte, error) { 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 { return fmt.Errorf("mfrc522: "+format, a...) } var _ conn.Resource = &Dev{} -var _ fmt.Stringer = &BlocksAccess{} diff --git a/experimental/devices/mfrc522/mfrc522_test.go b/experimental/devices/mfrc522/mfrc522_test.go index 9385e57..16f441a 100644 --- a/experimental/devices/mfrc522/mfrc522_test.go +++ b/experimental/devices/mfrc522/mfrc522_test.go @@ -40,7 +40,15 @@ func TestBitCalc(t *testing.T) { 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) { 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] - if !bytes.Equal(expected, access) { + if !bytes.Equal(expected, access[:]) { t.Fatalf("Access is incorrect: %v != %v", expected, access) } - parsedAccess := ParseBlockAccess(access) + var parsedAccess BlocksAccess - if !reflect.DeepEqual(ba, *parsedAccess) { - t.Fatalf("Parsed access mismatch %s != %s", ba.String(), (*parsedAccess).String()) + parsedAccess.Init(access[:]) + + 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, B3: KeyA_RN_WA_BITS_RA_WA_KeyB_RA_WA, } - access := CalculateBlockAccess(&ba) - if !reflect.DeepEqual(bitsData[:], access) { + var access [4]byte + + if err := ba.serialize(access[:]); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(bitsData[:], access[:]) { t.Fatalf("Wrong access calculation: %v != %v", bitsData, access) } }