You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
702 lines
25 KiB
702 lines
25 KiB
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "include/ese/teq1.h"
|
|
#include "../libese/include/ese/ese.h"
|
|
#include "../libese/include/ese/log.h"
|
|
|
|
#include "teq1_private.h"
|
|
|
|
const char *teq1_rule_result_to_name(enum RuleResult result) {
|
|
switch (result) {
|
|
case kRuleResultComplete:
|
|
return "Complete";
|
|
case kRuleResultAbort:
|
|
return "Abort";
|
|
case kRuleResultContinue:
|
|
return "Continue";
|
|
case kRuleResultHardFail:
|
|
return "Hard failure";
|
|
case kRuleResultResetDevice:
|
|
return "Reset device";
|
|
case kRuleResultResetSession:
|
|
return "Reset session";
|
|
case kRuleResultRetransmit:
|
|
return "Retransmit";
|
|
case kRuleResultSingleShot:
|
|
return "Single shot";
|
|
};
|
|
}
|
|
|
|
const char *teq1_pcb_to_name(uint8_t pcb) {
|
|
switch (pcb) {
|
|
case I(0, 0):
|
|
return "I(0, 0)";
|
|
case I(0, 1):
|
|
return "I(0, 1)";
|
|
case I(1, 0):
|
|
return "I(1, 0)";
|
|
case I(1, 1):
|
|
return "I(1, 1)";
|
|
case R(0, 0, 0):
|
|
return "R(0, 0, 0)";
|
|
case R(0, 0, 1):
|
|
return "R(0, 0, 1)";
|
|
case R(0, 1, 0):
|
|
return "R(0, 1, 0)";
|
|
case R(0, 1, 1):
|
|
return "R(0, 1, 1)";
|
|
case R(1, 0, 0):
|
|
return "R(1, 0, 0)";
|
|
case R(1, 0, 1):
|
|
return "R(1, 0, 1)";
|
|
case R(1, 1, 0):
|
|
return "R(1, 1, 0)";
|
|
case R(1, 1, 1):
|
|
return "R(1, 1, 1)";
|
|
case S(RESYNC, REQUEST):
|
|
return "S(RESYNC, REQUEST)";
|
|
case S(RESYNC, RESPONSE):
|
|
return "S(RESYNC, RESPONSE)";
|
|
case S(IFS, REQUEST):
|
|
return "S(IFS, REQUEST)";
|
|
case S(IFS, RESPONSE):
|
|
return "S(IFS, RESPONSE)";
|
|
case S(ABORT, REQUEST):
|
|
return "S(ABORT, REQUEST)";
|
|
case S(ABORT, RESPONSE):
|
|
return "S(ABORT, RESPONSE)";
|
|
case S(WTX, REQUEST):
|
|
return "S(WTX, REQUEST)";
|
|
case S(WTX, RESPONSE):
|
|
return "S(WTX, RESPONSE)";
|
|
case 255:
|
|
return "INTERNAL-ERROR";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
void teq1_dump_buf(const char *prefix, const uint8_t *buf, uint32_t len) {
|
|
uint32_t recvd = 0;
|
|
for (recvd = 0; recvd < len; ++recvd)
|
|
ALOGV("%s[%u]: %.2X", prefix, recvd, buf[recvd]);
|
|
}
|
|
|
|
int teq1_transmit(struct EseInterface *ese,
|
|
const struct Teq1ProtocolOptions *opts,
|
|
struct Teq1Frame *frame) {
|
|
/* Set correct node address. */
|
|
frame->header.NAD = opts->node_address;
|
|
|
|
/* Compute the LRC */
|
|
frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
|
|
|
|
/*
|
|
* If the card does something weird, like expect an CRC/LRC based on a
|
|
* different header value, the preprocessing can handle it.
|
|
*/
|
|
if (opts->preprocess) {
|
|
opts->preprocess(opts, frame, 1);
|
|
}
|
|
|
|
/*
|
|
* Begins transmission and ignore errors.
|
|
* Failed transmissions will result eventually in a resync then reset.
|
|
*/
|
|
teq1_trace_transmit(frame->header.PCB, frame->header.LEN);
|
|
teq1_dump_transmit(frame->val, sizeof(frame->header) + frame->header.LEN + 1);
|
|
ese->ops->hw_transmit(ese, frame->val,
|
|
sizeof(frame->header) + frame->header.LEN + 1, 1);
|
|
/*
|
|
* Even though in practice any WTX BWT extension starts when the above
|
|
* transmit ends, it is easier to implement it in the polling timeout of
|
|
* receive.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
int teq1_receive(struct EseInterface *ese,
|
|
const struct Teq1ProtocolOptions *opts, float timeout,
|
|
struct Teq1Frame *frame) {
|
|
/* Poll the bus until we see the start of frame indicator, the interface NAD.
|
|
*/
|
|
int bytes_consumed = ese->ops->poll(ese, opts->host_address, timeout, 0);
|
|
if (bytes_consumed < 0 || bytes_consumed > 1) {
|
|
/* Timed out or comm error. */
|
|
ALOGV("%s: comm error: %d", __func__, bytes_consumed);
|
|
return -1;
|
|
}
|
|
/* We polled for the NAD above -- if it was consumed, set it here. */
|
|
if (bytes_consumed) {
|
|
frame->header.NAD = opts->host_address;
|
|
}
|
|
/* Get the remainder of the header, but keep the line &open. */
|
|
ese->ops->hw_receive(ese, (uint8_t *)(&frame->header.NAD + bytes_consumed),
|
|
sizeof(frame->header) - bytes_consumed, 0);
|
|
teq1_dump_receive((uint8_t *)(&frame->header.NAD + bytes_consumed),
|
|
sizeof(frame->header) - bytes_consumed);
|
|
if (frame->header.LEN == 255) {
|
|
ALOGV("received invalid LEN of 255");
|
|
/* Close the receive window and return failure. */
|
|
ese->ops->hw_receive(ese, NULL, 0, 1);
|
|
return -1;
|
|
}
|
|
/*
|
|
* Get the data and the first byte of CRC data.
|
|
* Note, CRC support is not implemented. Only a single LRC byte is expected.
|
|
*/
|
|
ese->ops->hw_receive(ese, (uint8_t *)(&(frame->INF[0])),
|
|
frame->header.LEN + 1, 1);
|
|
teq1_dump_receive((uint8_t *)(&(frame->INF[0])), frame->header.LEN + 1);
|
|
teq1_trace_receive(frame->header.PCB, frame->header.LEN);
|
|
|
|
/*
|
|
* If the card does something weird, like expect an CRC/LRC based on a
|
|
* different
|
|
* header value, the preprocessing should fix up here prior to the LRC check.
|
|
*/
|
|
if (opts->preprocess) {
|
|
opts->preprocess(opts, frame, 0);
|
|
}
|
|
|
|
/* LRC and other protocol goodness checks are not done here. */
|
|
return frame->header.LEN; /* Return data bytes read. */
|
|
}
|
|
|
|
uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame) {
|
|
uint32_t inf_len = INF_LEN;
|
|
if (state->ifs < inf_len) {
|
|
inf_len = state->ifs;
|
|
}
|
|
switch (bs_get(PCB.type, frame->header.PCB)) {
|
|
case kPcbTypeInfo0:
|
|
case kPcbTypeInfo1: {
|
|
uint32_t len = state->app_data.tx_total;
|
|
uint32_t copied = 0;
|
|
if (len > inf_len) {
|
|
len = inf_len;
|
|
}
|
|
copied = ese_sg_to_buf(state->app_data.tx, state->app_data.tx_count,
|
|
state->app_data.tx_offset, len, frame->INF);
|
|
if (copied != len) {
|
|
ALOGE("Failed to copy %x bytes of app data for transmission",
|
|
frame->header.LEN);
|
|
/* TODO(wad): This return code is largely ignored. Is the precondition
|
|
* checking elsewhere enough? */
|
|
return 255;
|
|
}
|
|
frame->header.LEN = (len & 0xff);
|
|
ALOGV("Copying %x bytes of app data for transmission", frame->header.LEN);
|
|
/* Incrementing here means the caller MUST handle retransmit with prepared
|
|
* data. */
|
|
state->app_data.tx_offset += copied;
|
|
state->app_data.tx_total -= copied;
|
|
/* Perform chained transmission if needed. */
|
|
bs_assign(&frame->header.PCB, PCB.I.more_data, 0);
|
|
if (state->app_data.tx_total > 0) {
|
|
frame->header.PCB |= bs_mask(PCB.I.more_data, 1);
|
|
}
|
|
return len;
|
|
}
|
|
case kPcbTypeSupervisory:
|
|
case kPcbTypeReceiveReady:
|
|
default:
|
|
break;
|
|
}
|
|
return 255; /* Invalid block type. */
|
|
}
|
|
|
|
void teq1_get_app_data(struct Teq1State *state, const struct Teq1Frame *frame) {
|
|
switch (bs_get(PCB.type, frame->header.PCB)) {
|
|
case kPcbTypeInfo0:
|
|
case kPcbTypeInfo1: {
|
|
uint32_t len = frame->header.LEN;
|
|
/* TODO(wad): Some data will be left on the table. Should this error out? */
|
|
if (len > state->app_data.rx_total) {
|
|
len = state->app_data.rx_total;
|
|
}
|
|
ese_sg_from_buf(state->app_data.rx, state->app_data.rx_count,
|
|
state->app_data.rx_offset, len, frame->INF);
|
|
/* The original caller must retain the starting pointer to determine
|
|
* actual available data.
|
|
*/
|
|
state->app_data.rx_total -= len;
|
|
state->app_data.rx_offset += len;
|
|
return;
|
|
}
|
|
case kPcbTypeReceiveReady:
|
|
case kPcbTypeSupervisory:
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Returns an R(0) frame with error bits set. */
|
|
uint8_t teq1_frame_error_check(struct Teq1State *state,
|
|
struct Teq1Frame *tx_frame,
|
|
struct Teq1Frame *rx_frame) {
|
|
uint8_t lrc = 0;
|
|
int chained = 0;
|
|
if (rx_frame->header.PCB == 255) {
|
|
return R(0, 1, 0); /* Other error */
|
|
}
|
|
|
|
lrc = teq1_compute_LRC(rx_frame);
|
|
if (rx_frame->INF[rx_frame->header.LEN] != lrc) {
|
|
ALOGE("Invalid LRC %x instead of %x", rx_frame->INF[rx_frame->header.LEN],
|
|
lrc);
|
|
return R(0, 0, 1); /* Parity error */
|
|
}
|
|
|
|
/* Check if we were chained and increment the last sent sequence. */
|
|
switch (bs_get(PCB.type, tx_frame->header.PCB)) {
|
|
case kPcbTypeInfo0:
|
|
case kPcbTypeInfo1:
|
|
chained = bs_get(PCB.I.more_data, tx_frame->header.PCB);
|
|
state->card_state->seq.interface =
|
|
bs_get(PCB.I.send_seq, tx_frame->header.PCB);
|
|
}
|
|
|
|
/* Check if we've gone down an easy to catch error hole. The rest will turn up
|
|
* on the
|
|
* txrx switch.
|
|
*/
|
|
switch (bs_get(PCB.type, rx_frame->header.PCB)) {
|
|
case kPcbTypeSupervisory:
|
|
if (rx_frame->header.PCB != S(RESYNC, RESPONSE) &&
|
|
rx_frame->header.LEN != 1) {
|
|
ALOGE("Invalid supervisory RX frame.");
|
|
return R(0, 1, 0);
|
|
}
|
|
break;
|
|
case kPcbTypeReceiveReady:
|
|
if (rx_frame->header.LEN != 0) {
|
|
ALOGE("Invalid ReceiveReady RX frame.");
|
|
return R(0, 1, 0);
|
|
}
|
|
break;
|
|
case kPcbTypeInfo0:
|
|
case kPcbTypeInfo1:
|
|
/* I-blocks must always alternate for each endpoint. */
|
|
if ((bs_get(PCB.I.send_seq, rx_frame->header.PCB)) ==
|
|
state->card_state->seq.card) {
|
|
ALOGW("Got seq %d expected %d",
|
|
bs_get(PCB.I.send_seq, rx_frame->header.PCB),
|
|
state->card_state->seq.card);
|
|
ALOGE("Invalid Info RX frame.");
|
|
return R(0, 1, 0);
|
|
}
|
|
/* Update the card's last I-block seq. */
|
|
state->card_state->seq.card = bs_get(PCB.I.send_seq, rx_frame->header.PCB);
|
|
default:
|
|
break;
|
|
};
|
|
return 0;
|
|
}
|
|
|
|
enum RuleResult teq1_rules(struct Teq1State *state, struct Teq1Frame *tx_frame,
|
|
struct Teq1Frame *rx_frame,
|
|
struct Teq1Frame *next_tx) {
|
|
/* Rule 1 is enforced by first call∴ Start with I(0,M). */
|
|
/* 0 = TX, 1 = RX */
|
|
/* msb = tx pcb, lsb = rx pcb */
|
|
/* BUG_ON(!rx_frame && !tx_frame && !next_tx); */
|
|
uint16_t txrx = TEQ1_RULE(tx_frame->header.PCB, rx_frame->header.PCB);
|
|
uint8_t R_err;
|
|
|
|
while (1) {
|
|
/* Timeout errors come like invalid frames: 255. */
|
|
if ((R_err = teq1_frame_error_check(state, tx_frame, rx_frame)) != 0) {
|
|
ALOGW("incoming frame failed the error check");
|
|
state->last_error_message = "Invalid frame received";
|
|
/* Mark the frame as bad for our rule evaluation. */
|
|
txrx = TEQ1_RULE(tx_frame->header.PCB, 255);
|
|
state->errors++;
|
|
/* Rule 6.4 */
|
|
if (state->errors >= 6) {
|
|
return kRuleResultResetDevice;
|
|
}
|
|
/* Rule 7.4.2 */
|
|
if (state->errors >= 3) {
|
|
/* Rule 7.4.1: state should start with error count = 2 */
|
|
if (tx_frame->header.PCB != S(RESYNC, REQUEST)) {
|
|
next_tx->header.PCB = S(RESYNC, REQUEST);
|
|
return kRuleResultContinue;
|
|
}
|
|
return kRuleResultRetransmit;
|
|
}
|
|
}
|
|
|
|
/* Specific matches */
|
|
switch (txrx) {
|
|
/*** Rule 2.1: I() -> I() ***/
|
|
/* Error check will determine if the card seq is right. */
|
|
case TEQ1_RULE(I(0, 0), I(0, 0)):
|
|
case TEQ1_RULE(I(0, 0), I(1, 0)):
|
|
case TEQ1_RULE(I(1, 0), I(1, 0)):
|
|
case TEQ1_RULE(I(1, 0), I(0, 0)):
|
|
/* Read app data & return. */
|
|
teq1_get_app_data(state, rx_frame);
|
|
return kRuleResultComplete;
|
|
|
|
/* Card begins chained response. */
|
|
case TEQ1_RULE(I(0, 0), I(0, 1)):
|
|
case TEQ1_RULE(I(1, 0), I(1, 1)):
|
|
/* Prep R(N(S)) */
|
|
teq1_get_app_data(state, rx_frame);
|
|
next_tx->header.PCB =
|
|
TEQ1_R(!bs_get(PCB.I.send_seq, rx_frame->header.PCB), 0, 0);
|
|
next_tx->header.LEN = 0;
|
|
return kRuleResultContinue;
|
|
|
|
/*** Rule 2.2, Rule 5: Chained transmission ***/
|
|
case TEQ1_RULE(I(0, 1), R(1, 0, 0)):
|
|
case TEQ1_RULE(I(1, 1), R(0, 0, 0)):
|
|
/* Send next block -- error-checking assures the R seq is our next seq. */
|
|
next_tx->header.PCB =
|
|
TEQ1_I(bs_get(PCB.R.next_seq, rx_frame->header.PCB), 0);
|
|
teq1_fill_info_block(state, next_tx); /* Sets M-bit and LEN. */
|
|
return kRuleResultContinue;
|
|
|
|
/*** Rule 3 ***/
|
|
case TEQ1_RULE(I(0, 0), S(WTX, REQUEST)):
|
|
case TEQ1_RULE(I(1, 0), S(WTX, REQUEST)):
|
|
/* Note: Spec is unclear on if WTX can occur during chaining so we make it
|
|
an error for now.
|
|
case TEQ1_RULE(I(0, 1), S(WTX, REQUEST)):
|
|
case TEQ1_RULE(I(1, 1), S(WTX, REQUEST)):
|
|
*/
|
|
/* Send S(WTX, RESPONSE) with same INF */
|
|
next_tx->header.PCB = S(WTX, RESPONSE);
|
|
next_tx->header.LEN = 1;
|
|
next_tx->INF[0] = rx_frame->INF[0];
|
|
state->wait_mult = rx_frame->INF[0];
|
|
/* Then wait BWT*INF[0] after transmission. */
|
|
return kRuleResultSingleShot; /* Send then call back in with same tx_frame
|
|
and new rx_frame. */
|
|
|
|
/*** Rule 4 ***/
|
|
case TEQ1_RULE(S(IFS, REQUEST), S(IFS, RESPONSE)):
|
|
/* XXX: Check INFs match. */
|
|
return kRuleResultComplete; /* This is treated as an unique operation. */
|
|
case TEQ1_RULE(I(0, 0), S(IFS, REQUEST)):
|
|
case TEQ1_RULE(I(0, 1), S(IFS, REQUEST)):
|
|
case TEQ1_RULE(I(1, 0), S(IFS, REQUEST)):
|
|
case TEQ1_RULE(I(1, 1), S(IFS, REQUEST)):
|
|
/* Don't support a IFS_REQUEST if we sent an error R-block. */
|
|
case TEQ1_RULE(R(0, 0, 0), S(IFS, REQUEST)):
|
|
case TEQ1_RULE(R(1, 0, 0), S(IFS, REQUEST)):
|
|
next_tx->header.PCB = S(IFS, RESPONSE);
|
|
next_tx->header.LEN = 1;
|
|
next_tx->INF[0] = rx_frame->INF[0];
|
|
state->ifs = rx_frame->INF[0];
|
|
return kRuleResultSingleShot;
|
|
|
|
/*** Rule 5 (see Rule 2.2 for the chained-tx side. ) ***/
|
|
case TEQ1_RULE(R(0, 0, 0), I(0, 0)):
|
|
case TEQ1_RULE(R(1, 0, 0), I(1, 0)):
|
|
/* Chaining ended with terminal I-block. */
|
|
teq1_get_app_data(state, rx_frame);
|
|
return kRuleResultComplete;
|
|
case TEQ1_RULE(R(0, 0, 0), I(0, 1)):
|
|
case TEQ1_RULE(R(1, 0, 0), I(1, 1)):
|
|
/* Chaining continued; consume partial data and send R(N(S)) */
|
|
teq1_get_app_data(state, rx_frame);
|
|
/* The card seq bit will be tracked/validated earlier. */
|
|
next_tx->header.PCB =
|
|
TEQ1_R(!bs_get(PCB.I.send_seq, rx_frame->header.PCB), 0, 0);
|
|
return kRuleResultContinue;
|
|
|
|
/* Rule 6: Interface can send a RESYNC */
|
|
/* Rule 6.1: timeout BWT right. No case here. */
|
|
/* Rule 6.2, 6.3 */
|
|
case TEQ1_RULE(S(RESYNC, REQUEST), S(RESYNC, RESPONSE)):
|
|
/* Rule 6.5: indicates that the card should assume its prior
|
|
* block was lost _and_ the interface gets transmit privilege,
|
|
* so we just start fresh.
|
|
*/
|
|
return kRuleResultResetSession; /* Start a new exchange (rule 6.3) */
|
|
case TEQ1_RULE(S(RESYNC, REQUEST), 255):
|
|
/* Retransmit the same frame up to 3 times. */
|
|
return kRuleResultRetransmit;
|
|
|
|
/* Rule 7.1, 7.5, 7.6 */
|
|
case TEQ1_RULE(I(0, 0), 255):
|
|
case TEQ1_RULE(I(1, 0), 255):
|
|
case TEQ1_RULE(I(0, 1), 255):
|
|
case TEQ1_RULE(I(1, 1), 255):
|
|
next_tx->header.PCB = R_err;
|
|
bs_assign(&next_tx->header.PCB, PCB.R.next_seq,
|
|
bs_get(PCB.I.send_seq, tx_frame->header.PCB));
|
|
ALOGW("Rule 7.1,7.5,7.6: bad rx - sending error R: %x = %s",
|
|
next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB));
|
|
return kRuleResultSingleShot; /* So we still can retransmit the original.
|
|
*/
|
|
|
|
/* Caught in the error check. */
|
|
case TEQ1_RULE(I(0, 0), R(1, 0, 0)):
|
|
case TEQ1_RULE(I(0, 0), R(1, 0, 1)):
|
|
case TEQ1_RULE(I(0, 0), R(1, 1, 0)):
|
|
case TEQ1_RULE(I(0, 0), R(1, 1, 1)):
|
|
case TEQ1_RULE(I(1, 0), R(0, 0, 0)):
|
|
case TEQ1_RULE(I(1, 0), R(0, 0, 1)):
|
|
case TEQ1_RULE(I(1, 0), R(0, 1, 0)):
|
|
case TEQ1_RULE(I(1, 0), R(0, 1, 1)):
|
|
next_tx->header.PCB =
|
|
TEQ1_R(bs_get(PCB.I.send_seq, tx_frame->header.PCB), 0, 0);
|
|
ALOGW("Rule 7.1,7.5,7.6: weird rx - sending error R: %x = %s",
|
|
next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB));
|
|
return kRuleResultSingleShot;
|
|
|
|
/* Rule 7.2: Retransmit the _same_ R-block. */
|
|
/* The remainder of this rule is implemented in the next switch. */
|
|
case TEQ1_RULE(R(0, 0, 0), 255):
|
|
case TEQ1_RULE(R(0, 0, 1), 255):
|
|
case TEQ1_RULE(R(0, 1, 0), 255):
|
|
case TEQ1_RULE(R(0, 1, 1), 255):
|
|
case TEQ1_RULE(R(1, 0, 0), 255):
|
|
case TEQ1_RULE(R(1, 0, 1), 255):
|
|
case TEQ1_RULE(R(1, 1, 0), 255):
|
|
case TEQ1_RULE(R(1, 1, 1), 255):
|
|
return kRuleResultRetransmit;
|
|
|
|
/* Rule 7.3 request */
|
|
/* Note, 7.3 for transmission of S(*, RESPONSE) won't be seen because they
|
|
* are
|
|
* single shots.
|
|
* Instead, the invalid block will be handled as invalid for the prior TX.
|
|
* This should yield the correct R-block.
|
|
*/
|
|
case TEQ1_RULE(I(0, 0), R(0, 0, 0)):
|
|
case TEQ1_RULE(I(0, 0), R(0, 0, 1)):
|
|
case TEQ1_RULE(I(0, 0), R(0, 1, 0)):
|
|
case TEQ1_RULE(I(0, 0), R(0, 1, 1)):
|
|
case TEQ1_RULE(I(1, 0), R(1, 0, 0)):
|
|
case TEQ1_RULE(I(1, 0), R(1, 1, 0)):
|
|
case TEQ1_RULE(I(1, 0), R(1, 0, 1)):
|
|
case TEQ1_RULE(I(1, 0), R(1, 1, 1)):
|
|
case TEQ1_RULE(I(0, 1), R(0, 0, 0)):
|
|
case TEQ1_RULE(I(0, 1), R(0, 1, 0)):
|
|
case TEQ1_RULE(I(0, 1), R(0, 0, 1)):
|
|
case TEQ1_RULE(I(0, 1), R(0, 1, 1)):
|
|
case TEQ1_RULE(I(1, 1), R(1, 0, 0)):
|
|
case TEQ1_RULE(I(1, 1), R(1, 1, 0)):
|
|
case TEQ1_RULE(I(1, 1), R(1, 0, 1)):
|
|
case TEQ1_RULE(I(1, 1), R(1, 1, 1)):
|
|
/* Retransmit I-block */
|
|
return kRuleResultRetransmit;
|
|
|
|
/* Rule 8 is card only. */
|
|
/* Rule 9: aborting a chain.
|
|
* If a S(ABORT) is injected into this engine, then we may have sent an
|
|
* abort.
|
|
*/
|
|
case TEQ1_RULE(S(ABORT, REQUEST), S(ABORT, RESPONSE)):
|
|
/* No need to send back a R() because we want to keep transmit. */
|
|
return kRuleResultComplete; /* If we sent it, then we are complete. */
|
|
case TEQ1_RULE(S(ABORT, RESPONSE), R(0, 0, 0)):
|
|
case TEQ1_RULE(S(ABORT, RESPONSE), R(1, 0, 0)):
|
|
/* Card triggered abortion complete but we can resume sending. */
|
|
return kRuleResultAbort;
|
|
/* An abort request can interrupt a chain anywhere and could occur
|
|
* after a failure path too.
|
|
*/
|
|
case TEQ1_RULE(I(0, 1), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(I(1, 1), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(0, 0, 0), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(0, 0, 1), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(0, 1, 0), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(0, 1, 1), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(1, 0, 0), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(1, 0, 1), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(1, 1, 0), S(ABORT, REQUEST)):
|
|
case TEQ1_RULE(R(1, 1, 1), S(ABORT, REQUEST)):
|
|
next_tx->header.PCB = S(ABORT, REQUEST);
|
|
return kRuleResultContinue; /* Takes over prior flow. */
|
|
case TEQ1_RULE(S(ABORT, RESPONSE), 255):
|
|
return kRuleResultRetransmit;
|
|
/* Note, other blocks should be caught below. */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Note, only S(ABORT, REQUEST) and S(IFS, REQUEST) are supported
|
|
* for transmitting to the card. Others will result in error
|
|
* flows.
|
|
*
|
|
* For supported flows: If an operation was paused to
|
|
* send it, the caller may then switch to that state and resume.
|
|
*/
|
|
if (rx_frame->header.PCB != 255) {
|
|
ALOGW("Unexpected frame. Marking error and re-evaluating.");
|
|
rx_frame->header.PCB = 255;
|
|
continue;
|
|
}
|
|
|
|
return kRuleResultHardFail;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TODO(wad): Consider splitting teq1_transcieve() into
|
|
* teq1_transcieve_init() and teq1_transceive_process_one()
|
|
* if testing becomes onerous given the loop below.
|
|
*/
|
|
ESE_API uint32_t teq1_transceive(struct EseInterface *ese,
|
|
const struct Teq1ProtocolOptions *opts,
|
|
const struct EseSgBuffer *tx_bufs,
|
|
uint8_t tx_segs, struct EseSgBuffer *rx_bufs,
|
|
uint8_t rx_segs) {
|
|
struct Teq1Frame tx_frame[2];
|
|
struct Teq1Frame rx_frame;
|
|
struct Teq1Frame *tx = &tx_frame[0];
|
|
int active = 0;
|
|
bool was_reset = false;
|
|
bool needs_hw_reset = false;
|
|
int session_resets = 0;
|
|
bool done = false;
|
|
enum RuleResult result = kRuleResultComplete;
|
|
uint32_t rx_total = ese_sg_length(rx_bufs, rx_segs);
|
|
struct Teq1CardState *card_state = (struct Teq1CardState *)(&ese->pad[0]);
|
|
struct Teq1State init_state = TEQ1_INIT_STATE(
|
|
tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs,
|
|
ese_sg_length(rx_bufs, rx_segs), card_state);
|
|
struct Teq1State state = TEQ1_INIT_STATE(
|
|
tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs,
|
|
ese_sg_length(rx_bufs, rx_segs), card_state);
|
|
|
|
_static_assert(TEQ1HEADER_SIZE == sizeof(struct Teq1Header),
|
|
"Ensure compiler alignment/padding matches wire protocol.");
|
|
_static_assert(TEQ1FRAME_SIZE == sizeof(struct Teq1Frame),
|
|
"Ensure compiler alignment/padding matches wire protocol.");
|
|
|
|
/* First I-block is always I(0, M). After that, modulo 2. */
|
|
tx->header.PCB = TEQ1_I(!card_state->seq.interface, 0);
|
|
teq1_fill_info_block(&state, tx);
|
|
|
|
teq1_trace_header();
|
|
while (!done) {
|
|
/* Populates the node address and LRC prior to attempting to transmit. */
|
|
teq1_transmit(ese, opts, tx);
|
|
|
|
/* If tx was pointed to the inactive frame for a single shot, restore it
|
|
* now. */
|
|
tx = &tx_frame[active];
|
|
|
|
/* Clear the RX frame. */
|
|
ese_memset(&rx_frame, 0xff, sizeof(rx_frame));
|
|
|
|
/* -1 indicates a timeout or failure from hardware. */
|
|
if (teq1_receive(ese, opts, opts->bwt * (float)state.wait_mult, &rx_frame) <
|
|
0) {
|
|
/* TODO(wad): If the ese_error(ese) == 1, should this go ahead and fail?
|
|
*/
|
|
/* Failures are considered invalid blocks in the rule engine below. */
|
|
rx_frame.header.PCB = 255;
|
|
}
|
|
/* Always reset |wait_mult| once we have calculated the timeout. */
|
|
state.wait_mult = 1;
|
|
|
|
/* Clear the inactive frame header for use as |next_tx|. */
|
|
ese_memset(&tx_frame[!active].header, 0, sizeof(tx_frame[!active].header));
|
|
|
|
result = teq1_rules(&state, tx, &rx_frame, &tx_frame[!active]);
|
|
ALOGV("[ %s ]", teq1_rule_result_to_name(result));
|
|
switch (result) {
|
|
case kRuleResultComplete:
|
|
done = true;
|
|
break;
|
|
case kRuleResultRetransmit:
|
|
/* TODO(wad) Find a clean way to move into teq1_rules(). */
|
|
if (state.retransmits++ < 3) {
|
|
continue;
|
|
}
|
|
ALOGE("More than three retransmits have occurred");
|
|
if (tx->header.PCB == S(RESYNC, REQUEST)) {
|
|
/* More than three RESYNC retranmits have occurred. */
|
|
ese_set_error(ese, kTeq1ErrorHardFail);
|
|
return 0;
|
|
}
|
|
/* Fall through */
|
|
ALOGE("Triggering resynchronization.");
|
|
tx_frame[!active].header.PCB = S(RESYNC, REQUEST);
|
|
case kRuleResultContinue:
|
|
active = !active;
|
|
tx = &tx_frame[active];
|
|
/* Reset this to 0 to use the counter for RESYNC transmits. */
|
|
state.retransmits = 0;
|
|
/* Errors are not reset until the session is reset. */
|
|
continue;
|
|
case kRuleResultHardFail:
|
|
ese_set_error(ese, kTeq1ErrorHardFail);
|
|
return 0;
|
|
case kRuleResultAbort:
|
|
ese_set_error(ese, kTeq1ErrorAbort);
|
|
return 0;
|
|
case kRuleResultSingleShot:
|
|
/*
|
|
* Send the next_tx on loop, but tell the rule engine that
|
|
* the last sent state hasn't changed. This allows for easy error
|
|
* and supervisory block paths without nesting state.
|
|
*/
|
|
tx = &tx_frame[!active];
|
|
continue;
|
|
case kRuleResultResetDevice:
|
|
needs_hw_reset = true;
|
|
/* Fall through to session reset. */
|
|
case kRuleResultResetSession:
|
|
/* Reset to initial state and possibly do hw reset */
|
|
if (session_resets++ > 4) {
|
|
/* If there have been more than 4 resyncs without a
|
|
* physical reset, we should pull the plug.
|
|
*/
|
|
needs_hw_reset = true;
|
|
}
|
|
if (needs_hw_reset) {
|
|
needs_hw_reset = false;
|
|
if (was_reset || !ese->ops->hw_reset || ese->ops->hw_reset(ese) == -1) {
|
|
ese_set_error(ese, kTeq1ErrorDeviceReset);
|
|
return 0; /* Don't keep resetting -- hard fail. */
|
|
}
|
|
was_reset = true;
|
|
session_resets = 0;
|
|
}
|
|
state = init_state;
|
|
TEQ1_INIT_CARD_STATE(state.card_state);
|
|
/* Reset the active frame. */
|
|
ese_memset(tx, 0, sizeof(*tx));
|
|
/* Load initial I-block. */
|
|
tx->header.PCB = I(0, 0);
|
|
teq1_fill_info_block(&state, tx);
|
|
continue;
|
|
}
|
|
}
|
|
/* Return the number of bytes used in the RX buffers. */
|
|
return rx_total - state.app_data.rx_total;
|
|
}
|
|
|
|
ESE_API uint8_t teq1_compute_LRC(const struct Teq1Frame *frame) {
|
|
uint8_t lrc = 0;
|
|
const uint8_t *buffer = frame->val;
|
|
const uint8_t *end = buffer + frame->header.LEN + sizeof(frame->header);
|
|
while (buffer < end) {
|
|
lrc ^= *buffer++;
|
|
}
|
|
return lrc;
|
|
}
|