diff --git a/drivers/contactless/mfrc522.c b/drivers/contactless/mfrc522.c new file mode 100644 index 0000000000..5148aeac93 --- /dev/null +++ b/drivers/contactless/mfrc522.c @@ -0,0 +1,1616 @@ +/**************************************************************************** + * drivers/contactless/mfrc522.c + * + * Copyright(C) 2016 Uniquix Ltda. All rights reserved. + * Author: Alan Carvalho de Assis + * + * This driver is based on Arduino library for MFRC522 from Miguel + * Balboa released into the public domain: + * https://github.com/miguelbalboa/rfid/ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mfrc522.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef CONFIG_CL_MFRC522_DEBUG +# define mfrc522err _err +# define mfrc522info _info +#else +# ifdef CONFIG_CPP_HAVE_VARARGS +# define mfrc522err(x...) +# define mfrc522info(x...) +# else +# define mfrc522err (void) +# define mfrc522info (void) +# endif +#endif + +#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); + +/* Character driver methods */ + +static int mfrc522_open(FAR struct file *filep); +static int mfrc522_close(FAR struct file *filep); +static ssize_t mfrc522_read(FAR struct file *, FAR char *, size_t); +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); + +/* IRQ Handling TODO: +static int mfrc522_irqhandler(FAR int irq, FAR void *context, FAR void* dev); +static inline int mfrc522_attachirq(FAR struct mfrc522_dev_s *dev, xcpt_t isr); +*/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_mfrc522fops = +{ + mfrc522_open, + mfrc522_close, + mfrc522_read, + mfrc522_write, + 0, + mfrc522_ioctl +#ifndef CONFIG_DISABLE_POLL + , 0 +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , 0 +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void mfrc522_lock(FAR struct spi_dev_s *spi) +{ + (void)SPI_LOCK(spi, true); + + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + (void)SPI_HWFEATURES(spi, 0); + (void)SPI_SETFREQUENCY(spi, CONFIG_CL_MFRC522_SPI_FREQ); +} + +static void mfrc522_unlock(FAR struct spi_dev_s *spi) +{ + (void)SPI_LOCK(spi, false); +} + +static inline void mfrc522_configspi(FAR struct spi_dev_s *spi) +{ + /* Configure SPI for the MFRC522 module. */ + + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + (void)SPI_HWFEATURES(spi, 0); + (void)SPI_SETFREQUENCY(spi, CONFIG_CL_MFRC522_SPI_FREQ); +} + +static inline void mfrc522_select(struct mfrc522_dev_s *dev) +{ + SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS, true); +} + +static inline void mfrc522_deselect(struct mfrc522_dev_s *dev) +{ + SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS, 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", regval, 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); + + /* Inform the MFRC522 the address we want to read */ + + 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); + } + + /* Read the first byte */ + + value = SPI_SEND(dev->spi, address); + + /* Apply mask to current regval[0] with the read value */ + + regval[0] = (regval[0] & ~mask) | (value & mask); + } + else + { + /* Read the remaining bytes */ + + regval[i] = SPI_SEND(dev->spi, address); + } + i++; + } + + /* Read the last byte. 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, size); +} + +/**************************************************************************** + * 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); + + /* Inform the MFRC522 the address we want write to */ + + SPI_SEND(dev->spi, address); + + /* Send the block of bytes */ + + SPI_SNDBLOCK(dev->spi, regval, length); + + mfrc522_deselect(dev); + mfrc522_unlock(dev->spi); + + tracerx("writeblk", regval, size); +} + +/**************************************************************************** + * 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; + + /* Stop any command in execution */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD); + + /* Clear the CRCIRq interrupt request bit */ + + mfrc522_writeu8(dev, MFRC522_DIV_IRQ_REG, MFRC522_CRC_IRQ); + + /* Flush all bytes in the FIFO */ + + mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER); + + /* Write data to the FIFO */ + + mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, buffer, length); + + /* Start the calculation */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_CALC_CRC_CMD); + + /* Wait for CRC completion or 200ms time-out */ + + clock_gettime(CLOCK_REALTIME, &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; + } + + /* Get time now */ + + clock_gettime(CLOCK_REALTIME, &tend); + + if ((tend.tv_sec > tstart.tv_sec) && (tend.tv_nsec > tstart.tv_nsec)) + { + return -ETIMEDOUT; + } + } + + /* Stop calculating CRC for new content of FIFO */ + + 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; + + /* Prepare values for BitFramingReg */ + + uint8_t txlastbits = validbits ? *validbits : 0; + uint8_t bitframing = (rxalign << 4) + txlastbits; + + /* Stop any active command */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD); + + /* Clear all seven interrupt request bits */ + + value = mfrc522_readu8(dev, MFRC522_COM_IRQ_REG); + mfrc522_writeu8(dev, MFRC522_COM_IRQ_REG, value | MFRC522_COM_IRQ_MASK); + + /* Flush all bytes in the FIFO */ + + mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER); + + /* Write data to FIFO */ + + mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, send_data, send_len); + + /* Bit adjustments */ + + mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG, bitframing); + + /* Execute command */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, command); + + /* We setup the TAuto flag in the mfrc522_init() then we could use the + * internal MFC522 Timer to detect timeout, but because there could be some + * hardware fault, let us to use a NuttX timeout as well. + */ + + clock_gettime(CLOCK_REALTIME, &tstart); + tstart.tv_nsec += 200000; + if (tstart.tv_nsec >= 1000 * 1000 * 1000) + { + tstart.tv_sec++; + tstart.tv_nsec -= 1000 * 1000 * 1000; + } + + /* If it is a Transceive command, then start transmittion */ + + if (command == MFRC522_TRANSCV_CMD) + { + value = mfrc522_readu8(dev, MFRC522_BIT_FRAMING_REG); + mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG, value | MFRC522_START_SEND); + } + + /* Wait for the command to complete */ + + while (1) + { + uint8_t irqsreg; + + irqsreg = mfrc522_readu8(dev, MFRC522_COM_IRQ_REG); + + /* If at least an of selected IRQ happened */ + + if (irqsreg & waitirq) + { + break; + } + + /* Timer expired */ + + if (irqsreg & MFRC522_TIMER_IRQ) + { + return -ETIMEDOUT; + } + + /* Get time now */ + + clock_gettime(CLOCK_REALTIME, &tend); + + if ((tend.tv_sec > tstart.tv_sec) && (tend.tv_nsec > tstart.tv_nsec)) + { + return -ETIMEDOUT; + } + } + + /* Read error register to verify if there are any issue */ + + errors = mfrc522_readu8(dev, MFRC522_ERROR_REG); + + /* Check for Protocol error */ + + if (errors & (MFRC522_PROTO_ERR)) + { + return -EPROTO; + } + + /* Check for Parity and Buffer Overflow errors */ + + if (errors & (MFRC522_PARITY_ERR | MFRC522_BUF_OVFL_ERR)) + { + return -EIO; + } + + /* Check collision error */ + + if (errors & MFRC522_COLL_ERR) + { + return -EBUSY; /* should it be EAGAIN ? */ + } + + /* If the caller wants data back, get it from the MFRC522 */ + + if (back_data && back_len) + { + uint8_t nbytes; + + /* Number of bytes in the FIFO */ + + nbytes = mfrc522_readu8(dev, MFRC522_FIFO_LEVEL_REG); + + /* Returned more bytes than the expected */ + + if (nbytes > *back_len) + { + return -ENOMEM; + } + + *back_len = nbytes; + + /* Read the data from FIFO */ + + mfrc522_readblk(dev, MFRC522_FIFO_DATA_REG, back_data, nbytes, rxalign); + + /* RxLastBits[2:0] indicates the number of valid bits received */ + + vbits = mfrc522_readu8(dev, MFRC522_CONTROL_REG) + & MFRC522_RX_LAST_BITS_MASK; + + if (validbits) + { + *validbits = vbits; + } + } + + /* Perform CRC_A validation if requested */ + + if (back_data && back_len && checkcrc) + { + uint8_t ctrlbuf[2]; + + /* In this case a MIFARE Classic NAK is not OK */ + + if (*back_len == 1 && vbits == 4) + { + return -EACCES; + } + + /* We need the CRC_A value or all 8 bits of the last byte */ + + if (*back_len < 2 || vbits != 0) + { + return -EPERM; + } + + /* Verify CRC_A */ + + 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; + } + + /* Force clear of received bits if a collision is detected */ + + 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); + + /* For REQA and WUPA we need to transmit only 7 bits */ + + if (status != OK) + { + return status; + } + + /* ATQA must be exactly 16 bits */ + + if (length != 2 || validbits != 0) + { + return -EAGAIN; + } + + mfrc522info("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); + + /* Send a REQA command */ + + 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; + + /* The first index in uid->data[] that is used in the current Cascade Level */ + + uint8_t uid_index; + + /* The number of known UID bits in the current Cascade Level. */ + + uint8_t curr_level_known_bits; + + /* The SELECT/ANTICOLLISION uses a 7 byte standard frame + 2 bytes CRC_A */ + + uint8_t buffer[9]; + + /* The number of bytes used in the buffer, number bytes on FIFO */ + + uint8_t buffer_used; + + /* Used to defines the bit position for the first bit received */ + + uint8_t rxalign; + + /* The number of valid bits in the last transmitted byte. */ + + uint8_t txlastbits; + + uint8_t *resp_buf; + uint8_t resp_len; + + /* Sanity check */ + + if (validbits > 80) + { + return -EINVAL; + } + + /* Force clear of received bits if a collision is detected */ + + value = mfrc522_readu8(dev, MFRC522_COLL_REG); + mfrc522_writeu8(dev, MFRC522_COLL_REG, value & MFRC522_VALUES_AFTER_COLL); + + /* Repeat cascade level loop until we have a complete UID */ + + uid_complete = false; + while (!uid_complete) + { + uint8_t bytes_to_copy; + + /* Set the Cascade Level in the SEL byte, find out if we need to use the + * Cascade Tag in byte 2. + */ + + switch (cascade_level) + { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uid_index = 0; + + /* When we know that the UID has more than 4 bytes */ + + use_cascade_tag = validbits && (uid->size > 4); + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uid_index = 3; + + /* When we know that the UID has more than 7 bytes */ + + 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; /* Internal error */ + } + + /* How many UID bits are known in this Cascade Level? */ + + curr_level_known_bits = validbits - (8 * uid_index); + if (curr_level_known_bits < 0) + { + curr_level_known_bits = 0; + } + + /* Copy the known bits from uid->uid_data[] to buffer[] */ + + i = 2; /* destination index in buffer[] */ + if (use_cascade_tag) + { + buffer[i++] = PICC_CMD_CT; + } + + /* Number of bytes needed to represent the known bits for this level */ + + bytes_to_copy = curr_level_known_bits / 8 + + (curr_level_known_bits % 8 ? 1 : 0); + + if (bytes_to_copy) + { + /* Max 4 bytes in each Cascade Level. Only 3 left if we use the + * 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]; + } + } + + /* Now that the data has been copied we need to include the 8 bits in CT + * in curr_level_known_bits. + */ + + if (use_cascade_tag) + { + curr_level_known_bits += 8; + } + + /* Repeat anti collision loop until we can transmit all UID bits + BCC + * and receive a SAK - max 32 iterations. + */ + + select_done = false; + while (!select_done) + { + /* Find out how many bits and bytes to send and receive. */ + + if (curr_level_known_bits >= 32) + { + /* All UID bits in this Cascade Level are known. This is a + * SELECT. + */ + + /* NVB - Number of Valid Bits: Seven whole bytes */ + + buffer[1] = 0x70; + + /* Calculate BCC - Block Check Character */ + + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + + /* Calculate CRC_A */ + + result = mfrc522_calc_crc(dev, buffer, 7, &buffer[7]); + if (result != OK) + { + return result; + } + + txlastbits = 0; /* 0 => All 8 bits are valid. */ + buffer_used = 9; + + /* Store response in the last 3 bytes of buffer (BCC and CRC_A - + * not needed after tx). + */ + + resp_buf = &buffer[6]; + resp_len = 3; + } + else + { + /* This is an ANTICOLLISION */ + + txlastbits = curr_level_known_bits % 8; + + /* Number of whole bytes in the UID part. */ + + count = curr_level_known_bits / 8; + i = 2 + count; + + /* NVB - Number of Valid Bits */ + + buffer[1] = (i << 4) + txlastbits; + buffer_used = i + (txlastbits ? 1 : 0); + + /* Store response in the unused part of buffer */ + + resp_buf = &buffer[i]; + resp_len = sizeof(buffer) - i; + } + + /* Set bit adjustments */ + + rxalign = txlastbits; + mfrc522_writeu8(dev, MFRC522_BIT_FRAMING_REG, + (rxalign << 4) + txlastbits); + + /* Transmit the buffer and receive the response */ + + result = mfrc522_transcv_data(dev, buffer, buffer_used, resp_buf, + &resp_len, &txlastbits, rxalign, false); + + /* More than one PICC in the field => collision */ + + if (result == -EBUSY) + { + uint8_t coll_pos; + uint8_t coll_reg = mfrc522_readu8(dev, MFRC522_COLL_REG); + + /* CollPosNotValid */ + + if (coll_reg & 0x20) + { + /* Without a valid collision position we cannot continue */ + + return -EBUSY; + } + + coll_pos = coll_reg & 0x1F; /* Values 0-31, 0 means bit 32. */ + if (coll_pos == 0) + { + coll_pos = 32; + } + + if (coll_pos <= curr_level_known_bits) + { + /* No progress - should not happen */ + + return -EIO; + } + + /* Choose the PICC with the bit set. */ + + curr_level_known_bits = coll_pos; + + /* The bit to modify */ + + count = (curr_level_known_bits - 1) % 8; + + /* First byte is index 0. */ + + i = 1 + (curr_level_known_bits / 8) + (count ? 1 : 0); + buffer[i] |= (1 << count); + } + else if (result != OK) + { + return result; + } + else /* OK */ + { + /* This was a SELECT. */ + + if (curr_level_known_bits >= 32) + { + /* No more collision */ + + select_done = true; + } + else + { + /* This was an ANTICOLLISION. */ + /* We have all 32 bits of the UID in this Cascade Level */ + + curr_level_known_bits = 32; + + /* Run loop again to do the SELECT */ + } + } + } + + /* We do not check the CBB - it was constructed by us above. */ + /* Copy the found UID bytes from buffer[] to uid->uid_data[] */ + + i = (buffer[2] == PICC_CMD_CT) ? 3 : 2; /* source index in buffer[] */ + 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++]; + } + + /* Check response SAK (Select Acknowledge) */ + + if (resp_len != 3 || txlastbits != 0) + { + /* SAK must be exactly 24 bits (1 byte + CRC_A). */ + + return -EIO; + } + + /* Verify CRC_A - do our own calculation and store the control in + * buffer[2..3] - those bytes are not needed anymore. + */ + + result = mfrc522_calc_crc(dev, resp_buf, 1, &buffer[2]); + if (result != OK) + { + return result; + } + + /* Is it correct */ + + if ((buffer[2] != resp_buf[1]) || (buffer[3] != resp_buf[2])) + { + return -EINVAL; + } + + /* Cascade bit set - UID not complete yes */ + + if (resp_buf[0] & 0x04) + { + cascade_level++; + } + else + { + uid_complete = true; + uid->sak = resp_buf[0]; + } + } + + /* Set correct uid->size */ + + 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) +{ + /* Send a software reset command */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_SOFTRST_CMD); + + /* Wait the internal state machine to initialize */ + + usleep(50000); + + /* Wait for the PowerDown bit in COMMAND_REG to be cleared */ + + 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_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) +{ + /* Force a reset */ + + mfrc522_softreset(dev); + + /* We need a timeout if something when communicating with a TAG case + * 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); + + /* TPreScaler = TModeReg[3..0]:TPrescalerReg, ie: 0x0A9 = 169 => + * f_timer=40kHz, then the timer period will be 25us. + */ + + mfrc522_writeu8(dev, MFRC522_TPRESCALER_REG, 0xA9); + + /* Reload timer with 0x3E8 = 1000, ie 25ms before timeout. */ + + mfrc522_writeu8(dev, MFRC522_TRELOAD_REGH, 0x06); + mfrc522_writeu8(dev, MFRC522_TRELOAD_REGL, 0xE8); + + /* Force 100% ASK modulation independent of the ModGsPReg setting */ + + mfrc522_writeu8(dev, MFRC522_TX_ASK_REG, MFRC522_FORCE_100ASK); + + /* Set the preset value for the CRC to 0x6363 (ISO 14443-3 part 6.2.4) */ + + mfrc522_writeu8(dev, MFRC522_MODE_REG, 0x3D); + + /* Enable the Antenna pins */ + + 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 i; + uint8_t result[64]; + 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}; + + /* Execute a software reset */ + + mfrc522_softreset(dev); + + /* Flush the FIFO buffer */ + + mfrc522_writeu8(dev, MFRC522_FIFO_LEVEL_REG, MFRC522_FLUSH_BUFFER); + + /* Clear the internal buffer by writing 25 bytes 0x00 */ + + mfrc522_writeblk(dev, MFRC522_FIFO_DATA_REG, zeros, 25); + + /* Transfer to internal buffer */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_MEM_CMD); + + /* Enable self-test */ + + mfrc522_writeu8(dev, MFRC522_AUTOTEST_REG, MFRC522_SELFTEST_EN); + + /* Write 0x00 to FIFO buffer */ + + mfrc522_writeu8(dev, MFRC522_FIFO_DATA_REG, 0x00); + + /* Start self-test by issuing the CalcCRC command */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_CALC_CRC_CMD); + + /* Wait for self-test to complete */ + + for (i = 0; i < 255; i++) + { + uint8_t n; + + n = mfrc522_readu8(dev, MFRC522_DIV_IRQ_REG); + if (n & MFRC522_CRC_IRQ) + { + break; + } + } + + /* Stop calculating CRC for new content in the FIFO */ + + mfrc522_writeu8(dev, MFRC522_COMMAND_REG, MFRC522_IDLE_CMD); + + /* Read out the 64 bytes result from the FIFO buffer */ + + mfrc522_readblk(dev, MFRC522_FIFO_DATA_REG, result, 64, 0); + + /* Self-test done. Reset AutoTestReg register to normal operation */ + + mfrc522_writeu8(dev, MFRC522_AUTOTEST_REG, 0x00); + + mfrc522info("Self Test Result:\n"); + for (i = 1; i <= 64; i++) + { + printf("0x%02X ", result[i - 1]); + + if ((i % 8) == 0) + { + printf("\n"); + } + } + + mfrc522info("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; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + mfrc522_configspi(dev->spi); + + 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; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && 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; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + /* Is a card near? */ + + if (!mfrc522_picc_detect(dev)) + { + mfrc522err("Card is not present!\n"); + return -EAGAIN; + } + + /* Now read the UID */ + + mfrc522_picc_select(dev, &uid, 0); + + if (uid.sak != 0) + { + 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; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + (void)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; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + switch (cmd) + { + case MFRC522IOC_GET_PICC_UID: + { + struct picc_uid_s *uid = (struct picc_uid_s *)arg; + + /* Is a card near? */ + + if (mfrc522_picc_detect(dev)) + { + ret = mfrc522_picc_select(dev, uid, 0); + } + } + break; + + case MFRC522IOC_GET_STATE: + ret = dev->state; + break; + + default: + mfrc522err("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + + 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; + + /* Initialize the MFRC522 device structure */ + + dev = (FAR struct mfrc522_dev_s *)kmm_malloc(sizeof(struct mfrc522_dev_s)); + if (!dev) + { + mfrc522err("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + dev->spi = spi; + + /* Device is not initialized yet */ + + dev->state = MFRC522_STATE_NOT_INIT; + +#if defined CONFIG_PM + dev->pm_level = PM_IDLE; +#endif + + /* mfrc522_attachirq(dev, mfrc522_irqhandler); */ + + /* Initialize the MFRC522 */ + + mfrc522_init(dev); + + /* Device initialized and idle */ + + dev->state = MFRC522_STATE_IDLE; + + /* Read the Firmware Version */ + + fwver = mfrc522_getfwversion(dev); + + mfrc522info("MFRC522 Firmware Version: 0x%02X!\n", fwver); + + /* If returned firmware version is unknown don't register the device */ + + if (fwver != 0x90 && fwver != 0x91 && fwver != 0x92 && fwver != 0x88 ) + { + mfrc522err("None supported device detected!\n"); + goto firmware_error; + } + + /* Register the character driver */ + + ret = register_driver(devpath, &g_mfrc522fops, 0666, dev); + if (ret < 0) + { + mfrc522err("ERROR: Failed to register driver: %d\n", ret); + kmm_free(dev); + } + + return ret; + +firmware_error: + kmm_free(dev); + return -ENODEV; +} diff --git a/drivers/contactless/mfrc522.h b/drivers/contactless/mfrc522.h new file mode 100644 index 0000000000..9bc3903fd4 --- /dev/null +++ b/drivers/contactless/mfrc522.h @@ -0,0 +1,429 @@ +/**************************************************************************** + * drivers/contactless/mfrc522.h + * + * Copyright(C) 2016 Uniquix Ltda. All rights reserved. + * Authors: Alan Carvalho de Assis + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __DRIVERS_CONTACTLESS_MFRC522_H +#define __DRIVERS_CONTACTLESS_MFRC522_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +/* The commands used by the PCD to manage communication with several PICCs + * (ISO 14443-3, Type A, section 6.4) + */ + +#define PICC_CMD_REQA 0x26 /* REQuest command, Type A */ +#define PICC_CMD_WUPA 0x52 /* Wake-UP command, Type A */ +#define PICC_CMD_CT 0x88 /* Cascade Tag, used during anti collision. */ +#define PICC_CMD_SEL_CL1 0x93 /* Anti collision/Select, Cascade Level 1 */ +#define PICC_CMD_SEL_CL2 0x95 /* Anti collision/Select, Cascade Level 2 */ +#define PICC_CMD_SEL_CL3 0x97 /* Anti collision/Select, Cascade Level 3 */ +#define PICC_CMD_HLTA 0x50 /* HaLT command, Type A */ + +/* The commands used for MIFARE Classic + * (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) + * Use PCD_MFAuthent to authenticate access to a sector, then use these + * commands to read/write/modify the blocks on the sector. + * The read/write commands can also be used for MIFARE Ultralight. + */ + +#define PICC_CMD_MF_AUTH_KEY_A 0x60 /* Perform authentication with Key A */ +#define PICC_CMD_MF_AUTH_KEY_B 0x61 /* Perform authentication with Key B */ +#define PICC_CMD_MF_READ 0x30 /* Reads one 16 byte block from auth sector */ +#define PICC_CMD_MF_WRITE 0xA0 /* Writes one 16 byte block to auth senctor */ +#define PICC_CMD_MF_DECREMENT 0xC0 /* Decrements contents of a block */ +#define PICC_CMD_MF_INCREMENT 0xC1 /* Increments contents of a block */ +#define PICC_CMD_MF_RESTORE 0xC2 /* Reads the contents of a block */ +#define PICC_CMD_MF_TRANSFER 0xB0 /* Writes the contents of a block */ + +/* The commands used for MIFARE Ultralight + * (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + * The PICC_CMD_MF_READ/_MF_WRITE can also be used for MIFARE Ultralight. + */ + +#define PICC_CMD_UL_WRITE 0xA2 /* Writes one 4 byte page to the PICC. */ + +/* MFRC522 Registers */ + +/* NOTE: All SPI addresses are shifted one bit left in the SPI address byte + * See section 8.1.2.3 from MFRC522 datasheet + */ + +/* Page 0: Commands and status */ + /* 0x00 - reserved for future use */ +#define MFRC522_COMMAND_REG (0x01 << 1) /* starts/stops command execution */ +#define MFRC522_COM_IEN_REG (0x02 << 1) /* dis/enable int. req. ctrl bits */ +#define MFRC522_DIV_IEN_REG (0x03 << 1) /* dis/enable int. req. ctrl bits */ +#define MFRC522_COM_IRQ_REG (0x04 << 1) /* interrupt request bits */ +#define MFRC522_DIV_IRQ_REG (0x05 << 1) /* interrupt request bits */ +#define MFRC522_ERROR_REG (0x06 << 1) /* error bits status of last cmd */ +#define MFRC522_STATUS1_REG (0x07 << 1) /* communication status bits */ +#define MFRC522_STATUS2_REG (0x08 << 1) /* rcvr and transmitter status */ +#define MFRC522_FIFO_DATA_REG (0x09 << 1) /* input/output of FIFO buffer */ +#define MFRC522_FIFO_LEVEL_REG (0x0A << 1) /* number of bytes stored in the FIFO */ +#define MFRC522_WATER_LEVEL_REG (0x0B << 1) /* level for FIFO under/overflow */ +#define MFRC522_CONTROL_REG (0x0C << 1) /* miscellaneos control register */ +#define MFRC522_BIT_FRAMING_REG (0x0D << 1) /* adjustments for bit-oriented frames */ +#define MFRC522_COLL_REG (0x0E << 1) /* bit position of first bit-collision detected */ + /* 0x0F - reserved for future use */ +/* Page 1: Commands */ + /* 0x10 - reserved for future use */ +#define MFRC522_MODE_REG (0x11 << 1) /* defines general modes for transmit/receive */ +#define MFRC522_TX_MODE_REG (0x12 << 1) /* defines transmission data rate and framing */ +#define MFRC522_RX_MODE_REG (0x13 << 1) /* defines reception data rate and framing */ +#define MFRC522_TX_CTRL_REG (0x14 << 1) /* controls antenna driver pins TX1 and TX2 */ +#define MFRC522_TX_ASK_REG (0x15 << 1) /* controls the setting of transmission modulation */ +#define MFRC522_TX_SEL_REG (0x16 << 1) /* selects the internal sources for antenna driver */ +#define MFRC522_RX_SEL_REG (0x17 << 1) /* selects the internal receiver settings */ +#define MFRC522_RX_THLD_REG (0x18 << 1) /* selects the thresholds for bit decoder */ +#define MFRC522_DEMOD_REG (0x19 << 1) /* defines demodulator settings */ + /* 0x1A - reserved for future use */ + /* 0x1B - reserved for future use */ +#define MFRC522_MF_TX_REG (0x1C << 1) /* controls some MIFARE comm tx param */ +#define MFRC522_MF_RX_REG (0x1D << 1) /* controls some MIFARE comm rx param */ + /* 0x1E - reserved for future use */ +#define MFRC522_SERIAL_SPD_REG (0x1F << 1) /* selects the speed of the serial UART */ + +/* Page 2: Configuration */ + /* 0x20 - reserved for future use */ +#define MFRC522_CRC_RESULT_REGH (0x21 << 1) /* shows the MSB values of CRC calc. */ +#define MFRC522_CRC_RESULT_REGL (0x22 << 1) /* shows the LSB values of CRC calc. */ + /* 0x23 - reserved for future use */ +#define MFRC522_MOD_WIDTH_REG (0x24 << 1) /* controls the ModWidth setting */ + /* 0x25 - reserved for future use */ +#define MFRC522_RF_CFG_REG (0x26 << 1) /* configures the receiver gain */ +#define MFRC522_GSN_REG (0x27 << 1) /* selects the conductance of n-driver TX1/2 */ +#define MFRC522_CW_GSP_REG (0x28 << 1) /* defines the conductance of p-driver during no modulation */ +#define MFRC522_MOD_GSP_REG (0x29 << 1) /* defines the conductance of p-driver during modulation */ +#define MFRC522_TMODE_REG (0x2A << 1) /* defines settings for the internal timer */ +#define MFRC522_TPRESCALER_REG (0x2B << 1) /* the lower 8 bits of TPrescaler value */ +#define MFRC522_TRELOAD_REGH (0x2C << 1) /* defines the 16-bit timer reload value */ +#define MFRC522_TRELOAD_REGL (0x2D << 1) /* defines the 16-bit timer reload value */ +#define MFRC522_TCOUNT_VAL_REGH (0x2E << 1) /* shows the 16-bit timer value */ +#define MFRC522_TCOUNT_VAL_REGL (0x2F << 1) /* shows the 16-bit timer value */ + +/* Page 3: Test Registers */ + /* 0x30 - reserved for future use */ +#define MFRC522_TEST_SEL1_REG (0x31 << 1) /* general test signal configuration */ +#define MFRC522_TEST_SEL2_REG (0x32 << 1) /* general test signal configuration */ +#define MFRC522_TEST_PIN_EN_REG (0x33 << 1) /* enables pin output driver on pins D1 to D7 */ +#define MFRC522_TEST_PIN_VAL_REG (0x34 << 1) /* defines the values to D1 to D7 */ +#define MFRC522_TEST_BUS_REG (0x35 << 1) /* shows the status of the internal test bus */ +#define MFRC522_AUTOTEST_REG (0x36 << 1) /* controls the digital self test */ +#define MFRC522_VERSION_REG (0x37 << 1) /* shows the software version */ +#define MFRC522_ANALOG_TEST_REG (0x38 << 1) /* controls the pins AUX1 and AUX2 */ +#define MFRC522_TEST_DAC1_REG (0x39 << 1) /* defines the test value for TestDAC1 */ +#define MFRC522_TEST_DAC2_REG (0x3A << 1) /* defines the test value for TestDAC2 */ +#define MFRC522_TEST_ADC_REG (0x3B << 1) /* show the value of ADC I and Q channels */ + +/* Section 9.3.1.2: MFRC522 Command Register */ + +#define MFRC522_CMD_MASK 0x0F +# define MFRC522_IDLE_CMD 0x00 /* no action, cancels current command execution */ +# define MFRC522_MEM_CMD 0x01 /* stores 25 bytes into the internal buffer */ +# define MFRC522_GEN_RND_ID_CMD 0x02 /* generates a 10-bytes random ID number */ +# define MFRC522_CALC_CRC_CMD 0x03 /* activates the CRC coprocessor or self test */ +# define MFRC522_TRANSMIT_CMD 0x04 /* transmits data from the FIFO buffer */ +# define MFRC522_NO_CHANGE_CMD 0x07 /* no command change, used to modify CommandReg */ +# define MFRC522_RECEIVE_CMD 0x08 /* activates the receiver circuits */ +# define MFRC522_TRANSCV_CMD 0x0C /* transmits data from FIFO and receive automatically */ +# define MFRC522_MF_AUTH_CMD 0x0E /* performs the MIFARE stand authentication as a reader */ +# define MFRC522_SOFTRST_CMD 0x0F /* resets the MFRC522 */ +#define MFRC522_POWER_DOWN (1 << 4) /* soft power-down mode entered */ +#define MFRC522_RCV_OFF (1 << 5) /* turns off analog part of receiver */ + +/* Section 9.3.1.3: ComIEnReg register */ + +#define MFRC522_TIMER_IEN (1 << 0) /* allows the timer interrupt request on pin IRQ */ +#define MFRC522_ERR_IEN (1 << 1) /* allows the error interrupt request on pin IRQ */ +#define MFRC522_LO_ALERT_IEN (1 << 2) /* allows the low alert interrupt request on pin IRQ */ +#define MFRC522_HI_ALERT_IEN (1 << 3) /* allows the high alert interrupt request on pin IRQ */ +#define MFRC522_IDLE_IEN (1 << 4) /* allows the idle interrupt request on pin IRQ */ +#define MFRC522_RX_IEN (1 << 5) /* allows the receiver interrupt request on pin IRQ */ +#define MFRC522_TX_IEN (1 << 6) /* allows the transmitter interrupt request on pin IRQ */ +#define MFRC522_IRQ_INV (1 << 7) /* signal on pin IRQ is inverse of IRq bit from Status1Reg */ + +/* Section 9.3.1.4: DivIEnReg register */ + +#define MFRC522_CRC_IEN (1 << 2) /* allows the CRC interrupt request on pin IRQ */ +#define MFRC522_MFIN_ACT_IEN (1 << 4) /* allows the MFIN active interrupt request on pin IRQ */ +#define MFRC522_IRQ_PUSH_PULL (1 << 7) /* 1 = IRQ pin is a standard CMOS output pin, 0 = open-drain */ + +/* Section 9.3.1.5: ComIrqReg register */ + +#define MFRC522_COM_IRQ_MASK (0x7F) +#define MFRC522_TIMER_IRQ (1 << 0) /* enabled when TCounterValReg reaches value 0 */ +#define MFRC522_ERR_IRQ (1 << 1) /* any error bit in the ErrorReg register is set */ +#define MFRC522_LO_ALERT_IRQ (1 << 2) /* Status1Reg register’s LoAlert bit is set */ +#define MFRC522_HI_ALERT_IRQ (1 << 3) /* Status1Reg register’s HiAlert bit is set */ +#define MFRC522_IDLE_IRQ (1 << 4) /* if a command terminates this bit is set */ +#define MFRC522_RX_IRQ (1 << 5) /* receiver has detected the end of a valid data stream */ +#define MFRC522_TX_IRQ (1 << 6) /* set immediately after the last data bit was transmitted */ +#define MFRC522_SET1 (1 << 7) /* indicate the status of ComIrqReg bits */ + +/* Section 9.3.1.6: DivIrqReg register */ + +#define MFRC522_CRC_IRQ (1 << 2) /* the CalcCRC command is active and all data is processed */ +#define MFRC522_MFIN_ACT_IRQ (1 << 4) /* MFIN is active, int is set on rising/falling signal edge */ +#define MFRC522_SET2 (1 << 7) /* indicates the status of the marked bits in the DivIrqReg */ + +/* Section 9.3.1.7: ErrorReg register */ + +#define MFRC522_PROTO_ERR (1 << 0) /* set if the SOF is incorrect or during MFAuthent if data is incorrect */ +#define MFRC522_PARITY_ERR (1 << 1) /* parity check failed */ +#define MFRC522_CRC_ERR (1 << 2) /* the RxCRCEn bit is set and the CRC calculation fails */ +#define MFRC522_COLL_ERR (1 << 3) /* a bit-collision is detected */ +#define MFRC522_BUF_OVFL_ERR (1 << 4) /* FIFO is full and the host or internal state machine try to write data */ +#define MFRC522_TEMP_ERR (1 << 6) /* internal temperature sensor detects overheating */ +#define MFRC522_WR_ERR (1 << 7) /* data write error in the FIFO, host writing to FIFO at the wrong time */ + +/* Section 9.3.1.8: Status1Reg register */ + +#define MFRC522_LO_ALERT (1 << 0) /* number of bytes on FIFO lower than the water-mark */ +#define MFRC522_HI_ALERT (1 << 1) /* number of bytes on FIFO higher than the water-mark */ +#define MFRC522_TRUNNING (1 << 3) /* timer is running */ +#define MFRC522_IRQ (1 << 4) /* indicates if any interrupt source requests attention */ +#define MFRC522_CRC_READY (1 << 5) /* the CRC calculation has finished */ +#define MFRC522_CRC_OK (1 << 6) /* when the calculation is done correctly this bit change to 1 */ + +/* Section 9.3.1.9: Status2Reg register */ + +#define MFRC522_MODEM_STATE_MASK (7 << 0) /* shows the state of the transmitter and receiver state machine */ +#define MFRC522_MODEM_IDLE (0) /* idle */ +#define MFRC522_MODEM_WAIT_BFR (1) /* wait for the BitFramingReg register’s StartSend bit */ +#define MFRC522_MODEM_TXWAIT (2) /* wait until RF field is present if TxWaitRF bit is set to 1 */ +#define MFRC522_MODEM_TXING (3) /* transmitting */ +#define MFRC522_MODEM_RXWAIT (4) /* wait until RF field is present if TxWaitRF bit is set to 1 */ +#define MFRC522_MODEM_WAIT_DATA (5) /* wait for data */ +#define MFRC522_MODEM_RXING (6) /* receiving */ +#define MFRC522_MF_CRYPTO1_ON (1 << 3) /* MIFARE Crypto1 unit is switched on */ +#define MFRC522_I2C_FORCE_HS (1 << 6) /* set the I2C to high-speed mode (R/W bit) */ +#define MFRC522_TEMP_SENS_CLEAR (1 << 7) /* clears the temperature error if it is below 125C (R/W bit) */ + +/* Section 9.3.1.10: FIFODataReg register */ + +#define MFRC522_FIFO_DATA_MASK (0xFF) /* Input and output of 64 byte FIFO buffer */ + +/* Section 9.3.1.11: FIFOLevelReg register */ + +#define MFRC522_FIFOLEVEL_MASK (0x7F) /* indicates the number of bytes stored in the FIFO buffer */ +#define MFRC522_FLUSH_BUFFER (1 << 7) /* immediately clears the internal FIFO buffer */ + +/* Section 9.3.1.12: WaterLevelReg register */ + +#define MFRC522_WATER_LEVEL_MASK (0x3F) /* level for FIFO under- and overflow warning */ + +/* Section 9.3.1.13: ControlReg register */ + +#define MFRC522_RX_LAST_BITS_MASK (7 << 0) /* indicates the number of valid bits in the last received byte */ +#define MFRC522_TSTART_NOW (1 << 6) /* timer starts immediately */ +#define MFRC522_TSTOP_NOW (1 << 7) /* timer stops immediately */ + +/* Section 9.3.1.14: BitFramingReg register */ + +#define MFRC522_TX_LAST_BITS_MASK (7 << 0) /* defines the number of bits of the last byte that will be transmitted */ +#define MFRC522_RX_ALIGN_MASK (7 << 4) /* used for reception of bit-oriented frames */ +#define MFRC522_START_SEND (1 << 7) /* starts the transmission of data */ + +/* Section 9.3.1.15: CollReg register */ + +#define MFRC522_COLL_POS_MASK (0x1F) /* shows the bit position of the first detected collision */ +#define MFRC522_COLL_POS_NOT_VALID (1 << 5) /* no collision detected or it is out of the range of CollPos[4:0] */ +#define MFRC522_VALUES_AFTER_COLL (1 << 7) /* 0 means: all received bits will be cleared after a collision */ + +/* Section 9.3.2.2: ModeReg register */ + +#define MFRC522_CRC_PRESET_MASK (0x3) /* defines the preset value for the CalcCRC */ +#define MFRC522_CRC_PRESET_0000 (0x0) /* 0000h CRC preset value */ +#define MFRC522_CRC_PRESET_6363 (0x1) /* 6363h CRC preset value */ +#define MFRC522_CRC_PRESET_A671 (0x2) /* A671h CRC preset value */ +#define MFRC522_CRC_PRESET_FFFF (0x3) /* FFFFh CRC preset value */ +#define MFRC522_POL_MFIN (1 << 3) /* defines the polarity of pin MFIN */ +#define MFRC522_TX_WAIT_RF (1 << 5) /* transmitter can only be started if an RF field is generated */ +#define MFRC522_MSB_FIRST (1 << 7) /* CRC coprocessor calculates the CRC with MSB first */ + +/* Section 9.3.2.3: TxModeReg register */ + +#define MFRC522_INV_MOD (1 << 3) /* modulation of transmitted data is inverted */ +#define MFRC522_TX_SPEED_MASK (7 << 4) /* defines the bit rate during data transmission */ +#define MFRC522_TX_106KBD (0 << 4) /* 106 kBd */ +#define MFRC522_TX_212KBD (1 << 4) /* 212 kBd */ +#define MFRC522_TX_424KBD (2 << 4) /* 424 kBd */ +#define MFRC522_TX_848KBD (3 << 4) /* 848 kBd */ + /* 4-7 << 4 - reserved */ +#define MFRC522_TX_CRC_EN (1 << 7) /* enables CRC generation during data transmission */ + +/* Section 9.3.2.4: RxModeReg register */ + +#define MFRC522_RX_MULTIPLE (1 << 2) /* enable to receive more than one data frame, only at 106kBd */ +#define MFRC522_RX_NO_ERR (1 << 3) /* ignore invalid data stream error (less than 4 bits received) */ +#define MFRC522_RX_SPEED_MASK (7 << 4) /* defines the bit rate during data reception */ +#define MFRC522_RX_106KBD (0 << 4) /* 106 kBd */ +#define MFRC522_RX_212KBD (1 << 4) /* 212 kBd */ +#define MFRC522_RX_424KBD (2 << 4) /* 424 kBd */ +#define MFRC522_RX_848KBD (3 << 4) /* 848 kBd */ + /* 4-7 << 4 - reserved */ +#define MFRC522_RX_CRC_EN (1 << 7) /* enables CRC generation during data reception */ + +/* Section 9.3.2.5: TxControlReg register */ + +#define MFRC522_TX1_RF_EN (1 << 0) /* output signal on pin TX1 delivers 13.56MHz */ +#define MFRC522_TX2_RF_EN (1 << 1) /* output signal on pin TX2 delivers 13.56MHz */ + /* bit 2 - reserved */ +#define MFRC522_TX2_CW (1 << 3) /* output signal on pin TX2 delivers (un)modulated 13.56MHz */ +#define MFRC522_INV_TX1_RF_OFF (1 << 4) /* output signal on pin TX1 is inverted when driver TX1 is disabled */ +#define MFRC522_INV_TX2_RF_OFF (1 << 5) /* output signal on pin TX2 is inverted when driver TX2 is disabled */ +#define MFRC522_INV_TX1_RF_ON (1 << 6) /* output signal on pin TX1 is inverted when driver TX1 is enabled */ +#define MFRC522_INV_TX2_RF_ON (1 << 7) /* output signal on pin TX2 is inverted when driver TX2 is enabled */ + +/* Section 9.3.2.6: TxASKReg register */ + +#define MFRC522_FORCE_100ASK (1 << 6) /* forces a 100% ASK modulation independent of the ModGsPReg setting */ + +/* Section 9.3.2.7: TxSelReg register */ + +#define MFRC522_MFOUT_SEL_MASK (0xF) /* selects the input for pin MFOUT */ +#define MFRC522_MFOUT_3STATE (0) /* 3-state */ +#define MFRC522_MFOUT_LOW (1) /* constant Low */ +#define MFRC522_MFOUT_HIGH (2) /* constant High */ +#define MFRC522_MFOUT_TEST_BUS (3) /* test bus signal as defined by the TstBusBitSel[2:0] value */ +#define MFRC522_MFOUT_INT_ENV (4) /* modulation signal (envelope) from the internal encoder */ +#define MFRC522_MFOUT_TX_STREAM (5) /* serial data stream to be transmitted, data stream before Miller encoder */ + /* 6 - reserved */ +#define MFRC522_MFOUT_RX_STREAM (7) /* serial data stream received, data stream after Manchester decoder */ + /* 8-15 - reserved */ +#define MFRC522_DRV_SEL_MASK (3 << 4) /* selects the input of drivers TX1 and TX2 */ +#define MFRC522_DRV_SEL_3STATE (0 << 4) /* 3-state */ +#define MFRC522_DRV_SEL_INT_ENV (1 << 4) /* modulation signal (envelope) from the internal encoder */ +#define MFRC522_DVR_SEL_ENV_MFIN (2 << 4) /* modulation signal (envelope) from pin MFIN */ +#define MFRC522_DVR_SEL_HIGH (3 << 4) /* High: depends on InvTx1RFOn/InvTx1RFOff and InvTx2RFOn/InvTx2RFOff */ + +/* Section 9.3.2.8: RxSelReg register */ + +#define MFRC522_RX_WAIT_MASK (0x3F) /* delay the receiver RxWait bit-clocks after transmission */ +#define MFRC522_UART_SEL_MASK (3 << 6) /* selects the input of the contactless UART */ +#define MFRC522_UART_LOW (0 << 6) /* constant Low */ +#define MFRC522_UART_MANCHESTER (1 << 6) /* Manchester with subcarrier from pin MFIN */ +#define MFRC522_UART_INT_MOD (2 << 6) /* modulated signal from the internal analog module, default */ +#define MFRC522_UART_NRZ_CODE (3 << 6) /* NRZ coding without subcarrier from pin MFIN */ + +/* Section 9.3.2.9: RxThresholdReg register */ + +#define MFRC522_COLL_LEVEL_MASK (7) /* the minimum signal strength to generate a bit-collision */ +#define MFRC522_MIN_LEVEL_MASK (0xF << 4) /* the minimum signal strength that will be accepted */ + +/* Section 9.3.2.10: DemodReg register */ + +#define MFRC522_TAU_SYNC_MASK (3 << 0) /* changes the time-constant of the internal PLL during burst */ +#define MFRC522_TAU_RCV_MASK (3 << 2) /* changes the time-constant of the internal PLL during data reception */ +#define MFRC522_TPRESCAL_EVEN (1 << 4) /* defines the Timer Prescaler formula to use */ +#define MFRC522_FIX_IQ (1 << 5) /* defines if reception will be fixed at channel I or Q based on AddIQ[1:0] */ +#define MFRC522_ADD_IQ_MASK (3 << 6) /* defines the use of I and Q channel during reception */ + +/* Section 9.3.2.13: MfTxReg register */ + +#define MFRC522_MF_TX_WAIT_MASK (3 << 0) /* defines the additional response time */ + +/* Section 9.3.2.14 MfRxReg register */ + +#define MFRC522_MF_RX_PARITY_DIS (1 << 4 ) /* disable parity bit to transmittion and reception */ + +/* Section 9.3.2.16: SerialSpeedReg register */ + +#define MFRC522_BR_T1_MASK (0x1F) /* factor BR_T1 adjusts the transfer speed */ +#define MFRC522_BR_T0_MASK (7 << 5) /* factor BR_T0 adjusts the transfer speed */ + +/* Section 9.3.3.6: RFCfgReg register */ + +#define MFRC522_RX_GAIN_MASK (0x7 << 4) +#define MFRC522_RX_GAIN_18DB (0x0 << 4) +#define MFRC522_RX_GAIN_23DB (0x1 << 4) +#define MFRC522_RX_GAIN_18DB_2 (0x2 << 4) +#define MFRC522_RX_GAIN_23DB_2 (0x3 << 4) +#define MFRC522_RX_GAIN_33DB (0x4 << 4) +#define MFRC522_RX_GAIN_38DB (0x5 << 4) +#define MFRC522_RX_GAIN_43DB (0x6 << 4) +#define MFRC522_RX_GAIN_48DB (0x7 << 4) + +/* MFRC522 TModeReg and TPrescalerReg registers */ + +#define MFRC522_TPRESCALER_HI_MASK (0xF) +#define MFRC522_TAUTO_RESTART (1 << 4) +#define MFRC522_TGATED_MASK (3 << 5) +#define MFRC522_TGATED_NONGATED (0 << 5) /* non-gated mode */ +#define MFRC522_TGATED_MFIN (1 << 5) /* gated by pin MFIN */ +#define MFRC522_TGATED_AUX1 (2 << 5) /* gated by pin AUX1 */ +#define MFRC522_TAUTO (1 << 7) /* timer starts automatically at the end of the transmission */ + +/* MFRC522 AutoTestReg register */ + +#define MFRC522_SELFTEST_MASK (0xF) /* for default operation the self test must be disabled by value 0000b */ +#define MFRC522_RFT_MASK (3 << 4) /* reserved for production tests */ +#define MFRC522_AMP_RCV (1 << 6) /* non-linear signal processing mode, increase range distance at 106kBd */ + +#define MFRC522_SELFTEST_EN 9 /* the self test is enabled by value 1001b */ + +#ifndef CONFIG_MFRC522_SPI_FREQ +# define CONFIG_MFRC522_SPI_FREQ (5000000) +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct mfrc522_dev_s +{ + uint8_t state; + FAR struct spi_dev_s *spi; /* SPI interface */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +bool mfrc522_set_config(struct mfrc522_dev_s *dev, uint8_t flags); + +#endif /* __DRIVERS_CONTACTLESS_MFRC522_H */ diff --git a/drivers/contactless/pn532.c b/drivers/contactless/pn532.c new file mode 100644 index 0000000000..0f17f4c315 --- /dev/null +++ b/drivers/contactless/pn532.c @@ -0,0 +1,1165 @@ +/**************************************************************************** + * drivers/contactless/pn532.c + * + * Copyright(C) 2012, 2013, 2016 Offcode Ltd. All rights reserved. + * Authors: Janne Rosberg + * Teemu Pirinen + * Juho Grundström + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pn532.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ +/* Bit order H/W feature must be enabled in order to support LSB first + * operation. + */ + +#if !defined(CONFIG_SPI_HWFEATURES) || !defined(CONFIG_SPI_BITORDER) +# error CONFIG_SPI_HWFEATURES=y and CONFIG_SPI_BITORDER=y required by this driver +#endif + +#ifndef CONFIG_ARCH_HAVE_SPI_BITORDER +# warning This platform does not support SPI LSB-bit order +#endif + +#ifdef CONFIG_CL_PN532_DEBUG +# define pn532err _err +# define pn532info _info +#else +# ifdef CONFIG_CPP_HAVE_VARARGS +# define pn532err(x...) +# define pn532info(x...) +# else +# define pn532err (void) +# define pn532info (void) +# endif +#endif + +#ifdef CONFIG_CL_PN532_DEBUG_TX +# define tracetx errdumpbuffer +#else +# define tracetx(x...) +#endif + +#ifdef CONFIG_CL_PN532_DEBUG_RX +# define tracerx errdumpbuffer +#else +# define tracerx(x...) +#endif + +#define FRAME_SIZE(f) (sizeof(struct pn532_frame) + f->len + 2) +#define FRAME_POSTAMBLE(f) (f->data[f->len + 1]) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void pn532_configspi(FAR struct spi_dev_s *spi); +static void pn532_lock(FAR struct spi_dev_s *spi); +static void pn532_unlock(FAR struct spi_dev_s *spi); + +/* Character driver methods */ + +static int _open(FAR struct file *filep); +static int _close(FAR struct file *filep); +static ssize_t _read(FAR struct file *, FAR char *, size_t); +static ssize_t _write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static int _ioctl(FAR struct file *filep,int cmd,unsigned long arg); + +static uint8_t pn532_checksum(uint8_t value); +static uint8_t pn532_data_checksum(uint8_t *data, int datalen); + +int pn532_read(struct pn532_dev_s *dev, uint8_t *buff, uint8_t n); + +/* IRQ Handling TODO: +static int pn532_irqhandler(FAR int irq, FAR void *context, FAR void* dev); +static inline int pn532_attachirq(FAR struct pn532_dev_s *dev, xcpt_t isr); +*/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_pn532fops = +{ + _open, + _close, + _read, + _write, + 0, + _ioctl +#ifndef CONFIG_DISABLE_POLL + ,0 +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + ,0 +#endif +}; + +static const uint8_t pn532ack[] = +{ + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void pn532_lock(FAR struct spi_dev_s *spi) +{ + int ret; + + (void)SPI_LOCK(spi, true); + + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + + ret = SPI_HWFEATURES(spi, HWFEAT_LSBFIRST); + if (ret < 0) + { + pn532err("ERROR: SPI_HWFEATURES failed to set bit order: %d\n", ret); + } + + (void)SPI_SETFREQUENCY(spi, CONFIG_PN532_SPI_FREQ); +} + +static void pn532_unlock(FAR struct spi_dev_s *spi) +{ + (void)SPI_LOCK(spi, false); +} + +static inline void pn532_configspi(FAR struct spi_dev_s *spi) +{ + int ret; + + /* Configure SPI for the PN532 module. */ + + SPI_SETMODE(spi, SPIDEV_MODE0); + SPI_SETBITS(spi, 8); + + ret = SPI_HWFEATURES(spi, HWFEAT_LSBFIRST); + if (ret < 0) + { + pn532err("ERROR: SPI_HWFEATURES failed to set bit order: %d\n", ret); + } + + (void)SPI_SETFREQUENCY(spi, CONFIG_PN532_SPI_FREQ); +} + +static inline void pn532_select(struct pn532_dev_s *dev) +{ + if (dev->config->select) + { + dev->config->select(dev, true); + } + else + { + SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS, true); + } +} + +static inline void pn532_deselect(struct pn532_dev_s *dev) +{ + if (dev->config->select) + { + dev->config->select(dev, false); + } + else + { + SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS, false); + } +} + +static void pn532_frame_init(struct pn532_frame *frame, uint8_t cmd) +{ + frame->preamble = PN532_PREAMBLE; + frame->start_code = PN532_SOF; + frame->tfi = PN532_HOSTTOPN532; + frame->data[0] = cmd; + frame->len = 2; +} + +static void pn532_frame_finish(struct pn532_frame *frame) +{ + frame->lcs = pn532_checksum(frame->len); + frame->data[frame->len-1] = pn532_data_checksum(&frame->tfi, frame->len); + frame->data[frame->len] = PN532_POSTAMBLE; +} + +static inline uint8_t pn532_checksum(uint8_t value) +{ + return ~value + 1; +} + +static uint8_t pn532_data_checksum(uint8_t *data, int datalen) +{ + uint8_t sum = 0x00; + int i; + + for (i = 0; i < datalen; i++) + { + sum += data[i]; + } + + return pn532_checksum(sum); +} + +bool pn532_rx_frame_is_valid(struct pn532_frame *f, bool check_data) +{ + uint8_t chk; + + if (f->start_code != PN532_SOF) + { + pn532err("ERROR: Frame startcode 0x%X != 0x%X\n", + PN532_SOF, f->start_code); + return false; + } + + if (f->tfi != PN532_PN532TOHOST) + { + return false; + } + + chk = pn532_checksum(f->len); + if (chk != f->lcs) + { + pn532err("ERROR: Frame data len checksum failed"); + return false; + } + + if (check_data) + { + chk = pn532_data_checksum(&f->tfi, f->len); + if (chk != f->data[f->len-1]) + { + pn532err("ERROR: Frame data checksum failed: calc=0x%X != 0x%X", + chk, f->data[f->len-1]); + return false; + } + } + + return true; +} + +static uint8_t pn532_status(struct pn532_dev_s *dev) +{ + int rs; + + pn532_lock(dev->spi); + pn532_select(dev); + + rs = SPI_SEND(dev->spi, PN532_SPI_STATREAD); + rs = SPI_SEND(dev->spi, PN532_SPI_STATREAD); + + pn532_deselect(dev); + pn532_unlock(dev->spi); + + return rs; +} + +/**************************************************************************** + * Name: pn532_wait_rx_ready + * + * Description: + * Blocks until Data frame available from chip. + * + * Input Parameters: + * dev + * timeout + * + * Returned Value: + * 0 for OK. -ETIMEDOUT if no data available + * + ****************************************************************************/ + +static int pn532_wait_rx_ready(struct pn532_dev_s *dev, int timeout) +{ + int ret = OK; + +#ifdef CONFIG_PN532_USE_IRQ_FLOW_CONTROL + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += 1; + sem_timedwait(dev->sem_rx, &ts); +#endif + + /* TODO: Handle Exception bits 2, 3 */ + + while (pn532_status(dev) != PN532_SPI_READY) + { + if (--timeout == 0x00) + { + pn532err("ERROR: wait RX timeout!\n"); + return -ETIMEDOUT; + } + + usleep(1000); + } + + dev->state = PN532_STATE_DATA_READY; + return ret; +} + +/**************************************************************************** + * Name: pn532_writecommand + * + * Description: + * Helper for debug/testing + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#if 0 +static void pn532_writecommand(struct pn532_dev_s *dev, uint8_t cmd) +{ + char cmd_buffer[16]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + + pn532_frame_init(f, cmd); + pn532_frame_finish(f); + + pn532_lock(dev->spi); + pn532_select(dev); + usleep(10000); + + SPI_SEND(dev->spi, PN532_SPI_DATAWRITE); + SPI_SNDBLOCK(dev->spi, f, FRAME_SIZE(f)); + + pn532_deselect(dev); + pn532_unlock(dev->spi); + + tracetx("command sent", (uint8_t *) f, FRAME_SIZE(f)); +} +#endif + +/**************************************************************************** + * Name: pn532_read + * + * Description: + * RAW Read data from chip. + * NOTE: This WON'T wait if data is available! + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +int pn532_read(struct pn532_dev_s *dev, uint8_t *buff, uint8_t n) +{ + pn532_lock(dev->spi); + pn532_select(dev); + SPI_SEND(dev->spi, PN532_SPI_DATAREAD); + SPI_RECVBLOCK(dev->spi, buff, n); + pn532_deselect(dev); + pn532_unlock(dev->spi); + + tracerx("read", buff, n); + return n; +} + +int pn532_read_more(struct pn532_dev_s *dev, uint8_t *buff, uint8_t n) +{ + pn532_lock(dev->spi); + pn532_select(dev); + SPI_RECVBLOCK(dev->spi, buff, n); + pn532_deselect(dev); + pn532_unlock(dev->spi); + + tracerx("read_more", buff, n); + return n; +} + +/**************************************************************************** + * Name: pn532_read_ack + * + * Description: + * Read Ack responce from device + * + * Input Parameters: + * dev + * + * Returned Value: + * 0 = NOK, 1 = OK + * + ****************************************************************************/ + +int pn532_read_ack(struct pn532_dev_s *dev) +{ + int res = 0; + uint8_t ack[6]; + + pn532_read(dev, (uint8_t *) &ack, 6); + + if (memcmp(&ack, &pn532ack, 6) == 0x00) + { + res = 1; + } + else + { + pn532info("ACK NOK"); + res = 0; + } + + return res; +} + +/**************************************************************************** + * Name: pn532_write_frame + * + * Description: + * Write frame to chip. Also waits and reads ACK frame from chip. + * + * Construct frame with + * pn532_frame_init(), pn532_frame_finish() + * + * Input Parameters: + * dev - Device instance + * f - Pointer to start frame + * + * Returned Value: + * 0 for OK, negative for error + * + ****************************************************************************/ + +int pn532_write_frame(struct pn532_dev_s *dev, struct pn532_frame *f) +{ + int res = OK; + + pn532_lock(dev->spi); + pn532_select(dev); + usleep(2000); + + SPI_SEND(dev->spi, PN532_SPI_DATAWRITE); + SPI_SNDBLOCK(dev->spi, f, FRAME_SIZE(f)); + pn532_deselect(dev); + pn532_unlock(dev->spi); + tracetx("WriteFrame", (uint8_t *) f, FRAME_SIZE(f)); + + /* Wait ACK frame */ + + res = pn532_wait_rx_ready(dev, 30); + if (res == OK) + { + if (!pn532_read_ack(dev)) + { + pn532err("ERROR: command FAILED\n"); + res = -EIO; + } + } + + return res; +} + +int pn532_read_frame(struct pn532_dev_s *dev, struct pn532_frame *f, int max_size) +{ + int res = -EIO; + + /* Wait for frame available */ + + if ((res = pn532_wait_rx_ready(dev, 100)) == OK) + { + /* Read header */ + + pn532_read(dev, (uint8_t *) f, sizeof(struct pn532_frame)); + if (pn532_rx_frame_is_valid(f, false)) + { + if (max_size < f->len) + { + return -EINVAL; + } + + pn532_read_more(dev, &f->data[0], f->len); + + /* TODO: optimize frame integrity check... + * pn532_data_checksum(&f.tfi, f->len); + * errdumpbuffer("RX Frame:", f, f->len+6); + */ + + if (pn532_rx_frame_is_valid(f, true)) + { + res = OK; + } + } + } + + return res; +} + +bool pn532_set_config(struct pn532_dev_s *dev, uint8_t flags) +{ + char cmd_buffer[2+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + + pn532_frame_init(f, PN532_COMMAND_SETPARAMETERS); + f->data[1] = flags; + f->len += 1; + pn532_frame_finish(f); + + uint8_t resp[9]; + bool res = false; + + if (pn532_write_frame(dev, f) == OK) + { + pn532_read(dev, (uint8_t *) &resp, 9); + tracerx("set config responce", resp, 9); + res = true; + } + + return res; +} + +int pn532_sam_config(struct pn532_dev_s *dev, struct pn_sam_settings_s *settings) +{ + char cmd_buffer[4+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + int res; + + pn532_frame_init(f, PN532_COMMAND_SAMCONFIGURATION); + f->data[1] = PN532_SAM_NORMAL_MODE; + f->data[2] = 0x14; /* Timeout LSB=50ms 0x14*50ms = 1sec */ + f->data[3] = 0x01; /* P-70, IRQ enabled */ + + if (settings) + { + /* TODO: !!! */ + } + + f->len += 3; + pn532_frame_finish(f); + + res = -EIO; + + if (pn532_write_frame(dev, f) == OK) + { + if (pn532_read_frame(dev, f, 4) == OK) + { + tracerx("sam config response", (uint8_t *) f->data, 3); + if (f->data[0] == PN532_COMMAND_SAMCONFIGURATION + 1) + { + res = OK; + } + } + } + + return res; +} + +int pn532_get_fw_version(struct pn532_dev_s *dev, + struct pn_firmware_version *fv) +{ + uint8_t cmd_buffer[4+8+1]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + struct pn_firmware_version *fw; + int res = -EIO; + + pn532_frame_init(f, PN532_COMMAND_GETFIRMWAREVERSION); + pn532_frame_finish(f); + + if (pn532_write_frame(dev, f) == OK) + { + if (pn532_read_frame(dev, f, 6) == OK) + { + if (f->data[0] == PN532_COMMAND_GETFIRMWAREVERSION + 1) + { + fw = (struct pn_firmware_version*) &f->data[1]; + pn532info("FW: %d.%d on IC:0x%X (Features: 0x%X)\n", + fw->ver, fw->rev, fw->ic, fw->support); + if (fv) + { + memcpy(fv, fw, sizeof(struct pn_firmware_version)); + } + + res = OK; + } + } + } + + return res; +} + +int pn532_write_gpio(struct pn532_dev_s *dev, uint8_t p3, uint8_t p7) +{ + uint8_t cmd_buffer[3+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + int res = -EIO; + + pn532_frame_init(f, PN532_COMMAND_WRITEGPIO); + f->data[1] = p3; + f->data[2] = p7; + f->len += 2; + pn532_frame_finish(f); + + if (pn532_write_frame(dev, f)) + { + pn532_read(dev, cmd_buffer, 10); + tracetx("Resp:", cmd_buffer, 10); + pn532info("TFI=%x, data0=%X", f->tfi, f->data[0]); + if ((f->tfi == PN532_PN532TOHOST) && (f->data[0] == PN532_COMMAND_WRITEGPIO+1)) + { + res = OK; + } + } + + return res; +} + +uint32_t pn532_write_passive_data(struct pn532_dev_s *dev, uint8_t address, + uint8_t *data, uint8_t len) +{ + uint8_t cmd_buffer[8+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + uint8_t resp[20]; + uint32_t res = -EIO; + + pn532_frame_init(f, PN532_COMMAND_INDATAEXCHANGE); + f->data[1] = 1; /* max n cards at once */ + f->data[2] = 0xA2; /* command WRITE */ + f->data[3] = address; /* ADDRESS, 0 = serial */ + memcpy(&f->data[4], data, len); + f->len += 7; + pn532_frame_finish(f); + + if (pn532_write_frame(dev, f) == OK) + { + if (dev->state == PN532_STATE_DATA_READY) + { + if (pn532_read_frame(dev, (struct pn532_frame *) resp, 15) == OK) + { + dev->state = PN532_STATE_IDLE; + f = (struct pn532_frame *) resp; + tracerx("passive target id resp:", f, f->len+6); + + if (f->data[0] == PN532_COMMAND_INDATAEXCHANGE+1) + { + res = f->data[1]; + } + } + } + } + + return res; +} + +uint32_t pn532_read_passive_data(struct pn532_dev_s *dev, uint8_t address, + uint8_t *data, uint8_t len) +{ + uint8_t cmd_buffer[4+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + uint8_t resp[30]; + uint32_t res = -1; + + pn532_frame_init(f, PN532_COMMAND_INDATAEXCHANGE); + f->data[1] = 1; /* max n cards at once */ + f->data[2] = 0x30; /* command READ */ + f->data[3] = address; /* ADDRESS, 0 = serial */ + f->len += 3; + pn532_frame_finish(f); + + if (pn532_write_frame(dev, f) == OK) + { + if (dev->state == PN532_STATE_DATA_READY) + { + if (pn532_read_frame(dev, (struct pn532_frame *)resp, 25) == OK) + { + dev->state = PN532_STATE_IDLE; + f = (struct pn532_frame *) resp; + tracerx("passive target id resp:", f, f->len+6); + + if (f->data[0] == PN532_COMMAND_INDATAEXCHANGE+1) + { + if(f->data[1] == 0 && data && len) + { + memcpy(data, &f->data[2], len); + } + + res = f->data[1]; + } + } + } + } + + return res; +} + +uint32_t pn532_read_passive_target_id(struct pn532_dev_s *dev, uint8_t baudrate) +{ + uint8_t cmd_buffer[4+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + uint8_t resp[20]; + uint32_t res = -EAGAIN; + int i; + + if (dev->state == PN532_STATE_DATA_READY) + { + res = -EIO; + if (pn532_read_frame(dev, (struct pn532_frame *) resp, 15) == OK) + { + dev->state = PN532_STATE_IDLE; + f = (struct pn532_frame *) resp; + struct pn_poll_response *r = (struct pn_poll_response *) &f->data[1]; + tracerx("passive target id resp:", f, f->len+6); + + if (f->data[0] == PN532_COMMAND_INLISTPASSIVETARGET+1) + { + uint32_t cid = 0; + + if (r->nbtg == 1) + { + pn532info("Found %d card(s)\n", r->nbtg); + + /* now supports only type_a cards + * if (poll_mode == PN532_POLL_MOD_106KBPS_A) + */ + + struct pn_target_type_a *t = (struct pn_target_type_a *) &r->target_data; + pn532info("sens:0x%x sel:0x%x", t->sens_res, t->sel_res); + pn532info("idlen:0x%x ", t->nfcid_len); + + /* generate 32bit cid from id (could be longer) + * HACK: Using only top 4 bytes. + */ + + for (i = 0; i < 4 /*t->nfcid_len*/; i++) + { + cid <<= 8; + cid |= t->nfcid_data[i]; + } + } + + res = cid; + } + } + } + + return res; + +} + +static int pn532_read_passive_target(struct pn532_dev_s *dev, uint8_t baudrate) +{ + uint8_t cmd_buffer[4+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + + pn532_frame_init(f, PN532_COMMAND_INLISTPASSIVETARGET); + f->data[1] = 1; + f->data[2] = baudrate; + f->len += 2; + pn532_frame_finish(f); + return pn532_write_frame(dev, f); +} + +bool pn532_set_rf_config(struct pn532_dev_s *dev, struct pn_rf_config_s *conf) +{ + bool res = false; + uint8_t cmd_buffer[15+7]; + struct pn532_frame *f = (struct pn532_frame *) cmd_buffer; + + pn532_frame_init(f, PN532_COMMAND_RFCONFIGURATION); + f->data[1] = conf->cfg_item; + memcpy(&f->data[2], conf->config, conf->data_size); + f->len += conf->data_size+1; + pn532_frame_finish(f); + + if (pn532_write_frame(dev, f) == OK) + { + pn532_read(dev, (uint8_t *) f, 10); + tracerx("rf config response", (uint8_t *) f, 10); + if (pn532_rx_frame_is_valid(f, true)) + { + if (f->data[0] == PN532_COMMAND_RFCONFIGURATION + 1) + { + res = true; + } + } + } + + return res; +} + +/**************************************************************************** + * Name: pn532_attachirq + * + * Description: + * IRQ handling TODO: + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#if 0 +static inline int (FAR struct pn532_dev_s *dev, xcpt_t isr) +{ + return dev->config->irqattach(dev,isr); +} + +static int irq_handler(int irq, FAR void *context) +{ + (void) irq; + (void) context; + + /* pn532info("*IRQ*\n"); */ + /* work_queue(HPWORK, &g_dev->irq_work, pn532_worker, dev, 0); */ + + return OK; +} +#endif + +/**************************************************************************** + * Name: pn532_open + * + * Description: + * This function is called whenever the PN532 device is opened. + * + ****************************************************************************/ + +static int _open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct pn532_dev_s *dev; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + pn532_configspi(dev->spi); + + dev->config->reset(1); + usleep(10000); + + pn532_sam_config(dev, NULL); + pn532_get_fw_version(dev, NULL); + + dev->state = PN532_STATE_IDLE; + return OK; +} + +/**************************************************************************** + * Name: _close + * + * Description: + * This routine is called when the PN532 device is closed. + * + ****************************************************************************/ + +static int _close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct pn532_dev_s *dev; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + dev->config->reset(0); + dev->state = PN532_STATE_NOT_INIT; + +#ifdef CONFIG_PM + if(dev->pm_level >= PM_SLEEP) + { + //priv->config->reset(0); + } +#endif + + return OK; +} + +/**************************************************************************** + * Name: _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 _read(FAR struct file *filep, FAR char *buffer, size_t buflen) +{ + FAR struct inode *inode; + FAR struct pn532_dev_s *dev; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + uint32_t id = pn532_read_passive_target_id(dev, PN532_MIFARE_ISO14443A); + if (id != 0xFFFFFFFF) + { + if (buffer) + { + return snprintf(buffer, buflen, "0X%X", id); + } + } + + return -EIO; +} + +/**************************************************************************** + * Name: pn532_write + ****************************************************************************/ + +static ssize_t _write(FAR struct file *filep, FAR const char *buffer, + size_t buflen) +{ + FAR struct inode *inode; + FAR struct pn532_dev_s *dev; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + (void) dev; + + return -ENOSYS; +} + +/**************************************************************************** + * Name: pn532_ioctl + ****************************************************************************/ + +static int _ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct pn532_dev_s *dev; + int ret = OK; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + dev = inode->i_private; + + switch (cmd) + { + case PN532IOC_READ_TAG_DATA: + { + struct pn_mifare_tag_data_s *tag_data = (struct pn_mifare_tag_data_s *) arg; + if (tag_data) + { + /* HACK: get rid of previous command */ + + if (dev->state == PN532_STATE_CMD_SENT) + { + if (pn532_wait_rx_ready(dev, 1)) + { + pn532_read_passive_target_id(dev,0); + } + } + + ret = pn532_read_passive_data(dev, tag_data->address, + (uint8_t *) &tag_data->data, + sizeof(tag_data->data)); + + dev->state = PN532_STATE_IDLE; + } + } + break; + + case PN532IOC_WRITE_TAG_DATA: + { + struct pn_mifare_tag_data_s *tag_data = (struct pn_mifare_tag_data_s *) arg; + if (tag_data) + { + /* HACK: get rid of previous command */ + + if (dev->state == PN532_STATE_CMD_SENT) + { + if (pn532_wait_rx_ready(dev, 1)) + { + pn532_read_passive_target_id(dev,0); + } + } + + ret = pn532_write_passive_data(dev, tag_data->address, + (uint8_t *) &tag_data->data, + sizeof(tag_data->data)); + + dev->state = PN532_STATE_IDLE; + } + } + break; + + case PN532IOC_SET_SAM_CONF: + pn532_sam_config(dev, (struct pn_sam_settings_s *) arg); + break; + + case PN532IOC_READ_PASSIVE: + if (dev->state == PN532_STATE_CMD_SENT) + { + uint32_t *ptr = (uint32_t *)((uintptr_t)arg); + *ptr = pn532_read_passive_target_id(dev,0); + } + else + { + uint32_t *ptr = (uint32_t *)((uintptr_t)arg); + *ptr = -1; + } + break; + + case PN532IOC_SET_RF_CONF: + pn532_set_rf_config(dev, (struct pn_rf_config_s *) arg); + break; + + case PN532IOC_SEND_CMD_READ_PASSIVE: + ret = pn532_read_passive_target(dev,0); + if (ret == 0) + { + dev->state = PN532_STATE_CMD_SENT; + } + else + { + dev->state = PN532_STATE_IDLE; + } + break; + + case PN532IOC_GET_DATA_READY: + if (pn532_wait_rx_ready(dev, 1)) + { + ret = 0; + } + else + { + ret = 1; + } + break; + + case PN532IOC_GET_TAG_ID: + { + uint32_t *ptr = (uint32_t *)((uintptr_t)arg); + *ptr = pn532_read_passive_target_id(dev,0); + } + break; + + case PN532IOC_GET_STATE: + ret = dev->state; + break; + + default: + pn532err("ERROR: Unrecognized cmd: %d\n", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pn532_register + * + * Description: + * Register the PN532 character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. + * E.g., "/dev/nfc0" + * spi - An instance of the SPI interface to use to communicate with + * PN532. + * config - chip config + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int pn532_register(FAR const char *devpath, FAR struct spi_dev_s *spi, + FAR struct pn532_config_s *config) +{ + FAR struct pn532_dev_s *dev; + int ret; + + /* Initialize the PN532 device structure */ + + dev = (FAR struct pn532_dev_s *)kmm_malloc(sizeof(struct pn532_dev_s)); + if (!dev) + { + pn532err("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + dev->spi = spi; + dev->config = config; + +#if defined CONFIG_PM + dev->pm_level = PM_IDLE; +#endif + + /* pn532_attachirq(dev, pn532_irqhandler); */ + + /* Register the character driver */ + + ret = register_driver(devpath, &g_pn532fops, 0666, dev); + if (ret < 0) + { + pn532err("ERROR: Failed to register driver: %d\n", ret); + kmm_free(dev); + } + + return ret; +} diff --git a/drivers/contactless/pn532.h b/drivers/contactless/pn532.h new file mode 100644 index 0000000000..ee6980532b --- /dev/null +++ b/drivers/contactless/pn532.h @@ -0,0 +1,171 @@ +/**************************************************************************** + * drivers/contactless/pn532.h + * + * Copyright(C) 2012, 2013, 2016 Offcode Ltd. All rights reserved. + * Authors: Janne Rosberg + * Teemu Pirinen + * Juho Grundström + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __DRIVERS_CONTACTLESS_PN532_H +#define __DRIVERS_CONTACTLESS_PN532_H 1 + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ + +#define PN532_PREAMBLE 0x00 +#define PN532_STARTCODE1 0x00 +#define PN532_STARTCODE2 0xFF +#define PN532_POSTAMBLE 0x00 + +#define PN532_SOF 0xFF00 + +#define PN532_HOSTTOPN532 0xD4 +#define PN532_PN532TOHOST 0xD5 + +#define PN532_SPI_STATREAD 0x02 +#define PN532_SPI_DATAWRITE 0x01 +#define PN532_SPI_DATAREAD 0x03 +#define PN532_SPI_READY 0x01 + +/* PN532 Commands */ + +#define PN532_COMMAND_DIAGNOSE 0x00 +#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 +#define PN532_COMMAND_GETGENERALSTATUS 0x04 +#define PN532_COMMAND_READREGISTER 0x06 +#define PN532_COMMAND_WRITEREGISTER 0x08 +#define PN532_COMMAND_READGPIO 0x0C +#define PN532_COMMAND_WRITEGPIO 0x0E +#define PN532_COMMAND_SETSERIALBAUDRATE 0x10 +#define PN532_COMMAND_SETPARAMETERS 0x12 +#define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_POWERDOWN 0x16 +#define PN532_COMMAND_RFCONFIGURATION 0x32 +#define PN532_COMMAND_RFREGULATIONTEST 0x58 +#define PN532_COMMAND_INJUMPFORDEP 0x56 +#define PN532_COMMAND_INJUMPFORPSL 0x46 +#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A +#define PN532_COMMAND_INATR 0x50 +#define PN532_COMMAND_INPSL 0x4E +#define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INCOMMUNICATETHRU 0x42 +#define PN532_COMMAND_INDESELECT 0x44 +#define PN532_COMMAND_INRELEASE 0x52 +#define PN532_COMMAND_INSELECT 0x54 +#define PN532_COMMAND_INAUTOPOLL 0x60 +#define PN532_COMMAND_TGINITASTARGET 0x8C +#define PN532_COMMAND_TGSETGENERALBYTES 0x92 +#define PN532_COMMAND_TGGETDATA 0x86 +#define PN532_COMMAND_TGSETDATA 0x8E +#define PN532_COMMAND_TGSETMETADATA 0x94 +#define PN532_COMMAND_TGGETINITIATORCOMMAND 0x88 +#define PN532_COMMAND_TGRESPONSETOINITIATOR 0x90 +#define PN532_COMMAND_TGGETTARGETSTATUS 0x8A + +#define PN532_WAKEUP 0x55 + +#define PN532_SAM_NORMAL_MODE 0x01 +#define PN532_SAM_VIRTUAL_CARD 0x02 +#define PN532_SAM_WIRED_CARD 0x03 +#define PN532_SAM_DUAL_CARD 0x04 + +#ifndef CONFIG_PN532_SPI_FREQ +# define CONFIG_PN532_SPI_FREQ (5000000) +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct pn532_frame +{ + uint8_t preamble; /* 0x00 */ + uint16_t start_code; /* 0x00FF (BE) -> 0xFF00 (LE) */ + uint8_t len; /* 1 byte indicating the number of bytes in + * the data field */ + uint8_t lcs; /* 1 Packet Length Checksum LCS byte that satisfies + * the relation: Lower byte of [LEN + LCS] = 00h */ + uint8_t tfi; /* Frame idenfifier 0xD4, 0xD5 */ + uint8_t data[]; /* LEN-1 bytes of Packet Data Information. + * The first byte PD0 is the Command Code */ +} packed_struct; + +struct pn_poll_response +{ + uint8_t nbtg; + uint8_t tg; + uint8_t target_data[]; +} packed_struct; + +struct pn_target_type_a +{ + uint16_t sens_res; + uint8_t sel_res; + uint8_t nfcid_len; + uint8_t nfcid_data[]; +} packed_struct; + +struct pn_firmware_version +{ + uint8_t ic; + uint8_t ver; + uint8_t rev; + uint8_t support; +}; + +struct pn532_dev_s +{ + uint8_t state; + FAR struct spi_dev_s *spi; /* SPI interface */ + FAR struct pn532_config_s *config; /* Board configuration data */ +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +bool pn532_set_config(struct pn532_dev_s *dev, uint8_t flags); + +#endif /* __DRIVERS_CONTACTLESS_PN532_H */