| /****************************************************************************** |
| * |
| * Copyright (C) 2018 ST Microelectronics S.A. |
| * |
| * 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. |
| * |
| * |
| ******************************************************************************/ |
| #define LOG_TAG "StEse-T1protocol" |
| #include "T1protocol.h" |
| #include <errno.h> |
| #include <string.h> |
| #include "SpiLayerComm.h" |
| #include "SpiLayerDriver.h" |
| #include "SpiLayerInterface.h" |
| #include "android_logmsg.h" |
| #include "utils-lib/DataMgmt.h" |
| #include "utils-lib/Iso13239CRC.h" |
| #include "utils-lib/Tpdu.h" |
| |
| uint8_t SEQ_NUM_MASTER; |
| uint8_t SEQ_NUM_SLAVE; |
| uint8_t recoveryStatus; |
| T1TProtocol_TransceiveState gNextCmd = Idle; |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_getValidPcb |
| ** |
| ** Description Form a valid pcb according to the Tpdu type, subtype, |
| ** master sequence number,slave sequence number and isLast. |
| ** |
| ** Parameters type - The Tpdu type (IBlock, RBlock or SBlock). |
| ** subtype - The different subtype options(for RBlock). |
| ** numSeqMaster - Master sequence number. |
| ** numseqSlave - Slave sequence number. |
| ** isLast - Indicate if there are more tpdus to send |
| ** within the same APDU. |
| ** |
| ** Returns pcb - Computed PCB. |
| ** |
| *******************************************************************************/ |
| uint8_t T1protocol_getValidPcb(TpduType type, RBlockType subtype, |
| uint8_t numSeqMaster, uint8_t numseqSlave, |
| bool isLast) { |
| uint8_t pcb = 0xFF; |
| |
| switch (type) { |
| case IBlock: |
| pcb = 0x00; |
| // Set the Ns according to the numSeqMaster |
| if (numSeqMaster == 1) { |
| pcb |= (uint8_t)IBLOCK_NS_BIT_MASK; |
| } |
| |
| // If the Tpdu is a part of chain, set the M bit inside the pcb. |
| if (isLast == APDU_PART_IS_NOT_LAST) { |
| pcb |= IBLOCK_M_BIT_MASK; |
| } |
| break; |
| |
| case RBlock: |
| pcb = 0x80; |
| if (subtype == ErrorFree) { |
| // Set the bit for the N(R) |
| pcb |= ((uint8_t)numseqSlave) << 4; |
| } else if (subtype == ChecksumError) { |
| // Set the bits for the subtype checksum error and the N(R) |
| pcb |= 0b00000001; |
| pcb |= ((uint8_t)numseqSlave) << 4; |
| } else if (subtype == OtherErrors) { |
| // Set the bits for the subtype other errors and the N(R) |
| pcb |= 0b00000010; |
| pcb |= ((uint8_t)numseqSlave) << 4; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return pcb; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkResponseTpduChecksum |
| ** |
| ** Description Check if the checksum of a given tpdu is well formed. |
| ** |
| ** Parameters cmdTpdu -The TPDU to check |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkResponseTpduChecksum(Tpdu* respTpdu) { |
| if (ATP.checksumType == CRC) { |
| // Check CRC |
| uint8_t arrayTpdu[TPDU_PROLOGUE_LENGTH + respTpdu->len + TPDU_CRC_LENGTH]; |
| Tpdu_toByteArray(respTpdu, arrayTpdu); |
| if (computeCrc(arrayTpdu, TPDU_PROLOGUE_LENGTH + respTpdu->len) != |
| respTpdu->checksum) { |
| return -1; |
| } |
| } else if (ATP.checksumType == LRC) { |
| // Check LRC |
| // char arrayTpdu[TPDU_PROLOGUE_LENGTH + respTpdu->len + TPDU_LRC_LENGTH]; |
| // TODO: implement compute LRC |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkResponsePcbConsistency |
| ** |
| ** Description Check if the pcb of a given tpdu is valid. |
| ** |
| ** Parameters cmdTpdu -The TPDU to check |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkResponsePcbConsistency(Tpdu* tpdu) { |
| // Get the type of the incoming tpdu |
| TpduType type = Tpdu_getType(tpdu); |
| |
| switch (type) { |
| case IBlock: |
| // Match the IBlock pcb received with the bits that must be 0. If |
| // the result is higher than 0, means some of these bits was set to 1. |
| if ((tpdu->pcb & 0b00011111)) { |
| return -1; |
| } |
| break; |
| |
| case RBlock: |
| // Match the RBlock pcb received with the bits that must be 0. If |
| // the result is higher than 0, means some of these bits was set to 1. |
| if ((tpdu->pcb & 0b01101100)) { |
| return -1; |
| } |
| break; |
| |
| case SBlock: |
| // Match the SBlock pcb received with the bits that must be 0. If |
| // the result is higher than 0, means some of these bits was set to 1. |
| if ((tpdu->pcb & 0b00010000)) { |
| return -1; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkResponseLenConsistency |
| ** |
| ** Description Check if the length field of a given tpdu is valid. |
| ** |
| ** Parameters cmdTpdu -The TPDU to check |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkResponseLenConsistency(Tpdu* tpdu) { |
| // Check the length consistency according to the block type |
| TpduType type = Tpdu_getType(tpdu); |
| |
| switch (type) { |
| case IBlock: |
| // If the last Tpdu received was an IBlock, the len must be lower or |
| // equal than the ATP ifsd field. |
| if (tpdu->len > ATP.ifsc) { |
| return -1; |
| } |
| break; |
| |
| case RBlock: |
| // If the last Tpdu received was an RBlock, the len must be 0. |
| if (tpdu->len > 0) { |
| return -1; |
| } |
| break; |
| |
| case SBlock: |
| // If the last Tpdu received was an SBlock WTX... or IFS..., the length |
| // must be 1. If the last Tpdu received was an SBlock |
| // ABORT... or RESYNCH... the length must be 0. |
| if ((tpdu->pcb == (uint8_t)SBLOCK_WTX_REQUEST_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_WTX_RESPONSE_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_IFS_REQUEST_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_IFS_RESPONSE_MASK)) { |
| if (tpdu->len != 1) { |
| return -1; |
| } |
| } else if ((tpdu->pcb == (uint8_t)SBLOCK_ABORT_REQUEST_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_ABORT_RESPONSE_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_RESYNCH_REQUEST_MASK) || |
| (tpdu->pcb == (uint8_t)SBLOCK_RESYNCH_RESPONSE_MASK)) { |
| if (tpdu->len != 0) { |
| return -1; |
| } |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkResponseSeqNumberConsistency |
| ** |
| ** Description Check if the sequence number of a given tpdu is valid. |
| ** |
| ** Parameters cmdTpdu -The TPDU to check |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkResponseSeqNumberConsistency(Tpdu* tpdu) { |
| // Check the length consistency according to the block type |
| TpduType type = Tpdu_getType(tpdu); |
| |
| uint8_t seqNumber; |
| |
| switch (type) { |
| case IBlock: |
| // CHeck if the sequence number received in the last IBlock matches the |
| // expected. |
| seqNumber = (tpdu->pcb & 0b01000000) >> 6; |
| if (seqNumber != SEQ_NUM_SLAVE) { |
| return -1; |
| } |
| break; |
| |
| case RBlock: |
| // TODO |
| // If the original command Tpdu was Iblock chained, both sequence |
| // numbers are expected. If the original command Tpdu was Iblock |
| // without chaining an Rblock with sequence number different than the |
| // actual master sequence number is considered as invalid. |
| /*if ((cmdTpdu->pcb & IBLOCK_M_BIT_MASK) == 0) { |
| // Original Iblock without chaining |
| if (T1protocol_isSequenceNumberOk( |
| cmdTpdu, |
| respTpdu) == false) { |
| // Sequence number different from actual master sequence number |
| return -1; |
| } |
| }*/ |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkSBlockResponseConsistency |
| ** |
| ** Description Check if an SBlock response was received after having |
| ** transmitted a SBlock request. |
| ** |
| ** Parameters lastCmdTpduSent - Last Tpdu sent. |
| ** lastRespTpduReceived - Response received. |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkSBlockResponseConsistency(Tpdu* lastCmdTpduSent, |
| Tpdu* lastRespTpduReceived) { |
| // Check if last Tpdu received was an SBlock(...response) after having |
| // transmitted a SBlock(...request). |
| |
| if ((lastCmdTpduSent->pcb == (uint8_t)SBLOCK_WTX_REQUEST_MASK) || |
| (lastCmdTpduSent->pcb == (uint8_t)SBLOCK_ABORT_REQUEST_MASK) || |
| (lastCmdTpduSent->pcb == (uint8_t)SBLOCK_IFS_REQUEST_MASK) || |
| (lastCmdTpduSent->pcb == (uint8_t)SBLOCK_RESYNCH_REQUEST_MASK) || |
| (lastCmdTpduSent->pcb == (uint8_t)SBLOCK_SWRESET_REQUEST_MASK)) { |
| uint8_t expectedPcbResponse; |
| // Calculate the expected response according to the SBlock request |
| // previously sent. |
| expectedPcbResponse = lastCmdTpduSent->pcb | 0b00100000; |
| |
| if (expectedPcbResponse != lastRespTpduReceived->pcb) { |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_checkTpduConsistency |
| ** |
| ** Description Check if the response TPDU is consistent |
| ** (check the checksum, check if the pcb is valid and the |
| ** expected one and check the len consistency). |
| ** |
| ** Parameters lastCmdTpduSent - Last Tpdu sent. |
| ** lastRespTpduReceived - Last response from the slave. |
| ** |
| ** Returns 0 If checksum is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_checkTpduConsistency(Tpdu* lastCmdTpduSent, |
| Tpdu* lastRespTpduReceived) { |
| // Check checksum |
| if (T1protocol_checkResponseTpduChecksum(lastRespTpduReceived) == -1) { |
| return -1; |
| } |
| |
| // Check pcb consistency |
| if (T1protocol_checkResponsePcbConsistency(lastRespTpduReceived) == -1) { |
| return -1; |
| } |
| |
| // Check len consistency |
| if (T1protocol_checkResponseLenConsistency(lastRespTpduReceived) == -1) { |
| return -1; |
| } |
| |
| // Check sequence number consistency |
| if (T1protocol_checkResponseSeqNumberConsistency(lastRespTpduReceived) == |
| -1) { |
| return -1; |
| } |
| |
| // Check if a valid sBlock response has been received after having |
| // transmitted an sBlock request |
| if (T1protocol_checkSBlockResponseConsistency(lastCmdTpduSent, |
| lastRespTpduReceived) == -1) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_resetSequenceNumbers |
| ** |
| ** Description Set the sequence numbers to it's initial values. |
| ** |
| ** Parameters none |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void T1protocol_resetSequenceNumbers() { |
| // Set the sequence numbers to it's initial values. |
| SEQ_NUM_MASTER = 0; |
| SEQ_NUM_SLAVE = 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_updateMasterSequenceNumber |
| ** |
| ** Description Update the master sequence number. |
| ** |
| ** Parameters none |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void T1protocol_updateMasterSequenceNumber() { |
| // Sequence numbers are module 2, |
| SEQ_NUM_MASTER++; |
| SEQ_NUM_MASTER %= 2; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_updateSlaveSequenceNumber |
| ** |
| ** Description Update the slave sequence number. |
| ** |
| ** Parameters none |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void T1protocol_updateSlaveSequenceNumber() { |
| // Sequence numbers are module 2, |
| SEQ_NUM_SLAVE++; |
| SEQ_NUM_SLAVE %= 2; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_processIBlock |
| ** |
| ** Description Process the last IBlock received from the slave. |
| ** |
| ** Parameters originalCmdTpdu - Original Tpdu sent. |
| ** lastRespTpduReceived - Last response from the slave. |
| ** |
| ** Returns 0 If all went is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_processIBlock(Tpdu* originalCmdTpdu, |
| Tpdu* lastRespTpduReceived) { |
| // The last IBlock received was the good one. Update the sequence |
| // numbers needed. |
| int rc = 0; |
| TpduType type = Tpdu_getType(originalCmdTpdu); |
| |
| T1protocol_updateSlaveSequenceNumber(); |
| rc = DataMgmt_StoreDataInList(lastRespTpduReceived->len, |
| lastRespTpduReceived->data); |
| |
| if ((lastRespTpduReceived->pcb & IBLOCK_M_BIT_MASK) > 0) { |
| gNextCmd = R_ACK; |
| } else { |
| if (type == IBlock) { |
| T1protocol_updateMasterSequenceNumber(); |
| } |
| gNextCmd = Idle; |
| } |
| return rc; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_processRBlock |
| ** |
| ** Description Process the last RBlock received from the slave. |
| ** |
| ** Parameters originalCmdTpdu - Original Tpdu sent. |
| ** lastRespTpduReceived - Last response from the slave. |
| ** |
| ** Returns -1 if the retransmission needed fails, 0 if no more |
| ** retransmission were needed and 1 if extra retransmission |
| ** success. |
| ** |
| *******************************************************************************/ |
| void T1protocol_processRBlock(Tpdu* originalCmdTpdu, |
| Tpdu* lastRespTpduReceived) { |
| if ((originalCmdTpdu->pcb & IBLOCK_M_BIT_MASK) > 0) { |
| // Last IBlock sent was chained. Expected RBlock(NS+1) for error free |
| // operation and RBlock(NS) if something well bad. |
| if (T1protocol_isSequenceNumberOk(originalCmdTpdu, lastRespTpduReceived) == |
| false) { |
| STLOG_HAL_E("Wrong Seq number. Send again "); |
| gNextCmd = I_block; |
| } else { |
| T1protocol_updateMasterSequenceNumber(); |
| gNextCmd = Idle; |
| } |
| } else { |
| // Last IBlock sent wasn't chained. If we receive an RBlock(NS) means |
| // retransmission of the original IBlock, otherwise do resend request. |
| if (T1protocol_isSequenceNumberOk(originalCmdTpdu, lastRespTpduReceived) == |
| true) { |
| STLOG_HAL_D("%s : Need retransmissiom :", __func__); |
| gNextCmd = I_block; |
| } else { |
| gNextCmd = S_Resync_REQ; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_sendRBlock |
| ** |
| ** Description Send a R-block to the card. |
| ** |
| ** Parameters rack - if 1, send a ack frame, nack otherwise. |
| ** lastRespTpduReceived - Last response from the slave. |
| ** |
| ** Returns bytesRead if data was read, 0 if timeout expired with |
| ** no response, -1 otherwise |
| ** |
| *******************************************************************************/ |
| int T1protocol_sendRBlock(int rack, Tpdu* lastRespTpduReceived) { |
| int result = 0; |
| Tpdu* TempTpdu = (Tpdu*)malloc(sizeof(Tpdu)); |
| TempTpdu->data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| |
| result = Tpdu_formTpdu( |
| NAD_HOST_TO_SLAVE, |
| T1protocol_getValidPcb(RBlock, rack ? ErrorFree : OtherErrors, 0, |
| SEQ_NUM_SLAVE, 0), |
| 0, NULL, TempTpdu); |
| if (result == -1) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| result = SpiLayerInterface_transcieveTpdu(TempTpdu, lastRespTpduReceived, |
| DEFAULT_NBWT); |
| if (result < 0) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return result; |
| } |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_formSblockResponse |
| ** |
| ** Description Form a SBlock response according to a given SBlock Request. |
| ** |
| ** Parameters responseTpdu - A valid SBlock response according to the |
| ** SBlock request in the requestTpdu param. |
| ** requestTpdu - Sblock request received from the eSE to |
| ** process. |
| ** |
| ** Returns 0 If all went is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_formSblockResponse(Tpdu* responseTpdu, Tpdu* requestTpdu) { |
| uint8_t i; |
| |
| responseTpdu->nad = NAD_HOST_TO_SLAVE; |
| responseTpdu->pcb = requestTpdu->pcb | 0b00100000; |
| responseTpdu->len = requestTpdu->len; |
| for (i = 0; i < requestTpdu->len; i++) { |
| responseTpdu->data[i] = requestTpdu->data[i]; |
| } |
| responseTpdu->checksum = 0x0000; |
| |
| if (ATP.checksumType == CRC) { |
| uint8_t buffer[TPDU_PROLOGUE_LENGTH + responseTpdu->len + TPDU_CRC_LENGTH]; |
| Tpdu_toByteArray(responseTpdu, buffer); |
| responseTpdu->checksum = |
| computeCrc(buffer, (TPDU_PROLOGUE_LENGTH + responseTpdu->len)); |
| } else if (ATP.checksumType == LRC) { |
| // char buffer[TPDU_PROLOGUE_LENGTH + responseTpdu->len + TPDU_LRC_LENGTH]; |
| // TODO |
| STLOG_HAL_E("LRC still not implemented."); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_processSBlock |
| ** |
| ** Description Process the last SBlock received from the slave. |
| ** |
| ** Parameters originalCmdTpdu - Original Tpdu sent. |
| ** lastCmdTpduSent - Last Tpdu sent. |
| ** lastRespTpduReceived - Last response from the slave. |
| ** |
| ** Returns 0 If all went is ok, -1 otherwise. |
| ** |
| *******************************************************************************/ |
| int T1protocol_processSBlock(Tpdu* originalCmdTpdu, Tpdu* lastCmdTpduSent, |
| Tpdu* lastRespTpduReceived) { |
| int rc; |
| if (lastRespTpduReceived->pcb == (uint8_t)SBLOCK_WTX_REQUEST_MASK) { |
| gNextCmd = S_WTX_RES; |
| } else if (lastRespTpduReceived->pcb == (uint8_t)SBLOCK_IFS_REQUEST_MASK) { |
| gNextCmd = S_IFS_RES; |
| } else if (lastRespTpduReceived->pcb == (uint8_t)SBLOCK_IFS_RESPONSE_MASK) { |
| ATP.ifsc = (uint8_t)lastRespTpduReceived->data[0]; |
| return 0; |
| } else if (lastRespTpduReceived->pcb == |
| (uint8_t)SBLOCK_RESYNCH_REQUEST_MASK) { |
| T1protocol_resetSequenceNumbers(); |
| gNextCmd = S_Resync_RES; |
| } else if (lastRespTpduReceived->pcb == |
| (uint8_t)SBLOCK_RESYNCH_RESPONSE_MASK) { |
| T1protocol_resetSequenceNumbers(); |
| // Reset the sequence number of the original Tpdu if needed |
| if ((originalCmdTpdu->pcb & IBLOCK_NS_BIT_MASK) > 0) { |
| originalCmdTpdu->pcb &= ~IBLOCK_NS_BIT_MASK; |
| |
| rc = Tpdu_formTpdu(originalCmdTpdu->nad, originalCmdTpdu->pcb, |
| originalCmdTpdu->len, originalCmdTpdu->data, |
| originalCmdTpdu); |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| |
| Tpdu_copy(lastCmdTpduSent, originalCmdTpdu); |
| gNextCmd = I_block; |
| |
| } else if (lastRespTpduReceived->pcb == (uint8_t)SBLOCK_ABORT_REQUEST_MASK) { |
| // TODO |
| STLOG_HAL_E("ABORT request received still not supported."); |
| return -1; |
| } else if (lastRespTpduReceived->pcb == |
| (uint8_t)SBLOCK_SWRESET_RESPONSE_MASK) { |
| if (Atp_setAtp(lastRespTpduReceived->data) != 0) { |
| STLOG_HAL_E("Error setting ATP"); |
| return -1; |
| } |
| |
| T1protocol_resetSequenceNumbers(); |
| // SW Reset done |
| return -1; |
| } |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_isSequenceNumberOk |
| ** |
| ** Description Check if the sequence number of the response TPDU is the |
| ** expected one. |
| ** |
| ** Parameters originalTpdu - Original tpdu sent. |
| ** respTpdu - The last response received from the slave. |
| ** |
| ** Returns true If sequence number is ok, false otherwise. |
| ** |
| *******************************************************************************/ |
| bool T1protocol_isSequenceNumberOk(Tpdu* originalTpdu, Tpdu* respTpdu) { |
| int seqNumber; |
| |
| // Get the type of the TPDU and act consequently |
| TpduType tpduType = Tpdu_getType(respTpdu); |
| |
| switch (tpduType) { |
| case IBlock: |
| seqNumber = (respTpdu->pcb & 0b01000000) >> 6; |
| if (seqNumber == SEQ_NUM_SLAVE) { |
| return true; |
| } else { |
| return false; |
| } |
| break; |
| |
| case RBlock: |
| // If the original Tpdu sent was chained, the expected sequence number |
| // inside the RBlock i the next master sequence number. |
| // If the original Tpdu sent wans't chained, no RBlock expected. If an |
| // RBlock with sequence number equal to the master sequence number is |
| // received, retransmission is needed, otherwise retransmission request |
| // is needed. |
| // TODO |
| if ((originalTpdu->pcb & IBLOCK_M_BIT_MASK) > 0) { |
| seqNumber = (respTpdu->pcb & 0x10) >> 4; |
| if (seqNumber == ((SEQ_NUM_MASTER + 1) % 2)) { |
| return true; |
| } else { |
| return false; |
| } |
| } else { |
| seqNumber = (respTpdu->pcb & 0x10) >> 4; |
| if (seqNumber == SEQ_NUM_MASTER) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return false; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_updateRecoveryStatus |
| ** |
| ** Description Updates the recovery state to the following step. |
| ** |
| ** Parameters none |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void T1protocol_updateRecoveryStatus() { |
| switch (recoveryStatus) { |
| case RECOVERY_STATUS_OK: |
| STLOG_HAL_D("recoveryStatus: OK -> RESEND 1"); |
| recoveryStatus = RECOVERY_STATUS_RESEND_1; |
| break; |
| |
| case RECOVERY_STATUS_RESEND_1: |
| STLOG_HAL_D("recoveryStatus: RESEND 1 -> RESYNC 1"); |
| recoveryStatus = RECOVERY_STATUS_RESYNC_1; |
| break; |
| |
| case RECOVERY_STATUS_RESYNC_1: |
| STLOG_HAL_D("recoveryStatus: RESYNC 1 -> WARM RESET"); |
| recoveryStatus = RECOVERY_STATUS_WARM_RESET; |
| break; |
| |
| case RECOVERY_STATUS_WARM_RESET: |
| STLOG_HAL_D("recoveryStatus: WARM_RESET (recovery completed)"); |
| recoveryStatus = RECOVERY_STATUS_KO; |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_setRespApduData |
| ** |
| ** Description Copy the data in the response Tpdu into the respApduBuffer. |
| ** |
| ** Parameters respTpdu - Response tpdu where the data is stored. |
| ** respApduBuffer - Apdu buffer to store the data received |
| ** in the response Tpdu. |
| ** |
| ** Returns The amount of data bytes saved into the apdu buffer. |
| ** |
| *******************************************************************************/ |
| uint8_t T1protocol_setRespApduData(Tpdu* respTpdu, uint8_t** respApduBuffer) { |
| uint8_t i; |
| STLOG_HAL_D("%s : Enter", __func__); |
| |
| for (i = 0; i < respTpdu->len; i++) { |
| (*respApduBuffer)[i] = respTpdu->data[i]; |
| } |
| |
| return respTpdu->len; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doWTXResponse |
| ** |
| ** Description If the eSE send a S(WTX request), acknowledge it by sending |
| ** a S(WTX response) |
| ** |
| ** Parameters lastRespTpduReceived - Last response received. |
| ** |
| ** Returns bytesRead if data was read, 0 if timeout expired with |
| ** no response, -1 otherwise |
| ** |
| *******************************************************************************/ |
| int T1protocol_doWTXResponse(Tpdu* lastRespTpduReceived) { |
| Tpdu* TempTpdu = (Tpdu*)malloc(sizeof(Tpdu)); |
| TempTpdu->data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| // Form a SBlock Resynch request Tpdu to sent. |
| int result = Tpdu_formTpdu(NAD_HOST_TO_SLAVE, SBLOCK_WTX_RESPONSE_MASK, 0, |
| NULL, TempTpdu); |
| T1protocol_formSblockResponse(TempTpdu, lastRespTpduReceived); |
| if (result == -1) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| |
| // Send the SBlock and read the response from the slave. |
| result = SpiLayerInterface_transcieveTpdu(TempTpdu, lastRespTpduReceived, |
| DEFAULT_NBWT); |
| if (result < 0) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return result; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doResendRequest |
| ** |
| ** Description First thing to do in the recovery mechanism is to ask |
| ** for a retransmission. |
| ** |
| ** Parameters lastCmdTpduSent - Last Tpdu sent |
| ** lastRespTpduReceived - Last response received. |
| ** bytesRead - If a retransmission occurs, this |
| ** field contains the amount of bytes read from the slave |
| ** in the new transaction. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_doResendRequest(Tpdu* lastCmdTpduSent, |
| Tpdu* lastRespTpduReceived, int* bytesRead) { |
| // Form a RBlock - other errors tpdu with the expected sequence number to |
| // receive. |
| int result = Tpdu_formTpdu( |
| NAD_HOST_TO_SLAVE, |
| T1protocol_getValidPcb(RBlock, OtherErrors, 0, SEQ_NUM_SLAVE, 0), 0, NULL, |
| lastCmdTpduSent); |
| if (result == -1) { |
| return -1; |
| } |
| |
| // Send the RBlock an read the response |
| result = SpiLayerInterface_transcieveTpdu(lastCmdTpduSent, |
| lastRespTpduReceived, DEFAULT_NBWT); |
| if (result < 0) { |
| return -1; |
| } |
| *bytesRead = result; |
| return 1; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doResyncRequest |
| ** |
| ** Description Second thing to do in the recovery mechanism if the resend |
| ** fails is to perform a Resync. |
| ** |
| ** Parameters lastCmdTpduSent - Last Tpdu sent |
| ** lastRespTpduReceived - Last response received. |
| ** bytesRead - If a retransmission occurs, this |
| ** field contains the amount of bytes read from the slave |
| ** in the new transaction. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_doResyncRequest(Tpdu* lastRespTpduReceived) { |
| Tpdu* TempTpdu = (Tpdu*)malloc(sizeof(Tpdu)); |
| TempTpdu->data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| // Form a SBlock Resynch request Tpdu to sent. |
| int result = Tpdu_formTpdu(NAD_HOST_TO_SLAVE, SBLOCK_RESYNCH_REQUEST_MASK, 0, |
| NULL, TempTpdu); |
| if (result == -1) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| |
| // Send the SBlock and read the response from the slave. |
| result = SpiLayerInterface_transcieveTpdu(TempTpdu, lastRespTpduReceived, |
| DEFAULT_NBWT); |
| if (result < 0) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return result; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doSoftReset |
| ** |
| ** Description Third thing to do in the recovery mechanism is to send |
| ** a software reset to reset SPI interface. |
| ** |
| ** Parameters lastRespTpduReceived - memory position whre to store the |
| ** response. |
| ** |
| ** Returns 1 if interface reseted, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_doSoftReset(Tpdu* lastRespTpduReceived) { |
| Tpdu* TempTpdu = (Tpdu*)malloc(sizeof(Tpdu)); |
| TempTpdu->data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| // Form a SBlock Resynch request Tpdu to sent. |
| int result = Tpdu_formTpdu(NAD_HOST_TO_SLAVE, SBLOCK_SWRESET_REQUEST_MASK, 0, |
| NULL, TempTpdu); |
| if (result == -1) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| |
| // Send the SBlock and read the response from the slave. |
| result = SpiLayerInterface_transcieveTpdu(TempTpdu, lastRespTpduReceived, |
| DEFAULT_NBWT); |
| if (result < 0) { |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return -1; |
| } |
| free(TempTpdu->data); |
| free(TempTpdu); |
| return result; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doRecovery |
| ** |
| ** Description Implements the recovery mechanism when a non-consistent |
| ** TPDU has been received or no response has been received |
| ** before the timeout. |
| ** |
| ** Parameters lastCmdTpduSent - Last Tpdu sent |
| ** lastRespTpduReceived - Last response received. |
| ** bytesRead - If a retransmission occurs, this |
| ** field contains the amount of bytes read from the slave |
| ** in the new transaction. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_doRecovery() { |
| STLOG_HAL_W("Entering recovery"); |
| |
| // Update the recovery status |
| T1protocol_updateRecoveryStatus(); |
| |
| // Do the resend request or the resynck request according to the recovery |
| // status |
| switch (recoveryStatus) { |
| case RECOVERY_STATUS_RESEND_1: |
| case RECOVERY_STATUS_RESEND_2: |
| gNextCmd = R_Other_Error; |
| break; |
| case RECOVERY_STATUS_RESYNC_1: |
| case RECOVERY_STATUS_RESYNC_2: |
| case RECOVERY_STATUS_RESYNC_3: |
| gNextCmd = S_Resync_REQ; |
| break; |
| case RECOVERY_STATUS_WARM_RESET: |
| |
| // At this point, we consider that SE is dead and a reboot is requried |
| gNextCmd = S_SWReset_REQ; |
| break; |
| case RECOVERY_STATUS_KO: |
| default: |
| return -1; |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_handleTpduResponse |
| ** |
| ** Description Handles any TPDU response iteratively. |
| ** |
| ** Parameters originalCmdTpdu - Original Tpdu sent. |
| ** lastCmdTpduSent - Last Tpdu sent |
| ** lastRespTpduReceived - Last response received. |
| ** bytesRead - If a retransmission occurs, this |
| ** field contains the amount of bytes read from the slave |
| ** in the new transaction. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_handleTpduResponse(Tpdu* originalCmdTpdu, Tpdu* lastCmdTpduSent, |
| Tpdu* lastRespTpduReceived, int* bytesRead) { |
| int rc = 0; |
| STLOG_HAL_D("%s : Enter :", __func__); |
| |
| // If the last transmission ends without response from the slave, do |
| // recovery mechanism. |
| if (*bytesRead == 0) { |
| STLOG_HAL_D("bytesRead = 0 -> Going into recovery."); |
| rc = T1protocol_doRecovery(); |
| return rc; |
| } |
| |
| // Check the consistency of the last received tpdu |
| rc = T1protocol_checkTpduConsistency(lastCmdTpduSent, lastRespTpduReceived); |
| if (rc < 0) { |
| STLOG_HAL_D("%s : TPDU consistency check failed -> Going into recovery.", |
| __func__); |
| rc = T1protocol_doRecovery(); |
| return rc; |
| } |
| |
| // Reset the recovery if a valid Tpdu has been received from the slave |
| if (recoveryStatus != RECOVERY_STATUS_OK) { |
| recoveryStatus = RECOVERY_STATUS_OK; |
| } |
| |
| // If all went OK, process the last tpdu received |
| TpduType type = Tpdu_getType(lastRespTpduReceived); |
| switch (type) { |
| case IBlock: |
| rc = T1protocol_processIBlock(originalCmdTpdu, lastRespTpduReceived); |
| break; |
| |
| case RBlock: |
| T1protocol_processRBlock(originalCmdTpdu, lastRespTpduReceived); |
| break; |
| |
| case SBlock: |
| rc = T1protocol_processSBlock(originalCmdTpdu, lastCmdTpduSent, |
| lastRespTpduReceived); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_formCommandTpduToSend |
| ** |
| ** Description Form a valid Tpdu to send according to the if we need to |
| ** send an IBlock or a RBlock. |
| ** |
| ** Parameters cmdApduPart - Data to sent within an IBlock. |
| ** cmdLength - Amount of data to sent. |
| ** isLast - Flag if there are more data to send. |
| ** cmdTpdu - Resulting Tpdu. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_formCommandTpduToSend(uint8_t* cmdApduPart, uint8_t cmdLength, |
| bool isLast, Tpdu* cmdTpdu) { |
| STLOG_HAL_D("%s : Enter ", __func__); |
| if (cmdLength == 0) { |
| // Send RBlock to get the pending IBlock responses from the slave |
| if (Tpdu_formTpdu( |
| NAD_HOST_TO_SLAVE, |
| T1protocol_getValidPcb(RBlock, ErrorFree, 0, SEQ_NUM_SLAVE, isLast), |
| 0, cmdApduPart, cmdTpdu) == -1) { |
| STLOG_HAL_E("Error forming an RBlock to send."); |
| return -1; |
| } |
| } else { |
| // Send IBlock containing the data in cmdApduPart. Set it as chained if |
| // isLast is false. |
| if (Tpdu_formTpdu(NAD_HOST_TO_SLAVE, |
| T1protocol_getValidPcb(IBlock, ErrorFree, SEQ_NUM_MASTER, |
| 0, isLast), |
| cmdLength, cmdApduPart, cmdTpdu) == -1) { |
| STLOG_HAL_E("Error forming an IBlock to send."); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_doRequestIFS |
| ** |
| ** Description Send a IFS request to negotiate the IFSD value. Use the same |
| ** value for IFSD than the IFSC received in the ATP. |
| ** |
| ** Parameters None |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_doRequestIFS() { |
| Tpdu originalCmdTpdu, lastCmdTpduSent, lastRespTpduReceived; |
| originalCmdTpdu.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| lastCmdTpduSent.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| lastRespTpduReceived.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| |
| STLOG_HAL_D("%s : Enter ", __func__); |
| // Form a SBlock Resynch request Tpdu to sent. |
| int result = Tpdu_formTpdu(NAD_HOST_TO_SLAVE, SBLOCK_IFS_REQUEST_MASK, 1, |
| &ATP.ifsc, &originalCmdTpdu); |
| if (result) { |
| return result; |
| } |
| |
| Tpdu_copy(&lastCmdTpduSent, &originalCmdTpdu); |
| |
| // Send the SBlock and read the response from the slave. |
| result = SpiLayerInterface_transcieveTpdu( |
| &lastCmdTpduSent, &lastRespTpduReceived, DEFAULT_NBWT); |
| if (result <= 0) { |
| return -1; |
| } |
| |
| result = T1protocol_handleTpduResponse(&originalCmdTpdu, &lastCmdTpduSent, |
| &lastRespTpduReceived, &result); |
| |
| free(originalCmdTpdu.data); |
| originalCmdTpdu.data = NULL; |
| free(lastCmdTpduSent.data); |
| lastCmdTpduSent.data = NULL; |
| free(lastRespTpduReceived.data); |
| lastRespTpduReceived.data = NULL; |
| return result; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_init |
| ** |
| ** Description Initializes the T1 Protocol. |
| ** |
| ** Parameters tSpiDriver - hardware information |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_init(SpiDriver_config_t* tSpiDriver) { |
| STLOG_HAL_D("%s : Enter ", __func__); |
| if (SpiLayerInterface_init(tSpiDriver) != 0) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function T1protocol_transcieveApduPart |
| ** |
| ** Description Send and/or receive an APDU part. |
| ** |
| ** Parameters cmdApduPart - cmdApdu part that shall be sent |
| ** cmdLength - Length of the cmdApduPart to be sent. |
| ** isLast - APDU_PART_IS_NOT_LAST/APDU_PART_IS_LAST |
| ** pRsp - Structure to the response buffer and length. |
| ** |
| ** Returns 0 if everything went fine, -1 if something failed. |
| ** |
| *******************************************************************************/ |
| int T1protocol_transcieveApduPart(uint8_t* cmdApduPart, uint8_t cmdLength, |
| bool isLast, StEse_data* pRsp) { |
| Tpdu originalCmdTpdu, lastCmdTpduSent, lastRespTpduReceived; |
| originalCmdTpdu.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| lastCmdTpduSent.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| lastRespTpduReceived.data = (uint8_t*)malloc(ATP.ifsc * sizeof(uint8_t)); |
| StEse_data pRes; |
| |
| memset(&pRes, 0x00, sizeof(StEse_data)); |
| STLOG_HAL_D("%s : Enter", __func__); |
| |
| // Form the cmdTpdu according to the cmdApduPart, cmdLength and isLast |
| // fields. |
| if (T1protocol_formCommandTpduToSend(cmdApduPart, cmdLength, isLast, |
| &originalCmdTpdu) < 0) { |
| return -1; |
| } |
| |
| // Send the command Tpdu and receive the response. |
| int rc; |
| recoveryStatus = RECOVERY_STATUS_OK; |
| Tpdu_copy(&lastCmdTpduSent, &originalCmdTpdu); |
| |
| gNextCmd = I_block; |
| while (gNextCmd != 0) { |
| switch (gNextCmd) { |
| case I_block: |
| rc = SpiLayerInterface_transcieveTpdu( |
| &originalCmdTpdu, &lastRespTpduReceived, DEFAULT_NBWT); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| |
| case R_ACK: |
| rc = T1protocol_sendRBlock(true, &lastRespTpduReceived); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| case R_Other_Error: |
| rc = T1protocol_sendRBlock(false, &lastRespTpduReceived); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| |
| case S_Resync_REQ: |
| rc = T1protocol_doResyncRequest(&lastRespTpduReceived); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| |
| case S_SWReset_REQ: |
| rc = T1protocol_doSoftReset(&lastRespTpduReceived); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| |
| case S_WTX_RES: |
| rc = T1protocol_doWTXResponse(&lastRespTpduReceived); |
| if (rc < 0) { |
| return rc; |
| } |
| break; |
| |
| default: |
| return -1; |
| break; |
| } |
| |
| rc = T1protocol_handleTpduResponse(&originalCmdTpdu, &lastCmdTpduSent, |
| &lastRespTpduReceived, &rc); |
| |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| TpduType type = Tpdu_getType(&lastRespTpduReceived); |
| |
| if ((type == IBlock) && (DataMgmt_GetData(&pRes.len, &pRes.p_data) != 0)) { |
| return -1; |
| } |
| |
| pRsp->len = pRes.len; |
| pRsp->p_data = pRes.p_data; |
| |
| free(originalCmdTpdu.data); |
| originalCmdTpdu.data = NULL; |
| free(lastCmdTpduSent.data); |
| lastCmdTpduSent.data = NULL; |
| free(lastRespTpduReceived.data); |
| lastRespTpduReceived.data = NULL; |
| |
| if ((lastRespTpduReceived.pcb & IBLOCK_M_BIT_MASK) > 0) { |
| return 1; |
| } |
| |
| return 0; |
| } |