|
|
|
@ -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{}
|
|
|
|
|
|
|
|
|