mirror of https://github.com/ARMmbed/mbed-os.git
686 lines
24 KiB
C
686 lines
24 KiB
C
/*
|
|
* Copyright (c) 2015-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 isodep_target.c
|
|
* \copyright Copyright (c) ARM Ltd 2015
|
|
* \author Donatien Garnier
|
|
*/
|
|
|
|
#define __DEBUG__ 0
|
|
#ifndef __MODULE__
|
|
#define __MODULE__ "isodep_target.c"
|
|
#endif
|
|
|
|
#include "isodep_target.h"
|
|
|
|
#include "stack/nfc_errors.h"
|
|
#include "transceiver/transceiver.h"
|
|
|
|
//Private defines
|
|
#define RATS 0xE0
|
|
#define FSDI_TO_FSD(x) (((x)<5)?(((x)<<3) + 16):((x)<8)?(((x)<<5)-96):256)
|
|
#define FSC_TO_FSCI(x) (((x)<=48)?(((x)-16)>>3):((x)<=128)?(((x)+96)>>5):8) //returns the closest lower or equal value
|
|
#define DID(x) ((x) & 0x0F)
|
|
#define FSDI(x) (((x) >> 4) & 0x0F)
|
|
|
|
#define FSCI_TO_FSC(x) FSDI_TO_FSD(x)
|
|
#define FSD_TO_FSDI(x) FSC_TO_FSCI(x)
|
|
|
|
//#define DI_TO_D(x) (1 << (x))
|
|
#define DI_TO_BITRATE(x) ((RF_BITRATE)((x) + RF_BITRATE_106K))
|
|
|
|
|
|
#define GET_FRAME_TYPE(pcb) ((((pcb) & 0xF0) == 0xD0)?PPS_FRAME:(((pcb) & 0xC0) == (0x00 << 6))?I_FRAME:((((pcb) & 0xC0) == (0x2 << 6))?R_FRAME:S_FRAME))
|
|
|
|
#define I_BLOCK_PCB 0
|
|
#define R_BLOCK_PCB 2
|
|
#define S_BLOCK_PCB 3
|
|
|
|
#define PCB_TYPE(pcb) (((pcb)>>6)&0x03)
|
|
|
|
#define BUILD_I_BLOCK_PCB(chaining, cid, nad, block_toggle) ( (0x0 << 6) | (((chaining)?1:0) << 4) \
|
|
| (((cid)?1:0) << 3) | (((nad)?1:0) << 2) | (1 << 1) | (((block_toggle)?1:0)) )
|
|
#define BUILD_S_BLOCK_PCB(cid, wtx_n_deselect) ( (0x3 << 6) | (((wtx_n_deselect)?0x3:0) << 4) \
|
|
| (((cid)?1:0) << 3) | (1 << 1) )
|
|
#define BUILD_R_BLOCK_PCB(cid, block_toggle, nak) ( (0x2 << 6) | (1 <<5) | (((nak)?1:0) << 4) \
|
|
| (((cid)?1:0) << 3) | (1 << 1) | (((block_toggle)?1:0)) )
|
|
|
|
#define PCB_IS_CID(pcb) (((pcb) & (1 << 3))?true:false)
|
|
#define PCB_BLOCK_TOGGLE(pcb) (((pcb) & 1)?true:false)
|
|
#define PCB_CHAINING(pcb) (((pcb) & 0x10)?true:false)
|
|
#define PCB_NACK(pcb) (((pcb) & 0x10)?true:false)
|
|
#define PCB_WTX(pcb) (((pcb)&0x30)==0x30)
|
|
|
|
#define WTXM_DEFAULT 10
|
|
|
|
//Parameters
|
|
#define FSC 256 //Maximum frame size the PICC (us) can receive -- TODO should be a parameter at some point -- linked to PN512 buffer
|
|
#define SFGI 2 //Guard time ~ 1.2ms
|
|
//#define FWI 6 //Max time before answer is ~ 19.3ms
|
|
#define FWI 14 //Max time before answer is ~ 19.3ms
|
|
|
|
typedef enum __dep_type dep_type_t;
|
|
enum __dep_type {
|
|
dep_type_information,
|
|
dep_type_response,
|
|
dep_type_supervisory,
|
|
};
|
|
|
|
//Local functions
|
|
static void dep_init(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
static bool dep_ready(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
|
|
static void dep_req_information(nfc_tech_isodep_target_t *pIsodepTarget, ac_buffer_t *pReq, bool moreInformation, uint8_t blockNumber);
|
|
static void dep_req_response(nfc_tech_isodep_target_t *pIsodepTarget, bool ack, uint8_t blockNumber);
|
|
static void dep_req_supervisory(nfc_tech_isodep_target_t *pIsodepTarget, bool wtxNDeselect, uint8_t wtxm);
|
|
|
|
static dep_type_t dep_res_type(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
static void dep_res_information(nfc_tech_isodep_target_t *pIsodepTarget, size_t maxLength, ac_buffer_t **ppRes, bool *pMoreInformation, uint8_t *pBlockNumber);
|
|
static void dep_res_response(nfc_tech_isodep_target_t *pIsodepTarget, bool *pAck, uint8_t *pBlockNumber);
|
|
static void dep_res_supervisory(nfc_tech_isodep_target_t *pIsodepTarget, bool *pWtxNDeselect, uint8_t *pWtxm);
|
|
|
|
static void dep_disconnected(nfc_tech_isodep_target_t *pIsodepTarget, bool deselected);
|
|
|
|
static void command_init(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
|
|
static nfc_err_t command_ats_req(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
static nfc_err_t command_dep_req(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
|
|
static nfc_err_t command_ats_res(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
static nfc_err_t command_dep_res(nfc_tech_isodep_target_t *pIsodepTarget);
|
|
|
|
static void command_reply(nfc_tech_isodep_target_t *pIsodepTarget, bool depWait);
|
|
static void command_transceiver_cb(nfc_transceiver_t *pTransceiver, nfc_err_t ret, void *pUserData);
|
|
|
|
//High-level Target functions
|
|
void nfc_tech_isodep_target_init(nfc_tech_isodep_target_t *pIsodepTarget, nfc_transceiver_t *pTransceiver,
|
|
ac_buffer_t *pHist, nfc_tech_isodep_disconnected_cb disconnectedCb, void *pUserData)
|
|
{
|
|
pIsodepTarget->pTransceiver = pTransceiver;
|
|
|
|
pIsodepTarget->pHist = pHist;
|
|
|
|
pIsodepTarget->disconnectedCb = disconnectedCb;
|
|
pIsodepTarget->pUserData = pUserData;
|
|
|
|
dep_init(pIsodepTarget);
|
|
command_init(pIsodepTarget);
|
|
}
|
|
|
|
nfc_err_t nfc_tech_isodep_target_connect(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
NFC_DBG("Connecting");
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_CONNECTING;
|
|
|
|
transceiver_set_crc(pIsodepTarget->pTransceiver, true, true);
|
|
command_transceiver_cb(pIsodepTarget->pTransceiver, NFC_OK, pIsodepTarget);
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
void nfc_tech_isodep_target_disconnect(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
// This should not be called within a callback
|
|
|
|
transceiver_abort(pIsodepTarget->pTransceiver);
|
|
|
|
dep_disconnected(pIsodepTarget, false);
|
|
}
|
|
|
|
nfc_err_t nfc_tech_isodep_target_transmit(nfc_tech_isodep_target_t *pIsodepTarget, ac_istream_t *pStream, nfc_tech_isodep_cb_t cb, void *pUserData)
|
|
{
|
|
if (pIsodepTarget->dep.pReqStream != NULL) {
|
|
return NFC_ERR_BUSY;
|
|
}
|
|
|
|
pIsodepTarget->dep.pResStream = pStream;
|
|
pIsodepTarget->dep.resCb = cb;
|
|
pIsodepTarget->dep.pResUserData = pUserData;
|
|
|
|
//Do we need to start transceiving?
|
|
if (pIsodepTarget->commands.state == ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD) {
|
|
command_reply(pIsodepTarget, false); //Force reply
|
|
}
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
nfc_err_t nfc_tech_isodep_target_receive(nfc_tech_isodep_target_t *pIsodepTarget, ac_ostream_t *pStream, nfc_tech_isodep_cb_t cb, void *pUserData)
|
|
{
|
|
if (pIsodepTarget->dep.pResStream != NULL) {
|
|
return NFC_ERR_BUSY;
|
|
}
|
|
|
|
pIsodepTarget->dep.pReqStream = pStream;
|
|
pIsodepTarget->dep.reqCb = cb;
|
|
pIsodepTarget->dep.pReqUserData = pUserData;
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
//DEP Layer
|
|
void dep_init(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
//ac_buffer_init(&pIsodepTarget->dep.res, NULL, 0);
|
|
pIsodepTarget->dep.pReqStream = NULL;
|
|
pIsodepTarget->dep.pResStream = NULL;
|
|
|
|
pIsodepTarget->dep.reqCb = NULL;
|
|
pIsodepTarget->dep.pReqUserData = NULL;
|
|
|
|
pIsodepTarget->dep.resCb = NULL;
|
|
pIsodepTarget->dep.pResUserData = NULL;
|
|
|
|
pIsodepTarget->dep.blockNumber = 1; //Rule C
|
|
|
|
//pIsodepTarget->dep.pduState = ISO_DEP_TARGET_DEP_PDU_IDLE;
|
|
pIsodepTarget->dep.chaining = false;
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_IDLE;
|
|
}
|
|
|
|
bool dep_ready(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
//Anything to send back?
|
|
if (pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED) {
|
|
return true;
|
|
}
|
|
|
|
if ((pIsodepTarget->dep.pResStream != NULL)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void dep_req_information(nfc_tech_isodep_target_t *pIsodepTarget, ac_buffer_t *pReq, bool moreInformation, uint8_t blockNumber)
|
|
{
|
|
(void) blockNumber;
|
|
|
|
pIsodepTarget->dep.blockNumber++;
|
|
pIsodepTarget->dep.blockNumber %= 2;
|
|
|
|
// Note: callbacks can call nfc_tech_isodep_target_transmit() - however we must make sure that we wait AFTER this routine has been processed to actually transmit
|
|
// To do so, reset state to ATS_RES_SENT state
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT;
|
|
|
|
if (!pIsodepTarget->dep.chaining
|
|
&& (pIsodepTarget->dep.pResStream != NULL)) {
|
|
//Sent the full frame
|
|
pIsodepTarget->dep.pResStream = NULL;
|
|
pIsodepTarget->dep.resCb((nfc_tech_isodep_t *)pIsodepTarget, NFC_OK, pIsodepTarget->dep.pResUserData);
|
|
}
|
|
if (pIsodepTarget->dep.pReqStream != NULL) {
|
|
// Pull more
|
|
ac_ostream_push(pIsodepTarget->dep.pReqStream, pReq, !moreInformation);
|
|
if (!moreInformation) {
|
|
//Got the full frame
|
|
pIsodepTarget->dep.pReqStream = NULL;
|
|
pIsodepTarget->dep.reqCb((nfc_tech_isodep_t *)pIsodepTarget, NFC_OK, pIsodepTarget->dep.pReqUserData);
|
|
}
|
|
}
|
|
|
|
// Update state
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED;
|
|
pIsodepTarget->dep.chaining = moreInformation;
|
|
}
|
|
|
|
void dep_req_response(nfc_tech_isodep_target_t *pIsodepTarget, bool ack, uint8_t blockNumber)
|
|
{
|
|
if (blockNumber != pIsodepTarget->dep.blockNumber) {
|
|
//Should be NACK
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_NACK_DIFF_BLOCK_NUMBER_RECEIVED;
|
|
} else {
|
|
if (ack) {
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_ACK_RECEIVED;
|
|
} else {
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED;
|
|
}
|
|
}
|
|
}
|
|
|
|
void dep_req_supervisory(nfc_tech_isodep_target_t *pIsodepTarget, bool wtxNDeselect, uint8_t wtxm)
|
|
{
|
|
(void) wtxm;
|
|
|
|
if (wtxNDeselect) {
|
|
if ((pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_WTX_SENT)) {
|
|
NFC_WARN("Unexpected WTX frame");
|
|
}
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_WTX_RECEIVED;
|
|
} else {
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED;
|
|
}
|
|
}
|
|
|
|
dep_type_t dep_res_type(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
dep_type_t depType;
|
|
switch (pIsodepTarget->dep.frameState) {
|
|
case ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED:
|
|
depType = dep_type_supervisory; //Deselect
|
|
break;
|
|
case ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED:
|
|
case ISO_DEP_TARGET_DEP_FRAME_WTX_RECEIVED:
|
|
if (pIsodepTarget->dep.chaining) { //Need to ack?
|
|
depType = dep_type_response;
|
|
} else if (pIsodepTarget->dep.pResStream != NULL) { //Anything to send back?
|
|
depType = dep_type_information;
|
|
} else {
|
|
depType = dep_type_supervisory; //WTX
|
|
}
|
|
break;
|
|
case ISO_DEP_TARGET_DEP_FRAME_ACK_RECEIVED:
|
|
if ((pIsodepTarget->dep.pResStream != NULL) && (pIsodepTarget->dep.chaining)) {
|
|
depType = dep_type_information;
|
|
} else {
|
|
depType = dep_type_supervisory; //WTX
|
|
}
|
|
break;
|
|
case ISO_DEP_TARGET_DEP_FRAME_NACK_DIFF_BLOCK_NUMBER_RECEIVED:
|
|
depType = dep_type_response; //Should send ACK
|
|
break;
|
|
case ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED:
|
|
depType = dep_type_information;
|
|
break;
|
|
default:
|
|
depType = dep_type_supervisory; //ATN
|
|
break;
|
|
}
|
|
return depType;
|
|
}
|
|
|
|
void dep_res_information(nfc_tech_isodep_target_t *pIsodepTarget, size_t maxLength, ac_buffer_t **ppRes, bool *pMoreInformation, uint8_t *pBlockNumber)
|
|
{
|
|
*pBlockNumber = pIsodepTarget->dep.blockNumber;
|
|
if (pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED) {
|
|
if (pIsodepTarget->dep.pResStream != NULL) {
|
|
bool lastFrame = true;
|
|
ac_istream_pull(pIsodepTarget->dep.pResStream, &pIsodepTarget->dep.res, &lastFrame, maxLength);
|
|
pIsodepTarget->dep.chaining = !lastFrame;
|
|
}
|
|
} else {
|
|
//Retransmit previous frame (leave it as it is)
|
|
}
|
|
*ppRes = &pIsodepTarget->dep.res;
|
|
*pMoreInformation = pIsodepTarget->dep.chaining;
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_INFORMATION_SENT;
|
|
}
|
|
|
|
void dep_res_response(nfc_tech_isodep_target_t *pIsodepTarget, bool *pAck, uint8_t *pBlockNumber)
|
|
{
|
|
//Continue chaining or send ACK
|
|
*pAck = true;
|
|
*pBlockNumber = pIsodepTarget->dep.blockNumber;
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_ACK_SENT;
|
|
}
|
|
|
|
void dep_res_supervisory(nfc_tech_isodep_target_t *pIsodepTarget, bool *pWtxNDeselect, uint8_t *pWtxm)
|
|
{
|
|
if (pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED) {
|
|
*pWtxNDeselect = false;
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT;
|
|
} else {
|
|
*pWtxNDeselect = true;
|
|
*pWtxm = WTXM_DEFAULT;
|
|
pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_WTX_SENT;
|
|
}
|
|
}
|
|
|
|
void dep_disconnected(nfc_tech_isodep_target_t *pIsodepTarget, bool deselected)
|
|
{
|
|
//Call callbacks if needed
|
|
if (pIsodepTarget->dep.pReqStream != NULL) {
|
|
pIsodepTarget->dep.reqCb((nfc_tech_isodep_t *)pIsodepTarget, NFC_ERR_DISCONNECTED, pIsodepTarget->dep.pReqUserData);
|
|
pIsodepTarget->dep.pReqStream = NULL;
|
|
}
|
|
if (pIsodepTarget->dep.pReqStream != NULL) {
|
|
pIsodepTarget->dep.resCb((nfc_tech_isodep_t *)pIsodepTarget, NFC_ERR_DISCONNECTED, pIsodepTarget->dep.pResUserData);
|
|
pIsodepTarget->dep.pResStream = NULL;
|
|
}
|
|
if (pIsodepTarget->commands.state != ISO_DEP_TARGET_COMMANDS_DISCONNECTED) {
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DISCONNECTED;
|
|
pIsodepTarget->disconnectedCb((nfc_tech_isodep_t *)pIsodepTarget, deselected, pIsodepTarget->pUserData);
|
|
}
|
|
}
|
|
|
|
//Commands Layer
|
|
void command_init(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
ac_buffer_builder_init(&pIsodepTarget->commands.respBldr, pIsodepTarget->commands.respBuf, sizeof(pIsodepTarget->commands.respBuf));
|
|
pIsodepTarget->commands.pReq = NULL;
|
|
// Update if/when we support DIDs
|
|
//pIsodepTarget->commands.did = 0;
|
|
//pIsodepTarget->commands.didUsed = false;
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DISCONNECTED;
|
|
pIsodepTarget->commands.inPayloadSize = 0;
|
|
}
|
|
|
|
nfc_err_t command_ats_req(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
//Check we are in a correct state -- this should be the first command received
|
|
if (pIsodepTarget->commands.state != ISO_DEP_TARGET_COMMANDS_CONNECTING) {
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
|
|
if (ac_buffer_reader_readable(pIsodepTarget->commands.pReq) < 2) {
|
|
NFC_ERR("Payload too short");
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
|
|
ac_buffer_read_n_skip(pIsodepTarget->commands.pReq, 1);
|
|
|
|
uint8_t b = ac_buffer_read_nu8(pIsodepTarget->commands.pReq);
|
|
|
|
//Save DID -- not supported for now
|
|
//pIsodepTarget->commands.did = DID(b);
|
|
|
|
uint8_t fsdi = FSDI(b);
|
|
pIsodepTarget->commands.inPayloadSize = FSDI_TO_FSD(fsdi);
|
|
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_REQ_RECVD;
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
nfc_err_t command_dep_req(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
if (pIsodepTarget->commands.state < ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT) {
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
|
|
if (ac_buffer_reader_readable(pIsodepTarget->commands.pReq) < 1) {
|
|
NFC_ERR("Payload too short");
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
|
|
uint8_t pcb = ac_buffer_read_nu8(pIsodepTarget->commands.pReq);
|
|
|
|
// Udpate if/when we support DIDs
|
|
/*
|
|
if( pfb & PFB_DID )
|
|
{
|
|
uint8_t did = ac_buffer_read_nu8(pIsodepTarget->commands.pReq);
|
|
if( pIsodepTarget->commands.did != did )
|
|
{
|
|
//Not for us
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
pIsodepTarget->commands.didUsed = true;
|
|
}
|
|
else
|
|
{
|
|
pIsodepTarget->commands.didUsed = false;
|
|
}
|
|
|
|
if( pfb & PFB_NAD )
|
|
{
|
|
ac_buffer_read_nu8(pIsodepTarget->commands.pReq); //Skip NAD
|
|
}
|
|
*/
|
|
|
|
uint8_t wtxm = 0;
|
|
switch (PCB_TYPE(pcb)) {
|
|
case I_BLOCK_PCB:
|
|
dep_req_information(pIsodepTarget, pIsodepTarget->commands.pReq, PCB_CHAINING(pcb), PCB_BLOCK_TOGGLE(pcb));
|
|
break;
|
|
case R_BLOCK_PCB:
|
|
dep_req_response(pIsodepTarget, !PCB_NACK(pcb), PCB_BLOCK_TOGGLE(pcb));
|
|
break;
|
|
case S_BLOCK_PCB:
|
|
if (PCB_WTX(pcb)) {
|
|
if (ac_buffer_reader_readable(pIsodepTarget->commands.pReq) < 1) {
|
|
NFC_ERR("Payload too short");
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
wtxm = ac_buffer_read_nu8(pIsodepTarget->commands.pReq);
|
|
}
|
|
dep_req_supervisory(pIsodepTarget, PCB_WTX(pcb), wtxm);
|
|
break;
|
|
default:
|
|
NFC_ERR("PCB is invalid");
|
|
return NFC_ERR_PROTOCOL;
|
|
}
|
|
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD;
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
nfc_err_t command_ats_res(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
//Send ATS back
|
|
if (ac_buffer_builder_writable(&pIsodepTarget->commands.respBldr) < 5) {
|
|
return NFC_ERR_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (5 + ac_buffer_size(pIsodepTarget->pHist)) & 0xFF);
|
|
|
|
//T0
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0x7 << 4) | FSC_TO_FSCI(FSC)); //TA(1), TB(1) and TC(1) are transmitted
|
|
|
|
//TA(1)
|
|
//For now only 106kbps supported
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (1 << 7) | (0x0 << 4) | 0x0);
|
|
|
|
//TODO when supporting other bitrates
|
|
//ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0 << 7) | (0x3 << 4) | 0x3); //106, 212, 414 kbps bitrates
|
|
|
|
//TB(1)
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (FWI << 4) | SFGI); //Specify guard-time and time between frames
|
|
|
|
//TC(1)
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0 << 1) | 0); //DID not supported, NAD not supported
|
|
|
|
ac_buffer_set_next(ac_buffer_builder_buffer(&pIsodepTarget->commands.respBldr), pIsodepTarget->pHist); //Queue general bytes
|
|
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT;
|
|
|
|
//TODO PPS
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
nfc_err_t command_dep_res(nfc_tech_isodep_target_t *pIsodepTarget)
|
|
{
|
|
uint8_t pcb = 0;
|
|
|
|
// If/when supporting DIDs
|
|
/*
|
|
if( pIsodepTarget->commands.didUsed )
|
|
{
|
|
pcb |= PFB_DID;
|
|
}*/
|
|
|
|
ac_buffer_t *pDepBuf = NULL;
|
|
bool moreInformation = false;
|
|
bool ack = false;
|
|
bool wtxNDeselect = false;
|
|
uint8_t wtxm = 0;
|
|
uint8_t blockNumber = 0;
|
|
|
|
size_t maxLength = pIsodepTarget->commands.inPayloadSize - 1;
|
|
|
|
switch (dep_res_type(pIsodepTarget)) {
|
|
case dep_type_information:
|
|
dep_res_information(pIsodepTarget, maxLength, &pDepBuf, &moreInformation, &blockNumber);
|
|
pcb = BUILD_I_BLOCK_PCB(moreInformation, false, false, blockNumber);
|
|
break;
|
|
case dep_type_response:
|
|
dep_res_response(pIsodepTarget, &ack, &blockNumber);
|
|
pcb = BUILD_R_BLOCK_PCB(0, blockNumber, !ack);
|
|
break;
|
|
case dep_type_supervisory:
|
|
dep_res_supervisory(pIsodepTarget, &wtxNDeselect, &wtxm);
|
|
pcb = BUILD_S_BLOCK_PCB(0, wtxNDeselect);
|
|
break;
|
|
}
|
|
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, pcb);
|
|
/*
|
|
if( pIsodepTarget->commands.didUsed )
|
|
{
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, pIsodepTarget->commands.did);
|
|
}
|
|
*/
|
|
if (pDepBuf != NULL) {
|
|
ac_buffer_set_next(ac_buffer_builder_buffer(&pIsodepTarget->commands.respBldr), pDepBuf);
|
|
} else if (wtxNDeselect) {
|
|
ac_buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, wtxm);
|
|
}
|
|
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DEP_RES_SENT;
|
|
|
|
return NFC_OK;
|
|
}
|
|
|
|
void command_reply(nfc_tech_isodep_target_t *pIsodepTarget, bool depWait)
|
|
{
|
|
nfc_err_t ret;
|
|
|
|
//Check whether we want to reply or wait for the higher layer to send us something
|
|
if ((pIsodepTarget->commands.state == ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD) && depWait && !dep_ready(pIsodepTarget)) {
|
|
return;
|
|
}
|
|
|
|
//Reply
|
|
ac_buffer_builder_reset(&pIsodepTarget->commands.respBldr);
|
|
|
|
switch (pIsodepTarget->commands.state) {
|
|
case ISO_DEP_TARGET_COMMANDS_ATS_REQ_RECVD:
|
|
ret = command_ats_res(pIsodepTarget);
|
|
break;
|
|
case ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD:
|
|
ret = command_dep_res(pIsodepTarget);
|
|
break;
|
|
default:
|
|
NFC_ERR("Unknown state %d", pIsodepTarget->commands.state);
|
|
//Go back to receive mode
|
|
nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
return;
|
|
}
|
|
|
|
if (ret) {
|
|
NFC_ERR("Error %d", ret);
|
|
//Go back to receive mode
|
|
nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
return;
|
|
}
|
|
|
|
NFC_DBG("Transceive");
|
|
|
|
if (pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT) {
|
|
transceiver_set_transceive_options(pIsodepTarget->pTransceiver, true, false, true);
|
|
} else {
|
|
transceiver_set_transceive_options(pIsodepTarget->pTransceiver, true, true, false);
|
|
}
|
|
|
|
NFC_DBG_BLOCK(ac_buffer_dump(ac_buffer_builder_buffer(&pIsodepTarget->commands.respBldr));)
|
|
|
|
//Send next frame
|
|
transceiver_set_write(pIsodepTarget->pTransceiver, ac_buffer_builder_buffer(&pIsodepTarget->commands.respBldr));
|
|
nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
|
|
NFC_DBG("Processed");
|
|
}
|
|
|
|
void command_transceiver_cb(nfc_transceiver_t *pTransceiver, nfc_err_t ret, void *pUserData)
|
|
{
|
|
nfc_tech_isodep_target_t *pIsodepTarget = (nfc_tech_isodep_target_t *) pUserData;
|
|
|
|
if (ret == NFC_ERR_ABORTED) {
|
|
// Just return
|
|
return;
|
|
}
|
|
|
|
if (pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT) {
|
|
NFC_DBG("Deselect sent and re-polled: %u", ret);
|
|
//We are now disconnected (deselected)
|
|
//Reset status
|
|
dep_init(pIsodepTarget);
|
|
command_init(pIsodepTarget);
|
|
|
|
transceiver_set_crc(pIsodepTarget->pTransceiver, true, true);
|
|
pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_CONNECTING;
|
|
|
|
//Call so that we can reinit higher layer
|
|
dep_disconnected(pIsodepTarget, true); //This will call us again
|
|
return;
|
|
}
|
|
|
|
//Prepare default empty reply
|
|
transceiver_set_write(pTransceiver, NULL);
|
|
transceiver_set_transceive_options(pTransceiver, false, true, false);
|
|
|
|
if (ret == NFC_ERR_FIELD) {
|
|
NFC_WARN("Lost initiator");
|
|
dep_disconnected(pIsodepTarget, false);
|
|
return;
|
|
} else if (ret) {
|
|
//We should ignore this error and wait for another frame
|
|
NFC_WARN("Got invalid frame (error %d)", ret);
|
|
|
|
nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
return;
|
|
}
|
|
|
|
NFC_DBG("Reading data from initiator");
|
|
ac_buffer_t *pDataInitiator = transceiver_get_read(pTransceiver); //In buffer
|
|
|
|
NFC_DBG_BLOCK(ac_buffer_dump(pDataInitiator);)
|
|
|
|
//Framing is handled by transceiver
|
|
if ((ac_buffer_reader_readable(pDataInitiator) < 1)) {
|
|
NFC_ERR("Empty initiator message");
|
|
|
|
//Go back to receive mode
|
|
nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
return;
|
|
}
|
|
|
|
pIsodepTarget->commands.pReq = pDataInitiator;
|
|
|
|
//Duplicate to peek on req
|
|
ac_buffer_t dataInitiatorDup;
|
|
ac_buffer_dup(&dataInitiatorDup, pDataInitiator);
|
|
uint8_t req = ac_buffer_read_nu8(&dataInitiatorDup);
|
|
|
|
switch (req) {
|
|
case RATS:
|
|
ret = command_ats_req(pIsodepTarget);
|
|
break;
|
|
default:
|
|
ret = command_dep_req(pIsodepTarget);
|
|
break;
|
|
}
|
|
|
|
if (ret) {
|
|
NFC_ERR("Error %d", ret);
|
|
|
|
//Go back to receive mode
|
|
nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget);
|
|
return;
|
|
}
|
|
|
|
NFC_DBG("Reply");
|
|
|
|
//Reply
|
|
command_reply(pIsodepTarget, true); //Make sure we send a WTX frame if we cannot respond straight away
|
|
}
|
|
|
|
|
|
|