/*
* 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.
*
* Minimal functions that only validate arguments.
*/
#include "../libese/include/ese/ese.h"
enum EseFakeHwError {
kEseFakeHwErrorEarlyClose,
kEseFakeHwErrorReceiveDuringTransmit,
kEseFakeHwErrorInvalidReceiveSize,
kEseFakeHwErrorTransmitDuringReceive,
kEseFakeHwErrorInvalidTransmitSize,
kEseFakeHwErrorTranscieveWhileBusy,
kEseFakeHwErrorEmptyTransmit,
kEseFakeHwErrorMax,
};
static const char *kErrorMessages[] = {
"Interface closed without finishing transmission.",
"Receive called without completing transmission.",
"Invalid receive buffer supplied with non-zero length.",
"Transmit called without completing reception.",
"Invalid transmit buffer supplied with non-zero length.",
"Transceive called while other I/O in process.",
"Transmitted no data.", /* Can reach this by setting tx_len = 0. */
};
static int fake_open(struct EseInterface *ese,
void *hw_opts __attribute__((unused))) {
ese->pad[0] = 1; /* rx complete */
ese->pad[1] = 1; /* tx complete */
return 0;
}
static void fake_close(struct EseInterface *ese) {
if (!ese->pad[0] || !ese->pad[1]) {
/* Set by caller. ese->error.is_error = 1; */
ese_set_error(ese, kEseFakeHwErrorEarlyClose);
return;
}
}
static uint32_t fake_receive(struct EseInterface *ese, uint8_t *buf,
uint32_t len, int complete) {
if (!ese->pad[1]) {
ese_set_error(ese, kEseFakeHwErrorReceiveDuringTransmit);
return -1;
}
ese->pad[0] = complete;
if (!buf && len) {
ese_set_error(ese, kEseFakeHwErrorInvalidReceiveSize);
return -1;
}
if (!len) {
return 0;
}
return len;
}
static uint32_t fake_transmit(struct EseInterface *ese, const uint8_t *buf,
uint32_t len, int complete) {
if (!ese->pad[0]) {
ese_set_error(ese, kEseFakeHwErrorTransmitDuringReceive);
return -1;
}
ese->pad[1] = complete;
if (!buf && len) {
ese_set_error(ese, kEseFakeHwErrorInvalidTransmitSize);
return -1;
}
if (!len) {
return 0;
}
return len;
}
static int fake_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
int complete) {
/* Poll begins a receive-train so transmit needs to be completed. */
if (!ese->pad[1]) {
ese_set_error(ese, kEseFakeHwErrorReceiveDuringTransmit);
return -1;
}
if (timeout == 0.0f) {
/* Instant timeout. */
return 0;
}
/* Only expect one value to work. */
if (poll_for == 0xad) {
return 1;
}
ese->pad[0] = complete;
return 0;
}
uint32_t fake_transceive(struct EseInterface *ese,
const struct EseSgBuffer *tx_bufs, uint32_t tx_seg,
struct EseSgBuffer *rx_bufs, uint32_t rx_seg) {
uint32_t processed = 0;
uint32_t offset = 0;
struct EseSgBuffer *rx_buf = rx_bufs;
const struct EseSgBuffer *tx_buf = tx_bufs;
if (!ese->pad[0] || !ese->pad[1]) {
ese_set_error(ese, kEseFakeHwErrorTranscieveWhileBusy);
return 0;
}
while (tx_buf < tx_bufs + tx_seg) {
uint32_t sent =
fake_transmit(ese, tx_buf->base + offset, tx_buf->len - offset, 0);
if (sent != tx_buf->len - offset) {
offset = tx_buf->len - sent;
processed += sent;
continue;
}
if (sent == 0) {
if (ese_error(ese)) {
return 0;
}
ese_set_error(ese, kEseFakeHwErrorEmptyTransmit);
return 0;
}
tx_buf++;
offset = 0;
processed += sent;
}
fake_transmit(ese, NULL, 0, 1); /* Complete. */
if (fake_poll(ese, 0xad, 10, 0) != 1) {
ese_set_error(ese, kEseGlobalErrorPollTimedOut);
return 0;
}
/* This reads the full RX buffer rather than a protocol specified amount. */
processed = 0;
while (rx_buf < rx_bufs + rx_seg) {
processed += fake_receive(ese, rx_buf->base, rx_buf->len, 1);
}
return processed;
}
static const struct EseOperations ops = {
.name = "eSE Fake Hardware",
.open = &fake_open,
.hw_receive = &fake_receive,
.hw_transmit = &fake_transmit,
.transceive = &fake_transceive,
.poll = &fake_poll,
.close = &fake_close,
.opts = NULL,
.errors = kErrorMessages,
.errors_count = sizeof(kErrorMessages),
};
ESE_DEFINE_HW_OPS(ESE_HW_FAKE, ops);