mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Description: In kernel builds, any unprivileged process running on the NuttX device can open /dev/efuse and attempt to read/write fuse content. Reading the fuses may provide valuable information to an attacker controlling the user process. The write operation, in extreme cases where the fuse blocks are not locked, may brick the device. This is part of https://github.com/apache/nuttx/issues/19410 Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
1169 lines
28 KiB
C
1169 lines
28 KiB
C
/****************************************************************************
|
|
* drivers/contactless/pn532.c
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <assert.h>
|
|
#include <nuttx/debug.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/power/pm.h>
|
|
|
|
#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_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 *filep,
|
|
FAR char *buffer, size_t buflen);
|
|
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(FAR uint8_t *data, int datalen);
|
|
|
|
int pn532_read(FAR struct pn532_dev_s *dev, FAR uint8_t *buff, uint8_t n);
|
|
|
|
#if 0 /* TODO */
|
|
/* IRQ Handling */
|
|
|
|
static int pn532_irqhandler(int irq, FAR void *context, FAR void *dev);
|
|
static inline int pn532_attachirq(FAR struct pn532_dev_s *dev, xcpt_t isr);
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_pn532fops =
|
|
{
|
|
_open, /* open */
|
|
_close, /* close */
|
|
_read, /* read */
|
|
_write, /* write */
|
|
NULL, /* seek */
|
|
_ioctl, /* ioctl */
|
|
};
|
|
|
|
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;
|
|
|
|
SPI_LOCK(spi, true);
|
|
|
|
SPI_SETMODE(spi, SPIDEV_MODE0);
|
|
SPI_SETBITS(spi, 8);
|
|
|
|
ret = SPI_HWFEATURES(spi, HWFEAT_LSBFIRST);
|
|
if (ret < 0)
|
|
{
|
|
ctlserr("ERROR: SPI_HWFEATURES failed to set bit order: %d\n", ret);
|
|
}
|
|
|
|
SPI_SETFREQUENCY(spi, CONFIG_PN532_SPI_FREQ);
|
|
}
|
|
|
|
static void pn532_unlock(FAR struct spi_dev_s *spi)
|
|
{
|
|
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)
|
|
{
|
|
ctlserr("ERROR: SPI_HWFEATURES failed to set bit order: %d\n", ret);
|
|
}
|
|
|
|
SPI_SETFREQUENCY(spi, CONFIG_PN532_SPI_FREQ);
|
|
}
|
|
|
|
static inline void pn532_select(FAR struct pn532_dev_s *dev)
|
|
{
|
|
if (dev->config->select)
|
|
{
|
|
dev->config->select(dev, true);
|
|
}
|
|
else
|
|
{
|
|
SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS(0), true);
|
|
}
|
|
}
|
|
|
|
static inline void pn532_deselect(FAR struct pn532_dev_s *dev)
|
|
{
|
|
if (dev->config->select)
|
|
{
|
|
dev->config->select(dev, false);
|
|
}
|
|
else
|
|
{
|
|
SPI_SELECT(dev->spi, SPIDEV_CONTACTLESS(0), false);
|
|
}
|
|
}
|
|
|
|
static void pn532_frame_init(FAR 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(FAR 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(FAR 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(FAR struct pn532_frame *f, bool check_data)
|
|
{
|
|
uint8_t chk;
|
|
|
|
if (f->start_code != PN532_SOF)
|
|
{
|
|
ctlserr("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)
|
|
{
|
|
ctlserr("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])
|
|
{
|
|
ctlserr("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(FAR 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(FAR struct pn532_dev_s *dev, int timeout)
|
|
{
|
|
int ret = OK;
|
|
|
|
#ifdef CONFIG_PN532_USE_IRQ_FLOW_CONTROL
|
|
nxsem_tickwait(dev->sem_rx, SEC2TICK(1));
|
|
#endif
|
|
|
|
/* TODO: Handle Exception bits 2, 3 */
|
|
|
|
while (pn532_status(dev) != PN532_SPI_READY)
|
|
{
|
|
if (--timeout == 0x00)
|
|
{
|
|
ctlserr("ERROR: wait RX timeout!\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
nxsched_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(FAR struct pn532_dev_s *dev, uint8_t cmd)
|
|
{
|
|
char cmd_buffer[16];
|
|
FAR struct pn532_frame *f = (FAR struct pn532_frame *)cmd_buffer;
|
|
|
|
pn532_frame_init(f, cmd);
|
|
pn532_frame_finish(f);
|
|
|
|
pn532_lock(dev->spi);
|
|
pn532_select(dev);
|
|
nxsched_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", (FAR 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(FAR struct pn532_dev_s *dev, FAR 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(FAR struct pn532_dev_s *dev,
|
|
FAR 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 response from device
|
|
*
|
|
* Input Parameters:
|
|
* dev
|
|
*
|
|
* Returned Value:
|
|
* 0 = NOK, 1 = OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pn532_read_ack(FAR struct pn532_dev_s *dev)
|
|
{
|
|
int res = 0;
|
|
uint8_t ack[6];
|
|
|
|
pn532_read(dev, (FAR uint8_t *) &ack, 6);
|
|
|
|
if (memcmp(&ack, &pn532ack, 6) == 0x00)
|
|
{
|
|
res = 1;
|
|
}
|
|
else
|
|
{
|
|
ctlsinfo("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(FAR struct pn532_dev_s *dev, FAR struct pn532_frame *f)
|
|
{
|
|
int res = OK;
|
|
|
|
pn532_lock(dev->spi);
|
|
pn532_select(dev);
|
|
nxsched_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", (FAR uint8_t *)f, FRAME_SIZE(f));
|
|
|
|
/* Wait ACK frame */
|
|
|
|
res = pn532_wait_rx_ready(dev, 30);
|
|
if (res == OK)
|
|
{
|
|
if (!pn532_read_ack(dev))
|
|
{
|
|
ctlserr("ERROR: command FAILED\n");
|
|
res = -EIO;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int pn532_read_frame(FAR struct pn532_dev_s *dev, FAR 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, (FAR 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(FAR struct pn532_dev_s *dev, uint8_t flags)
|
|
{
|
|
char cmd_buffer[2 + 7];
|
|
FAR struct pn532_frame *f = (FAR 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, (FAR uint8_t *)&resp, 9);
|
|
tracerx("set config response", resp, 9);
|
|
res = true;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int pn532_sam_config(FAR struct pn532_dev_s *dev,
|
|
FAR struct pn_sam_settings_s *settings)
|
|
{
|
|
char cmd_buffer[4 + 7];
|
|
FAR struct pn532_frame *f = (FAR 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", (FAR uint8_t *)f->data, 3);
|
|
if (f->data[0] == PN532_COMMAND_SAMCONFIGURATION + 1)
|
|
{
|
|
res = OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int pn532_get_fw_version(FAR struct pn532_dev_s *dev,
|
|
FAR struct pn_firmware_version *fv)
|
|
{
|
|
uint8_t cmd_buffer[4 + 8 + 1];
|
|
FAR struct pn532_frame *f = (FAR 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 = (FAR struct pn_firmware_version *) &f->data[1];
|
|
ctlsinfo("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(FAR struct pn532_dev_s *dev, uint8_t p3, uint8_t p7)
|
|
{
|
|
uint8_t cmd_buffer[3 + 7];
|
|
FAR struct pn532_frame *f = (FAR 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);
|
|
ctlsinfo("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(FAR struct pn532_dev_s *dev,
|
|
uint8_t address, FAR uint8_t *data,
|
|
uint8_t len)
|
|
{
|
|
uint8_t cmd_buffer[8 + 7];
|
|
FAR struct pn532_frame *f = (FAR struct pn532_frame *) cmd_buffer;
|
|
uint8_t resp[20];
|
|
uint32_t res = -EIO;
|
|
|
|
/* Validate expected tag size (only pages 0x0, ..., 0xf are valid. */
|
|
|
|
if (address > 15)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
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, (FAR struct pn532_frame *) resp, 15)
|
|
== OK)
|
|
{
|
|
dev->state = PN532_STATE_IDLE;
|
|
f = (FAR struct pn532_frame *) resp;
|
|
tracerx("passive target id resp:", (FAR uint8_t *)f,
|
|
f->len + 6);
|
|
|
|
if (f->data[0] == PN532_COMMAND_INDATAEXCHANGE + 1)
|
|
{
|
|
res = f->data[1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
uint32_t pn532_read_passive_data(FAR struct pn532_dev_s *dev,
|
|
uint8_t address, FAR uint8_t *data,
|
|
uint8_t len)
|
|
{
|
|
uint8_t cmd_buffer[4 + 7];
|
|
FAR struct pn532_frame *f = (FAR struct pn532_frame *) cmd_buffer;
|
|
uint8_t resp[30];
|
|
uint32_t res = -1;
|
|
|
|
/* Validate against expected tag size */
|
|
|
|
if (address > 15)
|
|
{
|
|
return -ERANGE;
|
|
}
|
|
|
|
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,
|
|
(FAR struct pn532_frame *)resp, 25) == OK)
|
|
{
|
|
dev->state = PN532_STATE_IDLE;
|
|
f = (FAR struct pn532_frame *) resp;
|
|
tracerx("passive target id resp:", (FAR uint8_t *)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(FAR struct pn532_dev_s *dev,
|
|
uint8_t baudrate)
|
|
{
|
|
uint8_t cmd_buffer[4 + 7];
|
|
FAR struct pn532_frame *f = (FAR 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, (FAR struct pn532_frame *) resp, 15) == OK)
|
|
{
|
|
FAR struct pn_poll_response *r;
|
|
|
|
dev->state = PN532_STATE_IDLE;
|
|
f = (FAR struct pn532_frame *) resp;
|
|
r = (FAR struct pn_poll_response *) &f->data[1];
|
|
|
|
tracerx("passive target id resp:", (FAR uint8_t *)f, f->len + 6);
|
|
|
|
if (f->data[0] == PN532_COMMAND_INLISTPASSIVETARGET + 1)
|
|
{
|
|
uint32_t cid = 0;
|
|
|
|
if (r->nbtg == 1)
|
|
{
|
|
FAR struct pn_target_type_a *t =
|
|
(FAR struct pn_target_type_a *)&r->target_data;
|
|
|
|
ctlsinfo("Found %d card(s)\n", r->nbtg);
|
|
|
|
/* now supports only type_a cards
|
|
* if (poll_mode == PN532_POLL_MOD_106KBPS_A)
|
|
*/
|
|
|
|
ctlsinfo("sens:0x%x sel:0x%x", t->sens_res, t->sel_res);
|
|
ctlsinfo("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(FAR struct pn532_dev_s *dev,
|
|
uint8_t baudrate)
|
|
{
|
|
uint8_t cmd_buffer[4 + 7];
|
|
FAR struct pn532_frame *f = (FAR 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)
|
|
{
|
|
/* cmd_buffer is sizeof(pn532_frame) + up to 16 bytes data */
|
|
|
|
bool res = false;
|
|
uint8_t cmd_buffer[6 + 16];
|
|
FAR struct pn532_frame *f = (FAR struct pn532_frame *) cmd_buffer;
|
|
|
|
pn532_frame_init(f, PN532_COMMAND_RFCONFIGURATION);
|
|
f->data[1] = conf->cfg_item;
|
|
|
|
/* only copy 16 bytes minus 1 byte for each: cmd and cfg_item */
|
|
|
|
DEBUGASSERT(conf->data_size <= 16 - 2);
|
|
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, (FAR uint8_t *)f, 10);
|
|
tracerx("rf config response", (FAR 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)
|
|
{
|
|
ctlsinfo("*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;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
pn532_configspi(dev->spi);
|
|
|
|
dev->config->reset(1);
|
|
nxsched_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;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(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)
|
|
{
|
|
#if 0
|
|
priv->config->reset(0);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: _read
|
|
*
|
|
* Description:
|
|
* This routine is called when the device is read.
|
|
*
|
|
* Returned Value:
|
|
* 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;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(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;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
UNUSED(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;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
dev = inode->i_private;
|
|
|
|
switch (cmd)
|
|
{
|
|
case PN532IOC_READ_TAG_DATA:
|
|
{
|
|
FAR struct pn_mifare_tag_data_s *tag_data =
|
|
(FAR struct pn_mifare_tag_data_s *) arg;
|
|
|
|
if (tag_data != 0)
|
|
{
|
|
/* 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,
|
|
(FAR uint8_t *)&tag_data->data,
|
|
sizeof(tag_data->data));
|
|
|
|
dev->state = PN532_STATE_IDLE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PN532IOC_WRITE_TAG_DATA:
|
|
{
|
|
FAR struct pn_mifare_tag_data_s *tag_data =
|
|
(FAR struct pn_mifare_tag_data_s *) arg;
|
|
|
|
if (tag_data != 0)
|
|
{
|
|
/* 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,
|
|
(FAR uint8_t *) &tag_data->data,
|
|
sizeof(tag_data->data));
|
|
|
|
dev->state = PN532_STATE_IDLE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PN532IOC_SET_SAM_CONF:
|
|
pn532_sam_config(dev, (FAR struct pn_sam_settings_s *) arg);
|
|
break;
|
|
|
|
case PN532IOC_READ_PASSIVE:
|
|
if (dev->state == PN532_STATE_CMD_SENT)
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
*ptr = pn532_read_passive_target_id(dev, 0);
|
|
}
|
|
else
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
*ptr = -1;
|
|
}
|
|
break;
|
|
|
|
case PN532IOC_SET_RF_CONF:
|
|
pn532_set_rf_config(dev, (FAR 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:
|
|
{
|
|
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
|
|
*ptr = pn532_read_passive_target_id(dev, 0);
|
|
}
|
|
break;
|
|
|
|
case PN532IOC_GET_STATE:
|
|
ret = dev->state;
|
|
break;
|
|
|
|
default:
|
|
ctlserr("ERROR: Unrecognized cmd: %d\n", cmd);
|
|
ret = -ENOTTY;
|
|
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 = kmm_malloc(sizeof(struct pn532_dev_s));
|
|
if (!dev)
|
|
{
|
|
ctlserr("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, 0600, dev);
|
|
if (ret < 0)
|
|
{
|
|
ctlserr("ERROR: Failed to register driver: %d\n", ret);
|
|
kmm_free(dev);
|
|
}
|
|
|
|
return ret;
|
|
}
|