1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * Implement a simple T=1 echo endpoint.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <unistd.h>
22
23#include "../libese-teq1/include/ese/teq1.h"
24#include "../libese/include/ese/ese.h"
25#include "../libese/include/ese/log.h"
26
27struct EchoState {
28  struct Teq1Frame frame;
29  uint8_t *rx_fill;
30  uint8_t *tx_sent;
31  int recvd;
32};
33
34#define ECHO_STATE(ese) (*(struct EchoState **)(&ese->pad[1]))
35
36static int echo_open(struct EseInterface *ese, void *hw_opts) {
37  struct EchoState *es = hw_opts; /* shorter than __attribute */
38  struct EchoState **es_ptr;
39  if (sizeof(ese->pad) < sizeof(struct EchoState *)) {
40    /* This is a compile-time correctable error only. */
41    ALOGE("Pad size too small to use Echo HW (%zu < %zu)", sizeof(ese->pad),
42          sizeof(struct EchoState *));
43    return -1;
44  }
45  es_ptr = &ECHO_STATE(ese);
46  *es_ptr = malloc(sizeof(struct EchoState));
47  if (!*es_ptr) {
48    return -1;
49  }
50  es = ECHO_STATE(ese);
51  es->rx_fill = &es->frame.header.NAD;
52  es->tx_sent = es->rx_fill;
53  es->recvd = 0;
54  return 0;
55}
56
57static void echo_close(struct EseInterface *ese) {
58  struct EchoState *es;
59  es = ECHO_STATE(ese);
60  if (!es) {
61    return;
62  }
63  free(es);
64  es = NULL;
65}
66
67static uint32_t echo_receive(struct EseInterface *ese, uint8_t *buf,
68                             uint32_t len, int complete) {
69  struct EchoState *es = ECHO_STATE(ese);
70  ALOGV("interface attempting to read data");
71  if (!es->recvd) {
72    return 0;
73  }
74
75  if (len > sizeof(es->frame) - (es->tx_sent - &es->frame.header.NAD)) {
76    return 0;
77  }
78
79  /* NAD was polled for so skip it. */
80  memcpy(buf, es->tx_sent, len);
81  es->tx_sent += len;
82  if (complete) {
83    es->tx_sent = &es->frame.header.NAD;
84    es->recvd = 0;
85    ALOGV("card sent a frame");
86  }
87  return sizeof(es->frame.header) + es->frame.header.LEN;
88}
89
90static uint32_t echo_transmit(struct EseInterface *ese, const uint8_t *buf,
91                              uint32_t len, int complete) {
92  struct EchoState *es = ECHO_STATE(ese);
93  ALOGV("interface transmitting data");
94  if (len > sizeof(es->frame) - (es->rx_fill - &es->frame.header.NAD)) {
95    return 0;
96  }
97  memcpy(es->rx_fill, buf, len);
98  es->rx_fill += len;
99  es->recvd = complete;
100  if (complete) {
101    es->frame.header.NAD = 0x00;
102    if (teq1_compute_LRC(&es->frame) != es->frame.INF[es->frame.header.LEN]) {
103      ALOGV("card received frame with bad LRC");
104      return 0;
105    }
106    ALOGV("card received valid frame");
107    es->rx_fill = &es->frame.header.NAD;
108  }
109  return len;
110}
111
112static int echo_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
113                     int complete) {
114  struct EchoState *es = ECHO_STATE(ese);
115  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
116  ALOGV("interface polling for start of frame/host node address: %x", poll_for);
117  /* In reality, we should be polling at intervals up to the timeout. */
118  if (timeout > 0.0) {
119    usleep(timeout * 1000);
120  }
121  if (poll_for == opts->host_address) {
122    ALOGV("interface received NAD");
123    if (!complete) {
124      es->tx_sent++; /* Consume the polled byte: NAD */
125    }
126    return 1;
127  }
128  return -1;
129}
130
131int echo_preprocess(const struct Teq1ProtocolOptions *const opts,
132                    struct Teq1Frame *frame, int tx) {
133  if (tx) {
134    /* Recompute the LRC with the NAD of 0x00 */
135    frame->header.NAD = 0x00;
136    frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
137    frame->header.NAD = opts->node_address;
138    ALOGV("interface is preprocessing outbound frame");
139  } else {
140    /* Replace the NAD with 0x00 so the LRC check passes. */
141    frame->header.NAD = 0x00;
142    ALOGV("interface is preprocessing inbound frame");
143  }
144  return 0;
145}
146
147static const struct Teq1ProtocolOptions kTeq1Options = {
148    .host_address = 0xAA,
149    .node_address = 0xBB,
150    .bwt = 3.14152f,
151    .etu = 1.0f,
152    .preprocess = &echo_preprocess,
153};
154
155uint32_t echo_transceive(struct EseInterface *ese,
156                         const struct EseSgBuffer *tx_buf, uint32_t tx_len,
157                         struct EseSgBuffer *rx_buf, uint32_t rx_len) {
158  return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len);
159}
160
161static const char *kErrorMessages[] = {
162    "T=1 hard failure.",        /* TEQ1_ERROR_HARD_FAIL */
163    "T=1 abort.",               /* TEQ1_ERROR_ABORT */
164    "T=1 device reset failed.", /* TEQ1_ERROR_DEVICE_ABORT */
165};
166
167static const struct EseOperations ops = {
168    .name = "eSE Echo Hardware (fake)",
169    .open = &echo_open,
170    .hw_receive = &echo_receive,
171    .hw_transmit = &echo_transmit,
172    .transceive = &echo_transceive,
173    .poll = &echo_poll,
174    .close = &echo_close,
175    .opts = &kTeq1Options,
176    .errors = kErrorMessages,
177    .errors_count = sizeof(kErrorMessages),
178};
179ESE_DEFINE_HW_OPS(ESE_HW_ECHO, ops);
180