* drivers/contactless/mfrc522.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Balboa released into the public domain:
* https://github.com/miguelbalboa/rfid/
*/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/power/pm.h>
#include <nuttx/contactless/mfrc522.h>
#include "mfrc522.h"
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_CL_MFRC522_DEBUG_TX
# define tracetx errdumpbuffer
#else
# define tracetx(x...)
#endif
#ifdef CONFIG_CL_MFRC522_DEBUG_RX
# define tracerx errdumpbuffer
#else
# define tracerx(x...)
#endif
#define FRAME_SIZE(f) (sizeof(struct mfrc522_frame) + f->len + 2)
#define FRAME_POSTAMBLE(f) (f->data[f->len + 1])
* Private Function Prototypes
****************************************************************************/
static inline void mfrc522_configspi(FAR struct spi_dev_s *spi);
static void mfrc522_lock(FAR struct spi_dev_s *spi);
static void mfrc522_unlock(FAR struct spi_dev_s *spi);
static int mfrc522_open(FAR struct file *filep);
static int mfrc522_close(FAR struct file *filep);
static ssize_t mfrc522_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t mfrc522_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static int mfrc522_ioctl(FAR struct file *filep, int cmd,
unsigned long arg);
uint8_t mfrc522_readu8(FAR struct mfrc522_dev_s *dev, uint8_t regaddr);
void mfrc522_writeu8(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
FAR uint8_t regval);
void mfrc522_writeblk(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
uint8_t *regval, int length);
void mfrc522_readblk(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
FAR uint8_t *regval, int length, uint8_t rxalign);
void mfrc522_softreset(FAR struct mfrc522_dev_s *dev);
int mfrc522_picc_select(FAR struct mfrc522_dev_s *dev,
FAR struct picc_uid_s *uid, uint8_t validbits);
#if 0
static int mfrc522_irqhandler(int irq, FAR void *context, FAR void *dev);
static inline int mfrc522_attachirq(FAR struct mfrc522_dev_s *dev,
xcpt_t isr);
#endif
* Private Data
****************************************************************************/
static const struct file_operations g_mfrc522fops =
{
mfrc522_open,
mfrc522_close,
mfrc522_read,
mfrc522_write,
NULL,
mfrc522_ioctl,
};
* Private Functions
****************************************************************************/
static void mfrc522_lock(FAR struct spi_dev_s *spi)
{
SPI_LOCK(spi, true);
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETBITS(spi, 8);
SPI_HWFEATURES(spi, 0);
SPI_SETFREQUENCY(spi, CONFIG_MFRC522_SPI_FREQ);
}
static void mfrc522_unlock(FAR struct spi_dev_s *spi)
{
SPI_LOCK(spi, false);
}
static inline void mfrc522_configspi(FAR struct spi_dev_s *spi)
{
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETBITS(spi, 8);
SPI_HWFEATURES(spi, 0);
SPI_SETFREQUENCY(spi, CONFIG_MFRC522_SPI_FREQ);
}
static inline void mfrc522_select(struct mfrc522_dev_s *dev)
{
SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS(0), true);
}
static inline void mfrc522_deselect(struct mfrc522_dev_s *dev)
{
SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS(0), false);
}
* Name: mfrc522_readu8
*
* Description:
* Read a byte from a register address.
*
* Input Parameters:
*
* Returned Value: the read byte from the register
*
****************************************************************************/
uint8_t mfrc522_readu8(FAR struct mfrc522_dev_s *dev, uint8_t regaddr)
{
uint8_t regval;
uint8_t address = (0x80 | (regaddr & 0x7e));
mfrc522_lock(dev->spi);
mfrc522_select(dev);
SPI_SEND(dev->spi, address);
regval = SPI_SEND(dev->spi, 0);
mfrc522_deselect(dev);
mfrc522_unlock(dev->spi);
tracerx("read", ®val, 1);
return regval;
}
* Name: mfrc522_write8
*
* Description:
* Write a byte to a register address.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
void mfrc522_writeu8(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
FAR uint8_t regval)
{
mfrc522_lock(dev->spi);
mfrc522_select(dev);
SPI_SEND(dev->spi, regaddr & 0x7e);
SPI_SEND(dev->spi, regval);
mfrc522_deselect(dev);
mfrc522_unlock(dev->spi);
tracerx("write", ®val, 1);
}
* Name: mfrc522_readblk
*
* Description:
* Read a block of bytes from a register address. Align the bit positions
* of regval[0] from rxalign..7.
*
* Input Parameters:
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_readblk(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
FAR uint8_t *regval, int length, uint8_t rxalign)
{
uint8_t i = 0;
uint8_t address = (0x80 | (regaddr & 0x7e));
mfrc522_lock(dev->spi);
mfrc522_select(dev);
SPI_SEND(dev->spi, address);
while (i < length)
{
if (i == 0 && rxalign)
{
uint8_t mask = 0;
uint8_t value;
uint8_t j;
for (j = rxalign; j <= 7; j++)
{
mask |= (1 << j);
}
value = SPI_SEND(dev->spi, address);
regval[0] = (regval[0] & ~mask) | (value & mask);
}
else
{
regval[i] = SPI_SEND(dev->spi, address);
}
i++;
}
* Send 0 to stop reading (it maybe wrong, 1 byte out)
*/
regval[i] = SPI_SEND(dev->spi, 0);
mfrc522_deselect(dev);
mfrc522_unlock(dev->spi);
tracerx("readblk", regval, length);
}
* Name: mfrc522_writeblk
*
* Description:
* Write a block of bytes to a register address.
*
* Input Parameters:
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_writeblk(FAR struct mfrc522_dev_s *dev, uint8_t regaddr,
uint8_t *regval, int length)
{
uint8_t address = (regaddr & 0x7e);
mfrc522_lock(dev->spi);
mfrc522_select(dev);
SPI_SEND(dev->spi, address);
SPI_SNDBLOCK(dev->spi, regval, length);
mfrc522_deselect(dev);
mfrc522_unlock(dev->spi);
tracerx("writeblk", regval, length);
}
* Name: mfrc522_calc_crc
*
* Description:
* Use the CRC coprocessor in the MFRC522 to calculate a CRC_A.
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_calc_crc(FAR struct mfrc522_dev_s *dev, uint8_t *buffer,
int length, uint8_t *result)
{
struct timespec tstart;
struct timespec tend;
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD);
mfrc522_writeu8(dev, MFRC522_DIV_IRQ_REG, MFRC522_CRC_IRQ);
mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER);
mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, buffer, length);
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_CALC_CRC_CMD);
clock_systime_timespec(&tstart);
tstart.tv_nsec += 200000;
if (tstart.tv_nsec >= 1000 * 1000 * 1000)
{
tstart.tv_sec++;
tstart.tv_nsec -= 1000 * 1000 * 1000;
}
while (1)
{
uint8_t irqreg;
irqreg = mfrc522_readu8(dev, MFRC522_DIV_IRQ_REG);
if (irqreg & MFRC522_CRC_IRQ)
{
break;
}
clock_systime_timespec(&tend);
if ((tend.tv_sec > tstart.tv_sec) && (tend.tv_nsec > tstart.tv_nsec))
{
return -ETIMEDOUT;
}
}
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD);
result[0] = mfrc522_readu8(dev, MFRC522_CRC_RESULT_REGL);
result[1] = mfrc522_readu8(dev, MFRC522_CRC_RESULT_REGH);
return OK;
}
* Name: mfrc522_comm_picc
*
* Description:
* Transfers data to the MFRC522 FIFO, executes a command, waits for
* completion and transfers data back from the FIFO.
* CRC validation can only be done if back_data and back_len are specified.
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_comm_picc(FAR struct mfrc522_dev_s *dev, uint8_t command,
uint8_t waitirq, uint8_t *send_data, uint8_t send_len,
uint8_t *back_data, uint8_t *back_len,
uint8_t *validbits, uint8_t rxalign, bool checkcrc)
{
int ret;
uint8_t errors;
uint8_t vbits;
uint8_t value;
struct timespec tstart;
struct timespec tend;
uint8_t txlastbits = validbits ? *validbits : 0;
uint8_t bitframing = (rxalign << 4) + txlastbits;
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD);
value = mfrc522_readu8(dev, MFRC522_COM_IRQ_REG);
mfrc522_writeu8(dev, MFRC522_COM_IRQ_REG, value | MFRC522_COM_IRQ_MASK);
mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER);
mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, send_data, send_len);
mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG, bitframing);
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, command);
* internal MFC522 Timer to detect timeout, but because there could be some
* hardware fault, let us to use a NuttX timeout as well.
*/
clock_systime_timespec(&tstart);
tstart.tv_nsec += 200000;
if (tstart.tv_nsec >= 1000 * 1000 * 1000)
{
tstart.tv_sec++;
tstart.tv_nsec -= 1000 * 1000 * 1000;
}
if (command == MFRC522_TRANSCV_CMD)
{
value = mfrc522_readu8(dev, MFRC522_BIT_FRAMING_REG);
mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG,
value | MFRC522_START_SEND);
}
while (1)
{
uint8_t irqsreg;
irqsreg = mfrc522_readu8(dev, MFRC522_COM_IRQ_REG);
if (irqsreg & waitirq)
{
break;
}
if (irqsreg & MFRC522_TIMER_IRQ)
{
return -ETIMEDOUT;
}
clock_systime_timespec(&tend);
if ((tend.tv_sec > tstart.tv_sec) &&
(tend.tv_nsec > tstart.tv_nsec))
{
return -ETIMEDOUT;
}
}
errors = mfrc522_readu8(dev, MFRC522_ERROR_REG);
if (errors & (MFRC522_PROTO_ERR))
{
return -EPROTO;
}
if (errors & (MFRC522_PARITY_ERR | MFRC522_BUF_OVFL_ERR))
{
return -EIO;
}
if (errors & MFRC522_COLL_ERR)
{
return -EBUSY;
}
if (back_data && back_len)
{
uint8_t nbytes;
nbytes = mfrc522_readu8(dev, MFRC522_FIFO_LEVEL_REG);
if (nbytes > *back_len)
{
return -ENOMEM;
}
*back_len = nbytes;
mfrc522_readblk(dev,
MFRC522_FIFO_DATA_REG, back_data, nbytes, rxalign);
vbits = mfrc522_readu8(dev, MFRC522_CONTROL_REG)
& MFRC522_RX_LAST_BITS_MASK;
if (validbits)
{
*validbits = vbits;
}
}
if (back_data && back_len && checkcrc)
{
uint8_t ctrlbuf[2];
if (*back_len == 1 && vbits == 4)
{
return -EACCES;
}
if (*back_len < 2 || vbits != 0)
{
return -EPERM;
}
ret = mfrc522_calc_crc(dev, &back_data[0], *back_len - 2, &ctrlbuf[0]);
if (ret != OK)
{
return ret;
}
if ((back_data[*back_len - 2] != ctrlbuf[0]) ||
(back_data[*back_len - 1] != ctrlbuf[1]))
{
return -EFAULT;
}
}
return OK;
}
* Name: mfrc522_transcv_data
*
* Description:
* Executes the Transceive command
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_transcv_data(FAR struct mfrc522_dev_s *dev,
uint8_t *senddata, uint8_t sendlen,
uint8_t *backdata, uint8_t *backlen,
uint8_t *validbits, uint8_t rxalign,
bool check_crc)
{
uint8_t waitirq = MFRC522_RX_IRQ | MFRC522_IDLE_IRQ;
return mfrc522_comm_picc(dev, MFRC522_TRANSCV_CMD, waitirq, senddata,
sendlen, backdata, backlen,
validbits, rxalign, check_crc);
}
* Name: mfrc522_picc_reqa_wupa
*
* Description:
* Transmits REQA or WUPA commands
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_picc_reqa_wupa(FAR struct mfrc522_dev_s *dev, uint8_t command,
uint8_t *buffer, uint8_t length)
{
uint8_t validbits;
uint8_t value;
int status;
if (!buffer || length < 2)
{
return -EINVAL;
}
value = mfrc522_readu8(dev, MFRC522_COLL_REG);
mfrc522_writeu8(dev, MFRC522_COLL_REG, value & MFRC522_VALUES_AFTER_COLL);
validbits = 7;
status = mfrc522_transcv_data(dev, &command, 1, buffer,
&length, &validbits, 0, false);
if (status != OK)
{
return status;
}
if (length != 2 || validbits != 0)
{
return -EAGAIN;
}
ctlsinfo("buffer[0]=0x%02X | buffer[1]=0x%02X\n", buffer[0], buffer[1]);
return OK;
}
* Name: mfrc522_picc_request_a
*
* Description:
* Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go
* to READY and prepare for anticollision or selection.
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_picc_request_a(FAR struct mfrc522_dev_s *dev, uint8_t *buffer,
uint8_t length)
{
return mfrc522_picc_reqa_wupa(dev, PICC_CMD_REQA, buffer, length);
}
* Name: mfrc522_picc_detect
*
* Description:
* Detects if a Contactless Card is near
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_picc_detect(FAR struct mfrc522_dev_s *dev)
{
int ret;
uint8_t buffer_atqa[2];
uint8_t length = sizeof(buffer_atqa);
ret = mfrc522_picc_request_a(dev, buffer_atqa, length);
return (ret == OK || ret == -EBUSY);
}
* Name: mfrc522_picc_select
*
* Description:
* Selects a near Card and read its UID.
*
* Input Parameters:
*
* Returned Value: OK or -ETIMEDOUT
*
****************************************************************************/
int mfrc522_picc_select(FAR struct mfrc522_dev_s *dev,
FAR struct picc_uid_s *uid, uint8_t validbits)
{
bool uid_complete;
bool select_done;
bool use_cascade_tag;
uint8_t cascade_level = 1;
int result;
uint8_t i;
uint8_t value;
uint8_t count;
* Level
*/
uint8_t uid_index;
int8_t curr_level_known_bits;
uint8_t buffer[9];
uint8_t buffer_used;
uint8_t rxalign;
uint8_t txlastbits;
uint8_t *resp_buf;
uint8_t resp_len;
if (validbits > 80)
{
return -EINVAL;
}
value = mfrc522_readu8(dev, MFRC522_COLL_REG);
mfrc522_writeu8(dev,
MFRC522_COLL_REG, value & MFRC522_VALUES_AFTER_COLL);
uid_complete = false;
while (!uid_complete)
{
uint8_t bytes_to_copy;
* the Cascade Tag in byte 2.
*/
switch (cascade_level)
{
case 1:
buffer[0] = PICC_CMD_SEL_CL1;
uid_index = 0;
use_cascade_tag = validbits && (uid->size > 4);
break;
case 2:
buffer[0] = PICC_CMD_SEL_CL2;
uid_index = 3;
use_cascade_tag = validbits && (uid->size > 7);
break;
case 3:
buffer[0] = PICC_CMD_SEL_CL3;
uid_index = 6;
use_cascade_tag = false;
break;
default:
return -EIO;
}
curr_level_known_bits = validbits - (8 * uid_index);
if (curr_level_known_bits < 0)
{
curr_level_known_bits = 0;
}
i = 2;
if (use_cascade_tag)
{
buffer[i++] = PICC_CMD_CT;
}
bytes_to_copy = curr_level_known_bits / 8 +
(curr_level_known_bits % 8 ? 1 : 0);
if (bytes_to_copy)
{
* Cascade Tag.
*/
uint8_t max_bytes = use_cascade_tag ? 3 : 4;
if (bytes_to_copy > max_bytes)
{
bytes_to_copy = max_bytes;
}
for (count = 0; count < bytes_to_copy; count++)
{
buffer[i++] = uid->uid_data[uid_index + count];
}
}
* CT in curr_level_known_bits.
*/
if (use_cascade_tag)
{
curr_level_known_bits += 8;
}
* and receive a SAK - max 32 iterations.
*/
select_done = false;
while (!select_done)
{
if (curr_level_known_bits >= 32)
{
* SELECT.
*/
buffer[1] = 0x70;
buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5];
result = mfrc522_calc_crc(dev, buffer, 7, &buffer[7]);
if (result != OK)
{
return result;
}
txlastbits = 0;
buffer_used = 9;
* (BCC and CRC_A - not needed after tx).
*/
resp_buf = &buffer[6];
resp_len = 3;
}
else
{
txlastbits = curr_level_known_bits % 8;
count = curr_level_known_bits / 8;
i = 2 + count;
buffer[1] = (i << 4) + txlastbits;
buffer_used = i + (txlastbits ? 1 : 0);
resp_buf = &buffer[i];
resp_len = sizeof(buffer) - i;
}
rxalign = txlastbits;
mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG,
(rxalign << 4) + txlastbits);
result = mfrc522_transcv_data(dev, buffer, buffer_used,
resp_buf, &resp_len,
&txlastbits, rxalign, false);
if (result == -EBUSY)
{
uint8_t coll_pos;
uint8_t coll_reg = mfrc522_readu8(dev, MFRC522_COLL_REG);
if (coll_reg & 0x20)
{
return -EBUSY;
}
coll_pos = coll_reg & 0x1f;
if (coll_pos == 0)
{
coll_pos = 32;
}
if (coll_pos <= curr_level_known_bits)
{
return -EIO;
}
curr_level_known_bits = coll_pos;
count = (curr_level_known_bits - 1) % 8;
i = 1 + (curr_level_known_bits / 8) + (count ? 1 : 0);
buffer[i] |= (1 << count);
}
else if (result != OK)
{
return result;
}
else
{
if (curr_level_known_bits >= 32)
{
select_done = true;
}
else
{
curr_level_known_bits = 32;
}
}
}
i = (buffer[2] == PICC_CMD_CT) ? 3 : 2;
bytes_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4;
for (count = 0; count < bytes_to_copy; count++)
{
uid->uid_data[uid_index + count] = buffer[i++];
}
if (resp_len != 3 || txlastbits != 0)
{
return -EIO;
}
* buffer[2..3] - those bytes are not needed anymore.
*/
result = mfrc522_calc_crc(dev, resp_buf, 1, &buffer[2]);
if (result != OK)
{
return result;
}
if ((buffer[2] != resp_buf[1]) || (buffer[3] != resp_buf[2]))
{
return -EINVAL;
}
if (resp_buf[0] & 0x04)
{
cascade_level++;
}
else
{
uid_complete = true;
uid->sak = resp_buf[0];
}
}
uid->size = 3 * cascade_level + 1;
return OK;
}
* Name: mfrc522_softreset
*
* Description:
* Send a software reset command
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_softreset(FAR struct mfrc522_dev_s *dev)
{
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_SOFTRST_CMD);
nxsig_usleep(50000);
while (mfrc522_readu8(dev, MFRC522_COMMAND_REG) & MFRC522_POWER_DOWN);
}
* Name: mfrc522_enableantenna
*
* Description:
* Turns the antenna on by enabling the pins TX1 and TX2
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_enableantenna(FAR struct mfrc522_dev_s *dev)
{
uint8_t value = mfrc522_readu8(dev, MFRC522_TX_CTRL_REG);
if ((value & (MFRC522_TX1_RF_EN | MFRC522_TX2_RF_EN)) != 0x03)
{
mfrc522_writeu8(dev, MFRC522_TX_CTRL_REG, value | 0x03);
}
}
* Name: mfrc522_disableantenna
*
* Description:
* Turns the antenna off cutting the signals on TX1 and TX2
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_disableantenna(FAR struct mfrc522_dev_s *dev)
{
uint8_t value = mfrc522_readu8(dev, MFRC522_TX_CTRL_REG);
value &= ~(MFRC522_TX1_RF_EN | MFRC522_TX2_RF_EN);
mfrc522_writeu8(dev, MFRC522_TX_CTRL_REG, value);
}
* Name: mfrc522_getfwversion
*
* Description:
* Read the MFRC522 firmware version.
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: the firmware version byte
*
****************************************************************************/
uint8_t mfrc522_getfwversion(FAR struct mfrc522_dev_s *dev)
{
return mfrc522_readu8(dev, MFRC522_VERSION_REG);
}
* Name: mfrc522_getantennagain
*
* Description:
* Read the MFRC522 receiver gain (RxGain).
* See 9.3.3.6 / table 98 in MFRC522 datasheet.
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
uint8_t mfrc522_getantennagain(FAR struct mfrc522_dev_s *dev)
{
return mfrc522_readu8(dev, MFRC522_RF_CFG_REG) & MFRC522_RX_GAIN_MASK;
}
* Name: mfrc522_setantennagain
*
* Description:
* Set the MFRC522 receiver gain (RxGain) to value value specified in mask.
* See 9.3.3.6 / table 98 in MFRC522 datasheet.
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_setantennagain(FAR struct mfrc522_dev_s *dev, uint8_t mask)
{
uint8_t value;
if ((value = mfrc522_getantennagain(dev)) != mask)
{
mfrc522_writeu8(dev,
MFRC522_RF_CFG_REG, value & ~MFRC522_RX_GAIN_MASK);
mfrc522_writeu8(dev,
MFRC522_RF_CFG_REG, mask & MFRC522_RX_GAIN_MASK);
}
}
* Name: mfrc522_mifare_read
****************************************************************************/
int mfrc522_mifare_read(FAR struct mfrc522_dev_s *dev,
FAR struct mifare_tag_data_s *data)
{
uint8_t buffer[18];
uint8_t command[4];
uint8_t length = 18;
uint8_t validbits = 0;
int ret = OK;
command[0] = PICC_CMD_MF_READ;
command[1] = data->address;
ret = mfrc522_calc_crc(dev, command, 2, &command[2]);
if (ret != OK)
{
goto errout;
}
* We read back 16 bytes block data nad 2 bytes CRC.
*/
ret = mfrc522_transcv_data(dev, command, 4, buffer, &length,
&validbits, 0, false);
if (ret < 0)
{
goto errout;
}
memcpy(data->data, buffer, 16);
errout:
return ret;
}
* Name: mfrc522_init
*
* Description:
* Initializes the MFRC522 chip
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
void mfrc522_init(FAR struct mfrc522_dev_s *dev)
{
mfrc522_softreset(dev);
* something goes wrong. f_timer = 13.56 MHz / (2*TPreScaler+1) where:
* TPreScaler = [TPrescaler_Hi:Tprescaler_Lo]. Tprescaler_Hi are the four
* low bits in TmodeReg. Tprescaler_Lo is on TPrescalerReg.
*
* TAuto=1; timer starts automatically at the end of the transmission in
* all communication modes at all speeds.
*/
mfrc522_writeu8(dev, MFRC522_TMODE_REG, MFRC522_TAUTO);
* f_timer=40kHz, then the timer period will be 25us.
*/
mfrc522_writeu8(dev, MFRC522_TPRESCALER_REG, 0xa9);
mfrc522_writeu8(dev, MFRC522_TRELOAD_REGH, 0x06);
mfrc522_writeu8(dev, MFRC522_TRELOAD_REGL, 0xe8);
mfrc522_writeu8(dev, MFRC522_TX_ASK_REG, MFRC522_FORCE_100ASK);
mfrc522_writeu8(dev, MFRC522_MODE_REG, 0x3d);
mfrc522_enableantenna(dev);
}
* Name: mfrc522_selftest
*
* Description:
* Executes a self-test of the MFRC522 chip
*
* See 16.1.1 in the MFRC522 datasheet
*
* Input Parameters: a pointer to mfrc522_dev_s structure
*
* Returned Value: none
*
****************************************************************************/
int mfrc522_selftest(FAR struct mfrc522_dev_s *dev)
{
uint8_t zeros[25] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0
};
char outbuf[3 * 8 + 1];
uint8_t result[64];
int i;
int j;
int k;
mfrc522_softreset(dev);
mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER);
mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, zeros, 25);
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_MEM_CMD);
mfrc522_writeu8(dev, MFRC522_AUTOTEST_REG, MFRC522_SELFTEST_EN);
mfrc522_writeu8(dev, MFRC522_FIFO_DATA_REG, 0x00);
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_CALC_CRC_CMD);
for (i = 0; i < 255; i++)
{
uint8_t n;
n = mfrc522_readu8(dev, MFRC522_DIV_IRQ_REG);
if (n & MFRC522_CRC_IRQ)
{
break;
}
}
mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD);
mfrc522_readblk(dev, MFRC522_FIFO_DATA_REG, result, 64, 0);
mfrc522_writeu8(dev, MFRC522_AUTOTEST_REG, 0x00);
ctlsinfo("Self Test Result:\n");
for (i = 0; i < 64; i += 8)
{
for (j = 0, k = 0; j < 8; j++, k += 3)
{
snprintf(&outbuf[k], sizeof(outbuf) - k, " %02x", result[i + j]);
}
ctlsinfo(" %02x:%s\n", i, outbuf);
}
ctlsinfo("Done!\n");
return OK;
}
* Name: mfrc522_open
*
* Description:
* This function is called whenever the MFRC522 device is opened.
*
****************************************************************************/
static int mfrc522_open(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct mfrc522_dev_s *dev;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
mfrc522_configspi(dev->spi);
nxsig_usleep(10000);
mfrc522_getfwversion(dev);
dev->state = MFRC522_STATE_IDLE;
return OK;
}
* Name: mfrc522_close
*
* Description:
* This routine is called when the MFRC522 device is closed.
*
****************************************************************************/
static int mfrc522_close(FAR struct file *filep)
{
FAR struct inode *inode;
FAR struct mfrc522_dev_s *dev;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
dev->state = MFRC522_STATE_NOT_INIT;
return OK;
}
* Name: mfrc522_read
*
* Description:
* This routine is called when the device is read.
*
* Returns TAG id as string to buffer.
* or -EIO if no TAG found
*
****************************************************************************/
static ssize_t mfrc522_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct mfrc522_dev_s *dev;
FAR struct picc_uid_s uid;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
if (!mfrc522_picc_detect(dev))
{
ctlserr("Card is not present!\n");
return -EAGAIN;
}
mfrc522_picc_select(dev, &uid, 0);
if (uid.sak != PICC_TYPE_NOT_COMPLETE)
{
if (buffer)
{
snprintf(buffer, buflen, "0x%02X%02X%02X%02X",
uid.uid_data[0], uid.uid_data[1],
uid.uid_data[2], uid.uid_data[3]);
return buflen;
}
}
return OK;
}
* Name: mfrc522_write
****************************************************************************/
static ssize_t mfrc522_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode;
FAR struct mfrc522_dev_s *dev;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
UNUSED(dev);
return -ENOSYS;
}
* Name: mfrc522_ioctl
****************************************************************************/
static int mfrc522_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode;
FAR struct mfrc522_dev_s *dev;
int ret = OK;
inode = filep->f_inode;
DEBUGASSERT(inode->i_private);
dev = inode->i_private;
switch (cmd)
{
case MFRC522IOC_GET_PICC_UID:
{
FAR struct picc_uid_s *uid = (FAR struct picc_uid_s *)arg;
ret = mfrc522_picc_detect(dev);
if (ret < 0)
{
goto errout;
}
ret = mfrc522_picc_select(dev, uid, 0);
if (ret < 0)
{
goto errout;
}
break;
}
case CLIOC_READ_MIFARE_DATA:
{
FAR struct mifare_tag_data_s *data =
(struct mifare_tag_data_s *)arg;
*
* TODO: authentication for MIFARE Classic.
* Without authentication this will works only for MIFARE
* Ultralight.
*/
ret = mfrc522_mifare_read(dev, data);
break;
}
case MFRC522IOC_GET_STATE:
{
ret = dev->state;
break;
}
default:
{
ctlserr("ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
}
errout:
return ret;
}
* Public Functions
****************************************************************************/
* Name: mfrc522_register
*
* Description:
* Register the MFRC522 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register.
* E.g., "/dev/rfid0"
* spi - An instance of the SPI interface to use to communicate with
* MFRC522.
* config - chip config
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int mfrc522_register(FAR const char *devpath, FAR struct spi_dev_s *spi)
{
FAR struct mfrc522_dev_s *dev;
uint8_t fwver;
int ret = 0;
dev = kmm_malloc(sizeof(struct mfrc522_dev_s));
if (!dev)
{
ctlserr("ERROR: Failed to allocate instance\n");
return -ENOMEM;
}
dev->spi = spi;
dev->state = MFRC522_STATE_NOT_INIT;
#if defined CONFIG_PM
dev->pm_level = PM_IDLE;
#endif
mfrc522_init(dev);
dev->state = MFRC522_STATE_IDLE;
fwver = mfrc522_getfwversion(dev);
ctlsinfo("MFRC522 Firmware Version: 0x%02X!\n", fwver);
if (fwver != 0x90 && fwver != 0x91 && fwver != 0x92 && fwver != 0x88)
{
ctlserr("None supported device detected!\n");
goto firmware_error;
}
ret = register_driver(devpath, &g_mfrc522fops, 0666, dev);
if (ret < 0)
{
ctlserr("ERROR: Failed to register driver: %d\n", ret);
kmm_free(dev);
}
return ret;
firmware_error:
kmm_free(dev);
return -ENODEV;
}