mbed-os/connectivity/drivers/nfc/PN512/source/transceiver/pn512.c

357 lines
10 KiB
C

/*
* Copyright (c) 2013-2018, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed 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.
*/
/**
* \file pn512.c
* \copyright Copyright (c) ARM Ltd 2013
* \author Donatien Garnier
* \details PN512 implementation of the transceiver interface
*/
#define __DEBUG__ 4
#ifndef __MODULE__
#define __MODULE__ "pn512.c"
#endif
#include "stack/nfc_errors.h"
#include "stdlib.h"
#include "acore/ac_buffer.h"
#include "transceiver/transceiver.h"
#include "transceiver/protocols.h"
#include "pn512_rf.h"
#include "pn512_registers.h"
#include "pn512_cmd.h"
#include "pn512_hw.h"
#include "pn512_irq.h"
#include "pn512_poll.h"
#include "pn512_transceive.h"
#include "pn512_internal.h"
#include "pn512.h"
#define DEFAULT_READER_TRANSCEIVE_TIMEOUT 100
#define DEFAULT_TARGET_TRANSCEIVE_TIMEOUT -1
//Prototypes
#include "pn512_internal.h"
/** \addtogroup PN512
* @{
* \name Transceiver
* \details Implementation of the transceiver interface
* @{
*/
//PN 512 VTABLE
static const transceiver_impl_t pn512_impl = {
.set_protocols = pn512_set_protocols,
.poll = pn512_poll,
.transceive = pn512_transceive,
.abort = pn512_abort,
.set_crc = pn512_set_crc,
.set_timeout = pn512_set_timeout,
.set_transceive_options = pn512_set_transceive_options,
.set_transceive_framing = pn512_set_transceive_framing,
.set_write = pn512_set_write,
.get_read = pn512_get_read,
.set_last_byte_length = pn512_set_last_byte_length,
.get_last_byte_length = pn512_get_last_byte_length,
.set_first_byte_align = pn512_set_first_byte_align,
.close = pn512_close,
.sleep = pn512_sleep
};
/** Initialize PN512 transceiver
* \param pPN512 pointer to pn512_t structure to initialize
* \param pTransport pointer to already initialized nfc_transport_t structure
* \return NFC_OK (0) on success or NFC_ERR_* error on failure
*/
nfc_err_t pn512_init(pn512_t *pPN512, nfc_transport_t *pTransport, nfc_scheduler_timer_t *pTimer)
{
////
//For Self-test
////
#if NFC_PN512_SELFTEST
const uint8_t null_array[25] = {0};
#endif
////
uint8_t r;
//Init transceiver
transceiver_init((nfc_transceiver_t *)pPN512, pTransport, pTimer);
//Init buffer
ac_buffer_builder_init(&pPN512->readBufBldr, pPN512->payload, 256);
pPN512->readFirstByteAlign = 0;
pPN512->readLastByteLength = 8;
pPN512->writeLastByteLength = 8;
//Populate functions
pPN512->transceiver.fn = &pn512_impl;
//Init variables
memset(&pPN512->config.initiators, 0, sizeof(nfc_tech_t));
memset(&pPN512->config.targets, 0, sizeof(nfc_tech_t));
pPN512->timeout = -1;
pPN512->nextFrameMode = pn512_transceive_mode_transceive;
pn512_hw_init(pPN512);
pn512_registers_init(pPN512); //Cannot switch page now
pn512_cmd_init(pPN512);
pn512_cmd_exec(pPN512, PN512_CMD_SOFTRST);
pn512_cmd_wait_idle(pPN512, -1);
//pn512_registers_init(pPN512);
//Put into known state
pn512_registers_reset(pPN512);
pPN512->transceive.mode = pn512_transceive_mode_idle;
pn512_irq_clear(pPN512, PN512_IRQ_ALL);
pn512_fifo_clear(pPN512);
pn512_cmd_exec(pPN512, PN512_CMD_IDLE);
pn512_cmd_wait_idle(pPN512, -1);
pn512_rf_field_switch_off(pPN512);
//Required for polling loop
srand(4242);
#if NFC_PN512_SELFTEST // Self test
pn512_cmd_exec(pPN512, PN512_CMD_SOFTRST);
pn512_cmd_wait_idle(pPN512, -1);
const uint8_t null_array_buf[25] = {0}; //FIXME
ac_buffer_t null_array;
ac_buffer_init(&null_array, null_array_buf, 25);
//Perform self test
pn512_fifo_write(pPN512, &null_array);
pn512_cmd_exec(pPN512, PN512_CMD_CONFIG);
while (pn512_cmd_get(pPN512) != PN512_CMD_IDLE);
pn512_register_write(pPN512, PN512_REG_AUTOTEST, 0x09);
ac_buffer_init(&null_array, null_array_buf, 1);
pn512_fifo_write(pPN512, &null_array);
pn512_cmd_exec(pPN512, PN512_CMD_CRC);
while (pn512_cmd_get(pPN512) != PN512_CMD_IDLE);
DBGX_ENTER();
NFC_DBG("Test result:");
while (pn512_fifo_length(pPN512)) {
ac_buffer_builder_t read_byte;
ac_buffer_builder_init(&read_byte, null_array_buf, 1);
pn512_fifo_read(pPN512, &read_byte);
DBGX("%02x ", null_array_buf[0]);
}
DBGX("\n");
DBGX_LEAVE();
#endif
r = pn512_register_read(pPN512, PN512_REG_VERSION);
NFC_DBG_BLOCK(
NFC_DBG("PN512 version %02x", r);
)
if ((r != 0x82) && (r != 0xB1) && (r != 0xB2)) {
return NFC_ERR_UNSUPPORTED; //PN512 not found
}
return NFC_OK;
}
/** Get pointer to nfc_transceiver_t structure
* \param pPN512 pointer to pn512_t instance
* \return pointer to initialized nfc_transceiver_t instance
*/
nfc_transceiver_t *pn512_get_transceiver(pn512_t *pPN512)
{
return &pPN512->transceiver;
}
void pn512_set_protocols(nfc_transceiver_t *pTransceiver, nfc_tech_t initiators, nfc_tech_t targets, polling_options_t options)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
//If different, reconfigure
if (memcmp(&initiators, &pPN512->config.initiators, sizeof(nfc_tech_t)) || memcmp(&targets, &pPN512->config.targets, sizeof(nfc_tech_t))) {
pPN512->config.initiators = initiators;
if (memcmp(&targets, &pPN512->config.targets, sizeof(nfc_tech_t))) {
pPN512->config.targets = targets;
pn512_poll_setup(pPN512);
}
pTransceiver->initiator_ntarget = false;
memset(&pTransceiver->active_tech, 0, sizeof(nfc_tech_t));
}
pPN512->config.options = options;
}
void pn512_poll(nfc_transceiver_t *pTransceiver)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
pPN512->nextFrameMode = pn512_transceive_mode_transceive;
pn512_poll_hw(pPN512, pn512_transceiver_callback);
}
void pn512_set_crc(nfc_transceiver_t *pTransceiver, bool crc_out, bool crc_in)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
pn512_framing_crc_set(pPN512, crc_out, crc_in);
}
void pn512_set_timeout(nfc_transceiver_t *pTransceiver, int timeout)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
pPN512->timeout = timeout;
}
void pn512_set_transceive_options(nfc_transceiver_t *pTransceiver, bool transmit, bool receive, bool repoll)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
if (transmit && receive) {
pPN512->nextFrameMode = pn512_transceive_mode_transceive;
} else if (transmit && repoll) {
pPN512->nextFrameMode = pn512_transceive_mode_transmit_and_target_autocoll;
} else if (transmit) {
pPN512->nextFrameMode = pn512_transceive_mode_transmit;
} else if (receive) {
pPN512->nextFrameMode = pn512_transceive_mode_receive;
} else {
pPN512->nextFrameMode = pn512_transceive_mode_target_autocoll;
}
}
void pn512_set_transceive_framing(nfc_transceiver_t *pTransceiver, nfc_framing_t framing)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
pn512_framing_set(pPN512, framing);
//Switch NFC tech if NFC DEP
if (pTransceiver->active_tech.nfc_nfc_dep_a
|| pTransceiver->active_tech.nfc_nfc_dep_f_212
|| pTransceiver->active_tech.nfc_nfc_dep_f_424) {
//FIXME
pTransceiver->active_tech.nfc_nfc_dep_a = 0;
pTransceiver->active_tech.nfc_nfc_dep_f_212 = 0;
pTransceiver->active_tech.nfc_nfc_dep_f_424 = 0;
switch (framing) {
case nfc_framing_target_a_106:
case nfc_framing_initiator_a_106:
pTransceiver->active_tech.nfc_nfc_dep_a = 1;
break;
case nfc_framing_target_f_212:
case nfc_framing_initiator_f_212:
pTransceiver->active_tech.nfc_nfc_dep_f_212 = 1;
break;
case nfc_framing_target_f_424:
case nfc_framing_initiator_f_424:
pTransceiver->active_tech.nfc_nfc_dep_f_424 = 1;
break;
default:
break;
}
}
}
void pn512_set_write(nfc_transceiver_t *pTransceiver, ac_buffer_t *pWriteBuf)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
if (pWriteBuf == NULL) {
ac_buffer_init(&pPN512->writeBuf, NULL, 0);
return;
}
ac_buffer_dup(&pPN512->writeBuf, pWriteBuf);
}
ac_buffer_t *pn512_get_read(nfc_transceiver_t *pTransceiver)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
return ac_buffer_builder_buffer(&pPN512->readBufBldr);
}
void pn512_set_last_byte_length(nfc_transceiver_t *pTransceiver, size_t lastByteLength)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
if ((lastByteLength > 8) || (lastByteLength == 0)) {
lastByteLength = 8;
}
pPN512->writeLastByteLength = lastByteLength;
}
void pn512_set_first_byte_align(nfc_transceiver_t *pTransceiver, size_t firstByteAlign)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
firstByteAlign &= 0x7;
pPN512->readFirstByteAlign = firstByteAlign;
}
size_t pn512_get_last_byte_length(nfc_transceiver_t *pTransceiver)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
return pPN512->readLastByteLength;
}
void pn512_transceive(nfc_transceiver_t *pTransceiver)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
pn512_transceive_hw(pPN512, pPN512->nextFrameMode, pn512_transceiver_callback);
pPN512->nextFrameMode = pn512_transceive_mode_transceive;
}
void pn512_abort(nfc_transceiver_t *pTransceiver)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
nfc_scheduler_dequeue_task(&pTransceiver->scheduler, true, &pPN512->transceiver.task);
}
void pn512_close(nfc_transceiver_t *pTransceiver)
{
//pn512_t* pPN512 = (pn512_t*) pTransceiver;
(void) pTransceiver;
//TODO
return;
}
void pn512_sleep(nfc_transceiver_t *pTransceiver, bool sleep)
{
pn512_t *pPN512 = (pn512_t *) pTransceiver;
if (sleep) {
pn512_register_write(pPN512, PN512_REG_COMMAND, 0x30); //Receiver off + soft power down
} else {
pn512_register_write(pPN512, PN512_REG_COMMAND, 0x00);
while (pn512_register_read(pPN512, PN512_REG_COMMAND) & 0x10);
}
}
void pn512_transceiver_callback(pn512_t *pPN512, nfc_err_t ret)
{
transceiver_callback(&pPN512->transceiver, ret);
}
/**
* @}
* @}
* */