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