/* * Copyright (C) 2016 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. * * Implement a simple T=1 echo endpoint. */ #include #include #include #include "../libese-teq1/include/ese/teq1.h" #include "../libese/include/ese/ese.h" #include "../libese/include/ese/log.h" struct EchoState { struct Teq1Frame frame; uint8_t *rx_fill; uint8_t *tx_sent; int recvd; }; #define ECHO_STATE(ese) (*(struct EchoState **)(&ese->pad[1])) static int echo_open(struct EseInterface *ese, void *hw_opts) { struct EchoState *es = hw_opts; /* shorter than __attribute */ struct EchoState **es_ptr; if (sizeof(ese->pad) < sizeof(struct EchoState *)) { /* This is a compile-time correctable error only. */ ALOGE("Pad size too small to use Echo HW (%zu < %zu)", sizeof(ese->pad), sizeof(struct EchoState *)); return -1; } es_ptr = &ECHO_STATE(ese); *es_ptr = malloc(sizeof(struct EchoState)); if (!*es_ptr) { return -1; } es = ECHO_STATE(ese); es->rx_fill = &es->frame.header.NAD; es->tx_sent = es->rx_fill; es->recvd = 0; return 0; } static void echo_close(struct EseInterface *ese) { struct EchoState *es; es = ECHO_STATE(ese); if (!es) { return; } free(es); es = NULL; } static uint32_t echo_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len, int complete) { struct EchoState *es = ECHO_STATE(ese); ALOGV("interface attempting to read data"); if (!es->recvd) { return 0; } if (len > sizeof(es->frame) - (es->tx_sent - &es->frame.header.NAD)) { return 0; } /* NAD was polled for so skip it. */ memcpy(buf, es->tx_sent, len); es->tx_sent += len; if (complete) { es->tx_sent = &es->frame.header.NAD; es->recvd = 0; ALOGV("card sent a frame"); } return sizeof(es->frame.header) + es->frame.header.LEN; } static uint32_t echo_transmit(struct EseInterface *ese, const uint8_t *buf, uint32_t len, int complete) { struct EchoState *es = ECHO_STATE(ese); ALOGV("interface transmitting data"); if (len > sizeof(es->frame) - (es->rx_fill - &es->frame.header.NAD)) { return 0; } memcpy(es->rx_fill, buf, len); es->rx_fill += len; es->recvd = complete; if (complete) { es->frame.header.NAD = 0x00; if (teq1_compute_LRC(&es->frame) != es->frame.INF[es->frame.header.LEN]) { ALOGV("card received frame with bad LRC"); return 0; } ALOGV("card received valid frame"); es->rx_fill = &es->frame.header.NAD; } return len; } static int echo_poll(struct EseInterface *ese, uint8_t poll_for, float timeout, int complete) { struct EchoState *es = ECHO_STATE(ese); const struct Teq1ProtocolOptions *opts = ese->ops->opts; ALOGV("interface polling for start of frame/host node address: %x", poll_for); /* In reality, we should be polling at intervals up to the timeout. */ if (timeout > 0.0) { usleep(timeout * 1000); } if (poll_for == opts->host_address) { ALOGV("interface received NAD"); if (!complete) { es->tx_sent++; /* Consume the polled byte: NAD */ } return 1; } return -1; } int echo_preprocess(const struct Teq1ProtocolOptions *const opts, struct Teq1Frame *frame, int tx) { if (tx) { /* Recompute the LRC with the NAD of 0x00 */ frame->header.NAD = 0x00; frame->INF[frame->header.LEN] = teq1_compute_LRC(frame); frame->header.NAD = opts->node_address; ALOGV("interface is preprocessing outbound frame"); } else { /* Replace the NAD with 0x00 so the LRC check passes. */ frame->header.NAD = 0x00; ALOGV("interface is preprocessing inbound frame"); } return 0; } static const struct Teq1ProtocolOptions kTeq1Options = { .host_address = 0xAA, .node_address = 0xBB, .bwt = 3.14152f, .etu = 1.0f, .preprocess = &echo_preprocess, }; uint32_t echo_transceive(struct EseInterface *ese, const struct EseSgBuffer *tx_buf, uint32_t tx_len, struct EseSgBuffer *rx_buf, uint32_t rx_len) { return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len); } static const char *kErrorMessages[] = { "T=1 hard failure.", /* TEQ1_ERROR_HARD_FAIL */ "T=1 abort.", /* TEQ1_ERROR_ABORT */ "T=1 device reset failed.", /* TEQ1_ERROR_DEVICE_ABORT */ }; static const struct EseOperations ops = { .name = "eSE Echo Hardware (fake)", .open = &echo_open, .hw_receive = &echo_receive, .hw_transmit = &echo_transmit, .transceive = &echo_transceive, .poll = &echo_poll, .close = &echo_close, .opts = &kTeq1Options, .errors = kErrorMessages, .errors_count = sizeof(kErrorMessages), }; ESE_DEFINE_HW_OPS(ESE_HW_ECHO, ops);