Clean up and refactor; new hw support
(Originally, https://android-review.googlesource.com/#/c/341550/)
This change addresses portability, a pn80t platform abstraction, and
nq-nci support.
Refactor/clean up:
- Adds darwin-sysdeps.c to help avoid reverting again.
- Clean up Android.bp
- T=1: moved T=1 to using bit_specs to keep some
of the readability of bitfields without incurring
weird toolchain side effects.
- T=1 will still rely on compilers keeping uchars
aligned and check it with a div-by-zero build
assertion.
- ESE platform specific methods are now wrapped.
- Adjusted error message constant usage.
- Enclosing {} for every if statement.
- Moved to relative headers for inclusion into other code
bases.
- Added a comment to log.h to make debugging easier globally
in libese code.
PN80T:
- Common code now shared across different
wire configurations.
- Add support for kernel based driver (called nq-nci)
which interacts with the nq-nci behavior for power
management.
- Added cooldown/end of session code to pn80t/common.c
- Migrated the ese_nxp_sample code to NQ_NCI and added the empty
session to test the cooldown code submission.
Bug: 34193473,35105409
Change-Id: I8fc320c8c236282ed103ef3ee3cb8c0dc99d8bcb
Test: unittests pass, tested ese-relay on hardware forwarding globalplatform pro
diff --git a/Android.bp b/Android.bp
index 97af836..fce386e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,4 +14,11 @@
// limitations under the License.
//
-subdirs = ["libese", "libese-teq1", "libese-hw", "examples", "tools"]
+subdirs = [
+ "libese-sysdeps",
+ "libese",
+ "libese-teq1",
+ "libese-hw",
+ "examples",
+ "tools",
+]
diff --git a/NOTICE b/NOTICE
index 952429e..a0ac881 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 2016 The Android Open Source Project
+Copyright (C) 2017 The Android Open Source Project
icensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index efca4c6..273b3f5 100644
--- a/README.md
+++ b/README.md
@@ -19,10 +19,82 @@
## Usage
-(TBD: See tools/ and example/)
+Public client interface for Embedded Secure Elements.
+
+Prior to use in a file, import all necessary variables with:
+
+ ESE_INCLUDE_HW(SOME_HAL_IMPL);
+
+Instantiate in a function with:
+
+ ESE_DECLARE(my_ese, SOME_HAL_IMPL);
+
+or
+
+ struct EseInterface my_ese = ESE_INITIALIZER(SOME_HAL_IMPL);
+
+or
+
+ struct EseInterface *my_ese = malloc(sizeof(struct EseInterface));
+ ...
+ ese_init(my_ese, SOME_HAL_IMPL);
+
+To initialize the hardware abstraction, call:
+
+ ese_open(my_ese);
+
+To release any claimed resources, call
+
+ ese_close(my_ese)
+
+when interface use is complete.
+
+To perform a transmit-receive cycle, call
+
+ ese_transceive(my_ese, ...);
+
+with a filled transmit buffer with total data length and
+an empty receive buffer and a maximum fill length.
+A negative return value indicates an error and a hardware
+specific code and string may be collected with calls to
+
+ ese_error_code(my_ese);
+ ese_error_message(my_ese);
+
+The EseInterface is not safe for concurrent access.
+(Patches welcome! ;).
+
+# Components
+
+libese is broken into multiple pieces:
+ * libese
+ * libese-sysdeps
+ * libese-hw
+ * libese-teq1
+
+*libese* provides the headers and wrappers for writing libese clients
+and for implementing hardware backends. It depends on a backend being
+provided as per *libese-hw* and on *libese-sysdeps*.
+
+*libese-sysdeps* provides the system level libraries that are needed by
+libese provided software. If libese is being ported to a new environment,
+like a bootloader or non-Linux OS, this library may need to be replaced.
+(Also take a look at libese/include/ese/log.h for the macro definitions
+ that may be needed.)
+
+*libese-hw* provides existing libese hardware backends.
+
+*libese-teq1* provides a T=1 compatible transcieve function that may be
+used by a hardware backend. It comes with some prequisites for use,
+such as a specifically structured set of error messages and
+EseInteface pad usage, but otherwise it does not depends on any specific
+functionality not abstracted via the libese EseOperations structure.
+
## Supported backends
-At present, only sample backends and a Linux SPIdev driven NXP
-developer board are supported.
+There are two test backends, fake and echo, as well as one
+real backend for the NXP PN80T/PN81A.
+The NXP backends support both a direct kernel driver and
+a Linux SPIdev interface.
diff --git a/examples/Android.bp b/examples/Android.bp
index d4a6960..1678ba0 100644
--- a/examples/Android.bp
+++ b/examples/Android.bp
@@ -14,27 +14,19 @@
// limitations under the License.
//
-example_static_libraries = []
-
-example_shared_libraries = [
- "liblog",
- "libese",
- "libese-teq1",
- "libese-hw-nxp-pn80t-spidev",
-]
-
cc_binary {
name: "ese_nxp_sample",
srcs: ["ese_nxp_sample.c"],
host_supported: false,
- shared_libs: example_shared_libraries,
-
target: {
- darwin: {
- enabled: false,
- },
- windows: {
- enabled: false,
- },
+ darwin: {
+ enabled: false,
+ },
},
+ shared_libs: [
+ "liblog",
+ "libese",
+ "libese-teq1",
+ "libese-hw-nxp-pn80t-nq-nci",
+ ],
}
diff --git a/examples/ese_nxp_sample.c b/examples/ese_nxp_sample.c
index 392ce63..7eae531 100644
--- a/examples/ese_nxp_sample.c
+++ b/examples/ese_nxp_sample.c
@@ -22,19 +22,17 @@
#include <unistd.h>
#include <ese/ese.h>
-/* Note, the struct could be build just as well. */
-#include <ese/hw/nxp/pn80t/boards/hikey-spidev.h>
-ESE_INCLUDE_HW(ESE_HW_NXP_PN80T_SPIDEV);
+ESE_INCLUDE_HW(ESE_HW_NXP_PN80T_NQ_NCI);
/* APDU: CLA INS P1-P2 Lc Data Le */
struct Apdu {
- size_t length;
+ uint32_t length;
const uint8_t *bytes;
const char *desc;
};
struct ApduSession {
- size_t count;
+ uint32_t count;
const char *desc;
const struct Apdu *apdus[];
};
@@ -63,6 +61,10 @@
},
};
+const struct ApduSession kEmptySession = {
+ .count = 0, .desc = "Empty session (cooldown only)", .apdus = {},
+};
+
/* Define the loader service sessions here! */
const uint8_t kSelectJcopIdentifyBytes[] = {
0x00, 0xA4, 0x04, 0x00, 0x09, 0xA0, 0x00,
@@ -75,17 +77,18 @@
};
const struct ApduSession *kSessions[] = {
- &kGetCplcSession,
+ &kGetCplcSession, &kEmptySession,
};
int main() {
- struct EseInterface ese = ESE_INITIALIZER(ESE_HW_NXP_PN80T_SPIDEV);
+ struct EseInterface ese = ESE_INITIALIZER(ESE_HW_NXP_PN80T_NQ_NCI);
+ void *ese_hw_open_data = NULL;
size_t s = 0;
for (; s < sizeof(kSessions) / sizeof(kSessions[0]); ++s) {
int recvd;
- size_t apdu_index = 0;
+ uint32_t apdu_index = 0;
uint8_t rx_buf[1024];
- if (ese_open(&ese, (void *)(&nxp_boards_hikey_spidev))) {
+ if (ese_open(&ese, ese_hw_open_data) < 0) {
printf("Cannot open hw\n");
if (ese_error(&ese))
printf("eSE error (%d): %s\n", ese_error_code(&ese),
@@ -94,10 +97,10 @@
}
printf("Running session %s\n", kSessions[s]->desc);
for (; apdu_index < kSessions[s]->count; ++apdu_index) {
- size_t i;
+ uint32_t i;
const struct Apdu *apdu = kSessions[s]->apdus[apdu_index];
- printf("Sending APDU %zu: %s\n", apdu_index, apdu->desc);
- printf("Sending %zu bytes to card\n", apdu->length);
+ printf("Sending APDU %u: %s\n", apdu_index, apdu->desc);
+ printf("Sending %u bytes to card\n", apdu->length);
printf("TX: ");
for (i = 0; i < apdu->length; ++i)
printf("%.2X ", apdu->bytes[i]);
diff --git a/libese-hw/Android.bp b/libese-hw/Android.bp
index 0922f1f..7cd8ac4 100644
--- a/libese-hw/Android.bp
+++ b/libese-hw/Android.bp
@@ -18,7 +18,7 @@
name: "libese-hw-fake",
srcs: ["ese_hw_fake.c"],
host_supported: true,
- cflags: ["-DLOG_NDEBUG=0"],
+ cflags: ["-DLOG_NDEBUG=0", "-std=c99"],
shared_libs: ["liblog", "libese"],
}
@@ -26,7 +26,7 @@
name: "libese-hw-echo",
host_supported: true,
srcs: ["ese_hw_echo.c"],
- cflags: ["-DLOG_NDEBUG=0"],
+ cflags: ["-DLOG_NDEBUG=0", "-std=c99"],
shared_libs: ["liblog", "libese", "libese-teq1"],
}
diff --git a/libese-hw/ese_hw_echo.c b/libese-hw/ese_hw_echo.c
index cbb2fea..264ab2b 100644
--- a/libese-hw/ese_hw_echo.c
+++ b/libese-hw/ese_hw_echo.c
@@ -16,15 +16,13 @@
* Implement a simple T=1 echo endpoint.
*/
-#define LOG_TAG "libese-hw-echo"
-
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <ese/ese.h>
-#include <ese/log.h>
-#include <ese/teq1.h>
+#include "../libese-teq1/include/ese/teq1.h"
+#include "../libese/include/ese/ese.h"
+#include "../libese/include/ese/log.h"
struct EchoState {
struct Teq1Frame frame;
@@ -33,23 +31,22 @@
int recvd;
};
-#define ECHO_STATE(ese) (*(struct EchoState **)(&ese->pad[0]))
+#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 (!ese)
- return -1;
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 = (struct EchoState **)(&ese->pad[0]);
+ es_ptr = &ECHO_STATE(ese);
*es_ptr = malloc(sizeof(struct EchoState));
- if (!*es_ptr)
+ if (!*es_ptr) {
return -1;
+ }
es = ECHO_STATE(ese);
es->rx_fill = &es->frame.header.NAD;
es->tx_sent = es->rx_fill;
@@ -57,24 +54,27 @@
return 0;
}
-static int echo_close(struct EseInterface *ese) {
+static void echo_close(struct EseInterface *ese) {
struct EchoState *es;
- if (!ese)
- return -1;
es = ECHO_STATE(ese);
+ if (!es) {
+ return;
+ }
free(es);
- return 0;
+ es = NULL;
}
-static size_t echo_receive(struct EseInterface *ese, uint8_t *buf, size_t len,
- int complete) {
+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)
+ if (!es->recvd) {
return 0;
+ }
- if (len > sizeof(es->frame) - (es->tx_sent - &es->frame.header.NAD))
+ 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);
@@ -87,12 +87,13 @@
return sizeof(es->frame.header) + es->frame.header.LEN;
}
-static size_t echo_transmit(struct EseInterface *ese, const uint8_t *buf,
- size_t len, int complete) {
+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))
+ 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;
@@ -114,8 +115,9 @@
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)
+ if (timeout > 0.0) {
usleep(timeout * 1000);
+ }
if (poll_for == opts->host_address) {
ALOGV("interface received NAD");
if (!complete) {
@@ -142,7 +144,7 @@
return 0;
}
-static const struct Teq1ProtocolOptions teq1_options = {
+static const struct Teq1ProtocolOptions kTeq1Options = {
.host_address = 0xAA,
.node_address = 0xBB,
.bwt = 3.14152f,
@@ -150,21 +152,27 @@
.preprocess = &echo_preprocess,
};
-static const struct EseOperations ops = {
- .name = "eSE Echo Hardware (fake)",
- .open = &echo_open,
- .hw_receive = &echo_receive,
- .hw_transmit = &echo_transmit,
- .transceive = &teq1_transceive,
- .poll = &echo_poll,
- .close = &echo_close,
- .opts = &teq1_options,
-};
-ESE_DEFINE_HW_OPS(ESE_HW_ECHO, ops);
+uint32_t echo_transceive(struct EseInterface *ese, const uint8_t *const tx_buf,
+ uint32_t tx_len, uint8_t *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 */
};
-ESE_DEFINE_HW_ERRORS(ESE_HW_ECHO, kErrorMessages);
+
+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);
diff --git a/libese-hw/ese_hw_fake.c b/libese-hw/ese_hw_fake.c
index 8fe514f..310ef06 100644
--- a/libese-hw/ese_hw_fake.c
+++ b/libese-hw/ese_hw_fake.c
@@ -16,7 +16,28 @@
* Minimal functions that only validate arguments.
*/
-#include <ese/ese.h>
+#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))) {
@@ -25,50 +46,45 @@
return 0;
}
-static int fake_close(struct EseInterface *ese) {
- if (!ese)
- return -1;
+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, 0);
- return -1;
+ ese_set_error(ese, kEseFakeHwErrorEarlyClose);
+ return;
}
- return 0;
}
-static size_t fake_receive(struct EseInterface *ese, uint8_t *buf, size_t len,
- int complete) {
- if (!ese)
- return -1;
+static uint32_t fake_receive(struct EseInterface *ese, uint8_t *buf,
+ uint32_t len, int complete) {
if (!ese->pad[1]) {
- ese_set_error(ese, 1);
+ ese_set_error(ese, kEseFakeHwErrorReceiveDuringTransmit);
return -1;
}
ese->pad[0] = complete;
if (!buf && len) {
- ese_set_error(ese, 2);
+ ese_set_error(ese, kEseFakeHwErrorInvalidReceiveSize);
return -1;
}
- if (!len)
+ if (!len) {
return 0;
+ }
return len;
}
-static size_t fake_transmit(struct EseInterface *ese, const uint8_t *buf,
- size_t len, int complete) {
- if (!ese)
- return -1;
+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, 3);
+ ese_set_error(ese, kEseFakeHwErrorTransmitDuringReceive);
return -1;
}
ese->pad[1] = complete;
if (!buf && len) {
- ese_set_error(ese, 4);
+ ese_set_error(ese, kEseFakeHwErrorInvalidTransmitSize);
return -1;
}
- if (!len)
+ if (!len) {
return 0;
+ }
return len;
}
@@ -76,7 +92,7 @@
int complete) {
/* Poll begins a receive-train so transmit needs to be completed. */
if (!ese->pad[1]) {
- ese_set_error(ese, 1);
+ ese_set_error(ese, kEseFakeHwErrorReceiveDuringTransmit);
return -1;
}
if (timeout == 0.0f) {
@@ -91,26 +107,27 @@
return 0;
}
-size_t fake_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
- size_t tx_len, uint8_t *rx_buf, size_t rx_len) {
- size_t processed = 0;
+uint32_t fake_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
+ uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len) {
+ uint32_t processed = 0;
if (!ese->pad[0] || !ese->pad[1]) {
- ese_set_error(ese, 5);
+ ese_set_error(ese, kEseFakeHwErrorTranscieveWhileBusy);
return 0;
}
while (processed < tx_len) {
- size_t sent = fake_transmit(ese, tx_buf, tx_len, 0);
+ uint32_t sent = fake_transmit(ese, tx_buf, tx_len, 0);
if (sent == 0) {
- if (ese->error.is_err)
+ if (ese_error(ese)) {
return 0;
- ese_set_error(ese, 6);
+ }
+ ese_set_error(ese, kEseFakeHwErrorEmptyTransmit);
return 0;
}
processed += sent;
}
fake_transmit(ese, NULL, 0, 1); /* Complete. */
if (fake_poll(ese, 0xad, 10, 0) != 1) {
- ese_set_error(ese, -2);
+ ese_set_error(ese, kEseGlobalErrorPollTimedOut);
return 0;
}
/* A real implementation would have protocol errors to contend with. */
@@ -127,20 +144,8 @@
.poll = &fake_poll,
.close = &fake_close,
.opts = NULL,
+ .errors = kErrorMessages,
+ .errors_count = sizeof(kErrorMessages),
};
ESE_DEFINE_HW_OPS(ESE_HW_FAKE, ops);
-/* TODO(wad) move opts to data.
-const void *ESE_HW_FAKE_data = NULL;
-*/
-
-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. */
-};
-ESE_DEFINE_HW_ERRORS(ESE_HW_FAKE, kErrorMessages);
diff --git a/libese-hw/nxp/Android.bp b/libese-hw/nxp/Android.bp
index f0ce732..0e0208e 100644
--- a/libese-hw/nxp/Android.bp
+++ b/libese-hw/nxp/Android.bp
@@ -15,21 +15,43 @@
//
cc_library {
- name: "libese-hw-nxp-pn80t-spidev",
- host_supported: false,
+ name: "libese-hw-nxp-pn80t-common",
debug: {
- cflags: ["-DLOG_NDEBUG=0"],
+ cflags: ["-DLOG_NDEBUG=0"],
},
- srcs: ["pn80t_spidev.c"],
- local_include_dirs: ["include"],
+ srcs: ["pn80t/common.c"],
shared_libs: ["liblog", "libese", "libese-teq1"],
+ local_include_dirs: ["include"],
export_include_dirs: ["include"],
target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_defaults {
+ name: "pn80t_platform",
+ target: {
darwin: {
- enabled: false,
- },
- windows: {
- enabled: false,
+ enabled: false,
},
},
+ cflags: ["-fvisibility=internal"],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["liblog", "libese", "libese-teq1"],
+ static_libs: ["libese-hw-nxp-pn80t-common"],
+}
+
+cc_library {
+ name: "libese-hw-nxp-pn80t-spidev",
+ defaults: ["pn80t_platform"],
+ srcs: ["pn80t/linux_spidev.c"],
+}
+
+cc_library {
+ name: "libese-hw-nxp-pn80t-nq-nci",
+ defaults: ["pn80t_platform"],
+ srcs: ["pn80t/nq_nci.c"],
}
diff --git a/libese-hw/nxp/include/ese/hw/nxp/pn80t/boards/hikey-spidev.h b/libese-hw/nxp/include/ese/hw/nxp/pn80t/boards/hikey-spidev.h
index 4f73003..3026f02 100644
--- a/libese-hw/nxp/include/ese/hw/nxp/pn80t/boards/hikey-spidev.h
+++ b/libese-hw/nxp/include/ese/hw/nxp/pn80t/boards/hikey-spidev.h
@@ -19,6 +19,12 @@
static const struct NxpSpiBoard nxp_boards_hikey_spidev = {
.dev_path = "/dev/spidev0.0",
- .reset_gpio = 488, /* GPIO2_0 */
- .svdd_pwr_req_gpio = 490, /* GPIO2_2 */
+ .gpios = {
+ 488, /* kBoardGpioEseRst = GPIO2_0 */
+ 490, /* kBoardGpioEseSvddPwrReq = GPIO2_2 */
+ -1, /* kBoardGpioNfcVen = unused */
+ },
+ .mode = 0,
+ .bits = 8,
+ .speed = 1000000L,
};
diff --git a/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h b/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h
new file mode 100644
index 0000000..0125f24
--- /dev/null
+++ b/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef ESE_HW_NXP_PN80T_COMMON_H_
+#define ESE_HW_NXP_PN80T_COMMON_H_ 1
+
+#include "../../libese-teq1/include/ese/teq1.h"
+#include "../../libese/include/ese/ese.h"
+#include "../../libese/include/ese/log.h"
+#include "platform.h"
+
+/* Card state is _required_ to be at the front of eSE pad. */
+struct NxpState {
+ void *handle;
+};
+
+/* pad[0] is reserved for T=1. Lazily go to the middle. */
+#define NXP_PN80T_STATE(ese) \
+ ((struct NxpState *)(&ese->pad[ESE_INTERFACE_STATE_PAD / 2]))
+
+void nxp_pn80t_close(struct EseInterface *ese);
+uint32_t nxp_pn80t_transceive(struct EseInterface *ese,
+ const uint8_t *const tx_buf, uint32_t tx_len,
+ uint8_t *rx_buf, uint32_t rx_len);
+int nxp_pn80t_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
+ int complete);
+int nxp_pn80t_reset(struct EseInterface *ese);
+int nxp_pn80t_open(struct EseInterface *ese, void *board);
+
+enum NxpPn80tError {
+ kNxpPn80tError = kTeq1ErrorMax,
+ kNxpPn80tErrorPollRead,
+ kNxpPn80tErrorPlatformInit,
+ kNxpPn80tErrorResetToggle,
+ kNxpPn80tErrorTransmit,
+ kNxpPn80tErrorTransmitSize,
+ kNxpPn80tErrorReceive,
+ kNxpPn80tErrorReceiveSize,
+ kNxpPn80tErrorMax, /* sizeof(kNxpPn80tErrorMessages) */
+};
+
+extern const char *kNxpPn80tErrorMessages[];
+#endif /* ESE_HW_NXP_PN80T_COMMON_H_ */
diff --git a/libese-hw/nxp/include/ese/hw/nxp/pn80t/platform.h b/libese-hw/nxp/include/ese/hw/nxp/pn80t/platform.h
new file mode 100644
index 0000000..8dad425
--- /dev/null
+++ b/libese-hw/nxp/include/ese/hw/nxp/pn80t/platform.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef ESE_HW_NXP_PN80T_PLATFORM_H_
+#define ESE_HW_NXP_PN80T_PLATFORM_H_ 1
+
+typedef void *(pn80t_platform_initialize_t)(void *);
+typedef int (pn80t_platform_release_t)(void *);
+typedef int (pn80t_platform_toggle_t)(void *, int);
+typedef int (pn80t_platform_wait_t)(void *, long usec);
+
+/* Pn80tPlatform
+ *
+ * Provides the callbacks necessary to interface with the platform, be it the Linux
+ * kernel or a bootloader in pn80t_common.c
+ *
+ * All "required" functions must be provided.
+ * All "optional" functions may be set to NULL.
+ *
+ */
+struct Pn80tPlatform {
+ /* Required: Initializes the hardware and platform opaque handle. */
+ pn80t_platform_initialize_t *const initialize;
+ /* Required: free memory and release resources as needed. */
+ pn80t_platform_release_t *const release;
+ /* Required: determines eSE specific power. 1 = on, 0 = off. */
+ pn80t_platform_toggle_t *const toggle_reset; /* ESE_RST or other power control. */
+ /* Optional: determines global NFC power: 1 = on, 0 = off */
+ pn80t_platform_toggle_t *const toggle_ven; /* NFC_VEN */
+ /* Optional: enables eSE power control via |toggle_reset|. 1 = on, 0 = off */
+ pn80t_platform_toggle_t *const toggle_power_req; /* SVDD_PWR_REQ */
+ /* Required: provides a usleep() equivalent. */
+ pn80t_platform_wait_t *const wait;
+};
+
+#endif
diff --git a/libese-hw/nxp/include/ese/hw/nxp/spi_board.h b/libese-hw/nxp/include/ese/hw/nxp/spi_board.h
index 109e348..ddcb64b 100644
--- a/libese-hw/nxp/include/ese/hw/nxp/spi_board.h
+++ b/libese-hw/nxp/include/ese/hw/nxp/spi_board.h
@@ -17,10 +17,20 @@
#ifndef ESE_HW_NXP_SPI_BOARD_H_
#define ESE_HW_NXP_SPI_BOARD_H_ 1
+typedef enum {
+ kBoardGpioEseRst = 0,
+ kBoardGpioEseSvddPwrReq,
+ kBoardGpioNfcVen,
+ kBoardGpioMax,
+} BoardGpio;
+
+/* Allow GPIO assignment and configuration to vary without a new device definition. */
struct NxpSpiBoard {
const char *dev_path;
- int reset_gpio;
- int svdd_pwr_req_gpio;
+ int gpios[kBoardGpioMax];
+ uint8_t mode;
+ uint32_t bits;
+ uint32_t speed;
};
#endif /* ESE_HW_NXP_SPI_BOARD_H_ */
diff --git a/libese-hw/nxp/pn80t/README.md b/libese-hw/nxp/pn80t/README.md
new file mode 100644
index 0000000..49d6f29
--- /dev/null
+++ b/libese-hw/nxp/pn80t/README.md
@@ -0,0 +1,28 @@
+# NXP PN80T/PN81A support
+
+libese support for the PN80T series of embedded secure elements is
+implemented using a common set of shared hardware functions and
+communication specific behavior.
+
+The common behavior is found in "common.h". Support for using Linux
+spidev іs in "linux\_spidev.c", and support for using a simple nq-nci
+associated kernel driver is in "nq\_nci.c".
+
+# Implementing a new backend
+
+When implementing a new backend, the required header is:
+
+ #include "../include/ese/hw/nxp/pn80t/common.h"
+
+Once included, the implementation must provide its own
+receive and transmit libese-hw functions and a struct Pn80tPlatform,
+defined in "../include/ese/hw/nxp/pn80t/platform.h".
+
+Platform requires a dedicated initialize, release, wait functions as
+well support for controlling NFC\_VEN, SVDD\_PWR\_REQ, and reset.
+
+See the bottom of the other implementations for an example of the
+required exports.
+
+Any other functionality, such as libese-sysdeps, may also need to be
+provided separately.
diff --git a/libese-hw/nxp/pn80t/common.c b/libese-hw/nxp/pn80t/common.c
new file mode 100644
index 0000000..e8d8096
--- /dev/null
+++ b/libese-hw/nxp/pn80t/common.c
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ *
+ * Support SPI communication with NXP PN553/PN80T secure element.
+ */
+
+#include "include/ese/hw/nxp/pn80t/common.h"
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647
+#endif
+
+int nxp_pn80t_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. */
+ ALOGV("interface is preprocessing inbound frame (%x->%x)",
+ frame->header.NAD, 0x00);
+ if (frame->header.NAD != opts->host_address) {
+ ALOGV("Rewriting from unknown NAD: %x", frame->header.NAD);
+ }
+ frame->header.NAD = 0x00;
+ ALOGV("Frame length: %x", frame->header.LEN);
+ }
+ return 0;
+}
+
+static const struct Teq1ProtocolOptions kTeq1Options = {
+ .host_address = 0xA5,
+ .node_address = 0x5A,
+ .bwt = 1.624f, /* cwt by default would be ~8k * 1.05s */
+ .etu = 0.00105f, /* seconds */
+ .preprocess = &nxp_pn80t_preprocess,
+};
+
+int nxp_pn80t_open(struct EseInterface *ese, void *board) {
+ struct NxpState *ns;
+ const struct Pn80tPlatform *platform;
+ if (sizeof(ese->pad) < sizeof(struct NxpState *)) {
+ /* This is a compile-time correctable error only. */
+ ALOGE("Pad size too small to use NXP HW (%zu < %zu)", sizeof(ese->pad),
+ sizeof(struct NxpState));
+ return -1;
+ }
+ platform = ese->ops->opts;
+
+ /* Ensure all required functions exist */
+ if (!platform->initialize || !platform->release || !platform->toggle_reset ||
+ !platform->wait) {
+ ALOGE("Required functions not implemented in supplied platform");
+ return -1;
+ }
+
+ ns = NXP_PN80T_STATE(ese);
+ TEQ1_INIT_CARD_STATE((struct Teq1CardState *)(&ese->pad[0]));
+ ns->handle = platform->initialize(board);
+ if (!ns->handle) {
+ ALOGE("platform initialization failed");
+ ese_set_error(ese, kNxpPn80tErrorPlatformInit);
+ return -1;
+ }
+ /* Toggle all required power GPIOs.
+ * Each platform may prefer to handle the power
+ * muxing specific. E.g., if NFC is in use, it would
+ * be unwise to unset VEN. However, the implementation
+ * here will attempt it if supported.
+ */
+ if (platform->toggle_ven) {
+ platform->toggle_ven(ns->handle, 1);
+ }
+ if (platform->toggle_power_req) {
+ platform->toggle_power_req(ns->handle, 1);
+ }
+ /* Power on eSE */
+ platform->toggle_reset(ns->handle, 1);
+ /* Let the eSE boot. */
+ platform->wait(ns->handle, 5000);
+ return 0;
+}
+
+int nxp_pn80t_reset(struct EseInterface *ese) {
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ if (platform->toggle_reset(ns->handle, 0) < 0) {
+ ese_set_error(ese, kNxpPn80tErrorResetToggle);
+ return -1;
+ }
+ platform->wait(ns->handle, 1000);
+ if (platform->toggle_reset(ns->handle, 1) < 0) {
+ ese_set_error(ese, kNxpPn80tErrorResetToggle);
+ return -1;
+ }
+ return 0;
+}
+
+int nxp_pn80t_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
+ int complete) {
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ /* Attempt to read a 8-bit character once per 8-bit character transmission
+ * window (in seconds). */
+ int intervals = (int)(0.5f + timeout / (7.0f * kTeq1Options.etu));
+ uint8_t byte = 0xff;
+ ALOGV("interface polling for start of frame/host node address: %x", poll_for);
+ /* If we had interrupts, we could just get notified by the driver. */
+ do {
+ /*
+ * In practice, if complete=true, then no transmission
+ * should attempt again until after 1000usec.
+ */
+ if (ese->ops->hw_receive(ese, &byte, 1, complete) != 1) {
+ ALOGV("failed to read one byte");
+ ese_set_error(ese, kNxpPn80tErrorPollRead);
+ return -1;
+ }
+ if (byte == poll_for) {
+ ALOGV("Polled for byte seen: %x with %d intervals remaining.", poll_for,
+ intervals);
+ ALOGV("RX[0]: %.2X", byte);
+ return 1;
+ } else {
+ ALOGV("No match (saw %x)", byte);
+ }
+ platform->wait(ns->handle,
+ 7.0f * kTeq1Options.etu * 1000000.0f); /* s -> us */
+ ALOGV("poll interval %d: no match.", intervals);
+ } while (intervals-- > 0);
+ return -1;
+}
+
+uint32_t nxp_pn80t_transceive(struct EseInterface *ese,
+ const uint8_t *const tx_buf, uint32_t tx_len,
+ uint8_t *rx_buf, uint32_t rx_len) {
+ /* TODO(wad) Should we toggle power on each call? */
+ return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len);
+}
+
+uint32_t nxp_pn80t_send_cooldown(struct EseInterface *ese) {
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ const uint8_t kCooldown[] = {0xa5, 0xc5, 0x00, 0xc5};
+ uint8_t rx_buf[8];
+ uint32_t *res = (uint32_t *)(&rx_buf[3]);
+ ese->ops->hw_transmit(ese, kCooldown, sizeof(kCooldown), 1);
+ nxp_pn80t_poll(ese, kTeq1Options.host_address, 5.0f, 0);
+ ese->ops->hw_receive(ese, rx_buf, sizeof(rx_buf), 1);
+ if (rx_buf[2] == 4) {
+ ALOGI("Cooldown value is %u", *res);
+ return *res;
+ } else {
+ ALOGI("Cooldown value unavailable");
+ }
+ return 0;
+}
+
+void nxp_pn80t_close(struct EseInterface *ese) {
+ struct NxpState *ns;
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ ALOGV("%s: called", __func__);
+ ns = NXP_PN80T_STATE(ese);
+ nxp_pn80t_send_cooldown(ese);
+ platform->toggle_reset(ns->handle, 0);
+ if (platform->toggle_power_req) {
+ platform->toggle_power_req(ns->handle, 0);
+ }
+ if (platform->toggle_ven) {
+ platform->toggle_ven(ns->handle, 0);
+ }
+ platform->release(ns->handle);
+ ns->handle = NULL;
+}
+
+const char *kNxpPn80tErrorMessages[] = {
+ /* The first three are required by teq1_transceive use. */
+ "T=1 hard failure.", "T=1 abort.", "T=1 device reset failed.",
+ /* The rest are pn80t impl specific. */
+ "failed to read one byte", "unable to initialize platform",
+ "failed to read", "attempted to receive too much data",
+ "attempted to transfer too much data", "failed to transmit",
+ "failed to toggle reset",
+};
diff --git a/libese-hw/nxp/pn80t/linux_spidev.c b/libese-hw/nxp/pn80t/linux_spidev.c
new file mode 100644
index 0000000..d4ac386
--- /dev/null
+++ b/libese-hw/nxp/pn80t/linux_spidev.c
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ *
+ * Defines the PN80T spidev device and platform wrappers consumed in
+ * the common code.
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <linux/spi/spidev.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "../include/ese/hw/nxp/pn80t/common.h"
+#include "../include/ese/hw/nxp/spi_board.h"
+
+struct Handle {
+ int spi_fd;
+ struct NxpSpiBoard *board;
+};
+
+int gpio_set(int num, int val) {
+ char val_path[256];
+ char val_chr = (val ? '1' : '0');
+ int fd;
+ if (num < 0) {
+ return 0;
+ }
+ if (snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value",
+ num) >= (int)sizeof(val_path)) {
+ return -1;
+ }
+ printf("Gpio @ %s\n", val_path);
+ fd = open(val_path, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ if (write(fd, &val_chr, 1) < 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+int platform_toggle_ven(void *blob, int val) {
+ struct Handle *handle = blob;
+ printf("Toggling VEN: %d\n", val);
+ return gpio_set(handle->board->gpios[kBoardGpioNfcVen], val);
+}
+
+int platform_toggle_reset(void *blob, int val) {
+ struct Handle *handle = blob;
+ printf("Toggling RST: %d\n", val);
+ return gpio_set(handle->board->gpios[kBoardGpioEseRst], val);
+}
+
+int platform_toggle_power_req(void *blob, int val) {
+ struct Handle *handle = blob;
+ printf("Toggling SVDD_PWR_REQ: %d\n", val);
+ return gpio_set(handle->board->gpios[kBoardGpioEseSvddPwrReq], val);
+}
+
+int gpio_configure(int num, int out, int val) {
+ char dir_path[256];
+ char numstr[8];
+ char dir[5];
+ int fd;
+ /* <0 is unmapped. No work to do! */
+ if (num < 0) {
+ return 0;
+ }
+ if (snprintf(dir, sizeof(dir), "%s", (out ? "out" : "in")) >=
+ (int)sizeof(dir)) {
+ return -1;
+ }
+ if (snprintf(dir_path, sizeof(dir_path), "/sys/class/gpio/gpio%d/direction",
+ num) >= (int)sizeof(dir_path)) {
+ return -1;
+ }
+ if (snprintf(numstr, sizeof(numstr), "%d", num) >= (int)sizeof(numstr)) {
+ return -1;
+ }
+ fd = open("/sys/class/gpio/export", O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ /* Exporting can only happen once, so instead of stat()ing, just ignore
+ * errors. */
+ (void)write(fd, numstr, strlen(numstr));
+ close(fd);
+
+ fd = open(dir_path, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+ if (write(fd, dir, strlen(dir)) < 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return gpio_set(num, val);
+}
+
+void *platform_init(void *hwopts) {
+ struct NxpSpiBoard *board = hwopts;
+ struct Handle *handle;
+ int gpio = 0;
+
+ handle = malloc(sizeof(*handle));
+ if (!handle) {
+ return NULL;
+ }
+ handle->board = board;
+
+ /* Initialize the mapped GPIOs */
+ for (; gpio < kBoardGpioMax; ++gpio) {
+ if (gpio_configure(board->gpios[gpio], 1, 1) < 0) {
+ free(handle);
+ return NULL;
+ }
+ }
+
+ handle->spi_fd = open(board->dev_path, O_RDWR);
+ if (handle->spi_fd < 0) {
+ free(handle);
+ return NULL;
+ }
+ /* If we need anything fancier, we'll need MODE32 in the headers. */
+ if (ioctl(handle->spi_fd, SPI_IOC_WR_MODE, &board->mode) < 0) {
+ close(handle->spi_fd);
+ free(handle);
+ return NULL;
+ }
+ if (ioctl(handle->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &board->bits) < 0) {
+ close(handle->spi_fd);
+ free(handle);
+ return NULL;
+ }
+ if (ioctl(handle->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &board->speed) < 0) {
+ close(handle->spi_fd);
+ free(handle);
+ return NULL;
+ }
+ printf("Linux SPIDev initialized\n");
+ return (void *)handle;
+}
+
+int platform_release(void *blob) {
+ struct Handle *handle = blob;
+ close(handle->spi_fd);
+ free(handle);
+ /* Note, we don't unconfigure the GPIOs. */
+ return 0;
+}
+
+int platform_wait(void *blob __attribute__((unused)), long usec) {
+ return usleep((useconds_t)usec);
+}
+
+uint32_t spidev_transmit(struct EseInterface *ese, const uint8_t *buf,
+ uint32_t len, int complete) {
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ struct Handle *handle = ns->handle;
+ struct spi_ioc_transfer tr = {
+ .tx_buf = (unsigned long)buf,
+ .rx_buf = 0,
+ .len = (uint32_t)len,
+ .delay_usecs = 0,
+ .speed_hz = 0,
+ .bits_per_word = 0,
+ .cs_change = !!complete,
+ };
+ ssize_t ret = -1;
+ ALOGV("spidev:%s: called [%d]", __func__, len);
+ if (len > INT_MAX) {
+ ese_set_error(ese, kNxpPn80tErrorTransmitSize);
+ ALOGE("Unexpectedly large transfer attempted: %u", len);
+ return 0;
+ }
+ ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
+ if (ret < 1) {
+ ese_set_error(ese, kNxpPn80tErrorTransmit);
+ ALOGE("%s: failed to write to hw (ret=%zd)", __func__, ret);
+ return 0;
+ }
+ return len;
+}
+
+uint32_t spidev_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
+ int complete) {
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ struct Handle *handle = ns->handle;
+ ssize_t ret = -1;
+ struct spi_ioc_transfer tr = {
+ .tx_buf = 0,
+ .rx_buf = (unsigned long)buf,
+ .len = (uint32_t)len,
+ .delay_usecs = 0,
+ .speed_hz = 0,
+ .bits_per_word = 0,
+ .cs_change = !!complete,
+ };
+ ALOGV("spidev:%s: called [%d]", __func__, len);
+ if (len > INT_MAX) {
+ ese_set_error(ese, kNxpPn80tErrorReceiveSize);
+ ALOGE("Unexpectedly large receive attempted: %u", len);
+ return 0;
+ }
+ ret = ioctl(handle->spi_fd, SPI_IOC_MESSAGE(1), &tr);
+ if (ret < 1) {
+ ALOGE("%s: failed to read from hw (ret=%zd)", __func__, ret);
+ ese_set_error(ese, kNxpPn80tErrorReceive);
+ return 0;
+ }
+ ALOGV("%s: read bytes: %zd", __func__, len);
+ return len;
+}
+
+static const struct Pn80tPlatform kPn80tLinuxSpidevPlatform = {
+ .initialize = &platform_init,
+ .release = &platform_release,
+ .toggle_reset = &platform_toggle_reset,
+ .toggle_ven = &platform_toggle_ven,
+ .toggle_power_req = &platform_toggle_power_req,
+ .wait = &platform_wait,
+};
+
+static const struct EseOperations ops = {
+ .name = "NXP PN80T/PN81A (PN553)",
+ .open = &nxp_pn80t_open,
+ .hw_receive = &spidev_receive,
+ .hw_transmit = &spidev_transmit,
+ .hw_reset = &nxp_pn80t_reset,
+ .transceive = &nxp_pn80t_transceive,
+ .poll = &nxp_pn80t_poll,
+ .close = &nxp_pn80t_close,
+ .opts = &kPn80tLinuxSpidevPlatform,
+ .errors = kNxpPn80tErrorMessages,
+ .errors_count = kNxpPn80tErrorMax,
+};
+__attribute__((visibility("default")))
+ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops);
diff --git a/libese-hw/nxp/pn80t/nq_nci.c b/libese-hw/nxp/pn80t/nq_nci.c
new file mode 100644
index 0000000..5b20f0f
--- /dev/null
+++ b/libese-hw/nxp/pn80t/nq_nci.c
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ *
+ * Platform implementation for a nq-nci extension driver.
+ *
+ * The driver presents the following interface on a miscdev:
+ * - ioctl():
+ * - for setting and getting power.
+ * This handles SVDD_PWR_REQ and NFC_VEN muxing.
+ * (ESE_RST is not connected in this case.)
+ * - read():
+ * - For reading arbitrary amounts of data.
+ * CS is asserted and deasserted on each call, but the clock
+ * also appears to do the same which keeps the ese data available
+ * as far as I can tell.
+ * - write():
+ * - For writing arbitrary amounts of data.
+ * CS is asserted as with read() calls, so the less fragmented
+ * the better.
+ *
+ * All GPIO toggling and chip select requirements are handled behind this
+ * interface.
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "../include/ese/hw/nxp/pn80t/common.h"
+
+#ifndef UNUSED
+#define UNUSED(x) x __attribute__((unused))
+#endif
+
+/* From kernel/drivers/nfc/nq-nci.h */
+#define ESE_SET_PWR _IOW(0xE9, 0x02, unsigned int)
+#define ESE_GET_PWR _IOR(0xE9, 0x03, unsigned int)
+
+static const char kDevicePath[] = "/dev/pn81a";
+
+struct PlatformHandle {
+ int fd;
+};
+
+int platform_toggle_reset(void *blob, int val) {
+ const struct PlatformHandle *handle = blob;
+ /* 0=power and 1=no power in the kernel. */
+ return ioctl(handle->fd, ESE_SET_PWR, !val);
+}
+
+void *platform_init(void *hwopts) {
+ /* TODO(wad): It may make sense to pass in the dev path here. */
+ if (hwopts != NULL) {
+ return NULL;
+ }
+
+ struct PlatformHandle *handle = calloc(1, sizeof(*handle));
+ if (!handle) {
+ ALOGV("%s: unable to allocate memory for handle", __func__);
+ return NULL;
+ }
+ handle->fd = open(kDevicePath, O_RDWR);
+ if (handle->fd < 0) {
+ free(handle);
+ return NULL;
+ }
+ return handle;
+}
+
+int platform_release(void *blob) {
+ struct PlatformHandle *handle = blob;
+ /* Power off and cooldown should've happened via common code. */
+ close(handle->fd);
+ free(handle);
+ return 0;
+}
+
+int platform_wait(void *UNUSED(blob), long usec) {
+ return usleep((useconds_t)usec);
+}
+
+uint32_t nq_transmit(struct EseInterface *ese, const uint8_t *buf, uint32_t len,
+ int UNUSED(complete)) {
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ uint32_t bytes = 0;
+ ALOGV("nq_nci:%s: called [%d]", __func__, len);
+ if (len > INT_MAX) {
+ ese_set_error(ese, kNxpPn80tErrorTransmitSize);
+ ALOGE("Unexpectedly large transfer attempted: %u", len);
+ return 0;
+ }
+ if (len == 0)
+ return len;
+ const struct PlatformHandle *handle = ns->handle;
+ while (bytes < len) {
+ ssize_t ret = write(handle->fd, (void *)(buf + bytes), len - bytes);
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ }
+ ese_set_error(ese, kNxpPn80tErrorTransmit);
+ ALOGE("%s: failed to write to hw (ret=%zd, errno=%d)", __func__, ret,
+ errno);
+ return 0;
+ }
+ bytes += ret;
+ }
+ return len;
+}
+
+uint32_t nq_receive(struct EseInterface *ese, uint8_t *buf, uint32_t len,
+ int UNUSED(complete)) {
+ const struct Pn80tPlatform *platform = ese->ops->opts;
+ struct NxpState *ns = NXP_PN80T_STATE(ese);
+ ALOGV("nq_nci:%s: called [%d]", __func__, len);
+ if (!ns) {
+ ALOGE("NxpState was NULL");
+ return 0;
+ }
+ if (len > INT_MAX) {
+ ese_set_error(ese, kNxpPn80tErrorReceiveSize);
+ ALOGE("Unexpectedly large receive attempted: %u", len);
+ return 0;
+ }
+ const struct PlatformHandle *handle = ns->handle;
+ if (len == 0) {
+ return 0;
+ }
+ uint32_t bytes = 0;
+ while (bytes < len) {
+ ssize_t ret = read(handle->fd, (void *)(buf + bytes), len - bytes);
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ }
+ ALOGE("%s: failed to read from hw (ret=%zd, errno=%d)", __func__, ret,
+ errno);
+ ese_set_error(ese, kNxpPn80tErrorReceive);
+ return 0;
+ }
+ bytes += ret;
+ }
+ ALOGV("%s: read bytes: %u", __func__, bytes);
+ return len;
+}
+
+static const struct Pn80tPlatform kPn80tNqNciPlatform = {
+ .initialize = &platform_init,
+ .release = &platform_release,
+ .toggle_reset = &platform_toggle_reset,
+ .toggle_ven = NULL,
+ .toggle_power_req = NULL,
+ .wait = &platform_wait,
+};
+
+static const struct EseOperations ops = {
+ .name = "NXP PN80T/PN81A (NQ-NCI:PN553)",
+ .open = &nxp_pn80t_open,
+ .hw_receive = &nq_receive,
+ .hw_transmit = &nq_transmit,
+ .hw_reset = &nxp_pn80t_reset,
+ .transceive = &nxp_pn80t_transceive,
+ .poll = &nxp_pn80t_poll,
+ .close = &nxp_pn80t_close,
+ .opts = &kPn80tNqNciPlatform,
+ .errors = kNxpPn80tErrorMessages,
+ .errors_count = kNxpPn80tErrorMax,
+};
+__attribute__((visibility("default")))
+ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_NQ_NCI, ops);
diff --git a/libese-hw/nxp/pn80t_spidev.c b/libese-hw/nxp/pn80t_spidev.c
deleted file mode 100644
index 3f63103..0000000
--- a/libese-hw/nxp/pn80t_spidev.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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.
- *
- * Support SPI communication with NXP PN553/PN80T secure element.
- */
-
-#include <fcntl.h>
-#include <limits.h>
-#include <linux/spi/spidev.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-#include <ese/ese.h>
-#include <ese/hw/nxp/spi_board.h>
-#include <ese/teq1.h>
-#define LOG_TAG "libese-hw"
-#include <ese/log.h>
-
-/* Card state is _required_ to be at the front of eSE pad. */
-struct NxpState {
- struct Teq1CardState card_state;
- int spi_fd;
- struct NxpSpiBoard *board;
-};
-#define NXP_PN80T_SPIDEV_STATE(ese) ((struct NxpState *)(&ese->pad[0]))
-
-int gpio_set(int num, int val) {
- char val_path[256];
- char val_chr = (val ? '1' : '0');
- int fd;
- if (snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value",
- num) >= (int)sizeof(val_path))
- return -1;
- fd = open(val_path, O_WRONLY);
- if (fd < 0)
- return -1;
- if (write(fd, &val_chr, 1) < 0) {
- close(fd);
- return -1;
- }
- close(fd);
- return 0;
-}
-
-int gpio_configure(int num, int out, int val) {
- char dir_path[256];
- char numstr[8];
- char dir[5];
- int fd;
- if (snprintf(dir, sizeof(dir), "%s", (out ? "out" : "in")) >=
- (int)sizeof(dir))
- return -1;
- if (snprintf(dir_path, sizeof(dir_path), "/sys/class/gpio/gpio%d/direction",
- num) >= (int)sizeof(dir_path))
- return -1;
- if (snprintf(numstr, sizeof(numstr), "%d", num) >= (int)sizeof(numstr))
- return -1;
- fd = open("/sys/class/gpio/export", O_WRONLY);
- if (fd < 0)
- return -1;
- /* Exporting can only happen once, so instead of stat()ing, just ignore
- * errors. */
- (void)write(fd, numstr, strlen(numstr));
- close(fd);
-
- fd = open(dir_path, O_WRONLY);
- if (fd < 0)
- return -1;
- if (write(fd, dir, strlen(dir)) < 0) {
- close(fd);
- return -1;
- }
- close(fd);
- return gpio_set(num, val);
-}
-
-int nxp_pn80t_open(struct EseInterface *ese, void *hw_opts) {
- struct NxpState *ns;
- uint8_t mode = 0;
- uint32_t bits = 8;
- uint32_t speed = 1000000L;
- if (!ese)
- return -1;
- if (sizeof(ese->pad) < sizeof(struct NxpState *)) {
- /* This is a compile-time correctable error only. */
- ALOGE("Pad size too small to use NXP HW (%zu < %zu)", sizeof(ese->pad),
- sizeof(struct NxpState));
- return -1;
- }
- ns = NXP_PN80T_SPIDEV_STATE(ese);
- TEQ1_INIT_CARD_STATE((&ns->card_state));
- ns->board = (struct NxpSpiBoard *)(hw_opts);
-
- /* Configure ESE_SVDD_PWR_REQ */
- /* TODO(wad): We can leave this low and move it to set/unset
- * in a transceive() wrapper.
- */
- if (gpio_configure(ns->board->svdd_pwr_req_gpio, 1, 1) < 0) {
- ese_set_error(ese, 13);
- return -1;
- }
-
- /* Configure ESE_RST_GPIO */
- if (gpio_configure(ns->board->reset_gpio, 1, 1) < 0) {
- ese_set_error(ese, 12);
- return -1;
- }
-
- ns->spi_fd = open(ns->board->dev_path, O_RDWR);
- if (ns->spi_fd < 0) {
- ALOGE("failed to open spidev: %s", ns->board->dev_path);
- ese_set_error(ese, 4);
- return -1;
- }
-
- /* If we need anything fancier, we'll need MODE32 in the headers. */
- if (ioctl(ns->spi_fd, SPI_IOC_WR_MODE, &mode) < 0) {
- ALOGE("failed to set spidev mode to %d", mode);
- ese_set_error(ese, 5);
- return -1;
- }
- if (ioctl(ns->spi_fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {
- ALOGE("failed to set spidev bits per word to %d", bits);
- ese_set_error(ese, 6);
- return -1;
- }
- if (ioctl(ns->spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
- ALOGE("failed to set spidev max speed to %dhz", speed);
- ese_set_error(ese, 7);
- return -1;
- }
- return 0;
-}
-
-int nxp_pn80t_close(struct EseInterface *ese) {
- struct NxpState *ns;
- if (!ese)
- return -1;
- ns = NXP_PN80T_SPIDEV_STATE(ese);
- close(ns->spi_fd);
- /* We're done. */
- gpio_set(ns->board->svdd_pwr_req_gpio, 0);
- return 0;
-}
-
-size_t nxp_pn80t_receive(struct EseInterface *ese, uint8_t *buf, size_t len,
- int complete) {
- struct NxpState *ns = NXP_PN80T_SPIDEV_STATE(ese);
- size_t recvd = len;
- struct spi_ioc_transfer tr = {
- .tx_buf = 0,
- .rx_buf = (unsigned long)buf,
- .len = (uint32_t)len,
- .delay_usecs = 0,
- .speed_hz = 0,
- .bits_per_word = 0,
- .cs_change = !!complete,
- };
-
- if (len > UINT_MAX) {
- ese_set_error(ese, 9);
- ALOGE("Unexpectedly large receive attempted: %zu", len);
- return 0;
- }
-
- ALOGV("interface attempting to receive card data");
- if (ioctl(ns->spi_fd, SPI_IOC_MESSAGE(1), &tr) < 1) {
- ese_set_error(ese, 8);
- return 0;
- }
- ALOGV("card sent %zu bytes", len);
- for (recvd = 0; recvd < len; ++recvd)
- ALOGV("RX[%zu]: %.2X", recvd, buf[recvd]);
- if (complete) {
- ALOGV("card sent a frame");
- /* XXX: cool off the bus for 1ms [t_3] */
- }
- return recvd;
-}
-
-int nxp_pn80t_reset(struct EseInterface *ese) {
- struct NxpState *ns = NXP_PN80T_SPIDEV_STATE(ese);
- if (gpio_set(ns->board->reset_gpio, 0) < 0) {
- ese_set_error(ese, 13);
- return -1;
- }
- usleep(1000);
- if (gpio_set(ns->board->reset_gpio, 1) < 0) {
- ese_set_error(ese, 13);
- return -1;
- }
- return 0;
-}
-
-size_t nxp_pn80t_transmit(struct EseInterface *ese, const uint8_t *buf,
- size_t len, int complete) {
- struct NxpState *ns = NXP_PN80T_SPIDEV_STATE(ese);
- size_t recvd = len;
- struct spi_ioc_transfer tr = {
- .tx_buf = (unsigned long)buf,
- .rx_buf = 0,
- .len = (uint32_t)len,
- .delay_usecs = 0,
- .speed_hz = 0,
- .bits_per_word = 0,
- .cs_change = !!complete,
- };
- ALOGV("interface transmitting data");
- if (len > UINT_MAX) {
- ese_set_error(ese, 10);
- ALOGE("Unexpectedly large transfer attempted: %zu", len);
- return 0;
- }
-
- ALOGV("interface attempting to transmit data");
- for (recvd = 0; recvd < len; ++recvd)
- ALOGV("TX[%zu]: %.2X", recvd, buf[recvd]);
- if (ioctl(ns->spi_fd, SPI_IOC_MESSAGE(1), &tr) < 1) {
- ese_set_error(ese, 11);
- return 0;
- }
- ALOGV("interface sent %zu bytes", len);
- if (complete) {
- ALOGV("interface sent a frame");
- /* TODO(wad): Remove this once we have live testing. */
- usleep(1000); /* t3 = 1ms */
- }
- return recvd;
-}
-
-int nxp_pn80t_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
- int complete) {
- struct NxpState *es = NXP_PN80T_SPIDEV_STATE(ese);
- const struct Teq1ProtocolOptions *opts = ese->ops->opts;
- /* Attempt to read a 8-bit character once per 8-bit character transmission
- * window (in seconds). */
- int intervals = (int)(0.5f + timeout / (7.0f * opts->etu));
- uint8_t byte = 0xff;
- ALOGV("interface polling for start of frame/host node address: %x", poll_for);
- /* If we weren't using spidev, we could just get notified by the driver. */
- do {
- struct spi_ioc_transfer tr = {
- .tx_buf = 0,
- .rx_buf = (unsigned long)&byte,
- .len = 1,
- .delay_usecs = 0,
- .speed_hz = 0,
- .bits_per_word = 0,
- .cs_change = !!complete,
- };
- /*
- * In practice, if complete=true, then no transmission
- * should attempt again until after 1000usec.
- */
- if (ioctl(es->spi_fd, SPI_IOC_MESSAGE(1), &tr) < 1) {
- ALOGV("spidev (fd:%d) failed to read one byte", es->spi_fd);
- ese_set_error(ese, 3);
- return -1;
- }
- if (byte == poll_for) {
- ALOGV("Polled for byte seen: %x with %d intervals remaining.", poll_for,
- intervals);
- ALOGV("RX[0]: %.2X", byte);
- return 1;
- } else {
- ALOGV("No match (saw %x)", byte);
- }
- usleep(7.0f * opts->etu * 1000000.0f); /* s -> us */
- ALOGV("poll interval %d: no match.", intervals);
- } while (intervals-- > 0);
- return -1;
-}
-
-int nxp_pn80t_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. */
- ALOGV("interface is preprocessing inbound frame (%x->%x)",
- frame->header.NAD, 0x00);
- if (frame->header.NAD != opts->host_address)
- ALOGV("Rewriting from unknown NAD: %x", frame->header.NAD);
- frame->header.NAD = 0x00;
- ALOGV("Frame length: %x", frame->header.LEN);
- }
- return 0;
-}
-
-static const struct Teq1ProtocolOptions teq1_options = {
- .host_address = 0xA5,
- .node_address = 0x5A,
- .bwt = 1.624f, /* cwt by default would be ~8k * 1.05s */
- .etu = 0.00105f, /* seconds */
- .preprocess = &nxp_pn80t_preprocess,
-};
-
-static const struct EseOperations ops = {
- .name = "NXP PN80T (PN553)",
- .open = &nxp_pn80t_open,
- .hw_receive = &nxp_pn80t_receive,
- .hw_transmit = &nxp_pn80t_transmit,
- .hw_reset = &nxp_pn80t_reset,
- .transceive = &teq1_transceive,
- .poll = &nxp_pn80t_poll,
- .close = &nxp_pn80t_close,
- .opts = &teq1_options,
-};
-ESE_DEFINE_HW_OPS(ESE_HW_NXP_PN80T_SPIDEV, ops);
-
-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 */
- "spidev failed to read one byte", /* 3 */
- "unable to open spidev device", /* 4 */
- "unable to set spidev mode", /* 5 */
- "unable to set spidev bits per word", /* 6 */
- "unable to set spidev max speed in hz", /* 7 */
- "spidev failed to read", /* 8 */
- "attempted to receive more than uint_max", /* 9 */
- "attempted to transfer more than uint_max", /* 10 */
- "spidev failed to transmit", /* 11 */
- "unable to configure ESE_RST gpio", /* 12 */
- "unable to configure ESE_SVDD_PWR_REQ gpio", /* 13 */
- "unable to toggle ESE_SVDD_PWR_REQ", /* 14 */
-};
-ESE_DEFINE_HW_ERRORS(ESE_HW_NXP_PN80T_SPIDEV, kErrorMessages);
diff --git a/libese-hw/tests/Android.bp b/libese-hw/tests/Android.bp
index 1ce0b6b..75aaf21 100644
--- a/libese-hw/tests/Android.bp
+++ b/libese-hw/tests/Android.bp
@@ -14,30 +14,14 @@
// limitations under the License.
//
-cc_defaults {
- name: "libese_hw_tests_default",
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
-
-test_libraries = [
- "libese",
- "libese-teq1",
- "libese-hw-echo",
- "liblog",
-]
-
cc_test {
name: "ese_hw_tests",
- defaults: ["libese_tests_default"],
srcs: ["ese_hw_echo_tests.cpp"],
host_supported: true,
- shared_libs: test_libraries,
+ shared_libs: [
+ "libese",
+ "libese-teq1",
+ "libese-hw-echo",
+ "liblog",
+ ],
}
diff --git a/libese-hw/tests/ese_hw_echo_tests.cpp b/libese-hw/tests/ese_hw_echo_tests.cpp
index 4c62822..793f506 100644
--- a/libese-hw/tests/ese_hw_echo_tests.cpp
+++ b/libese-hw/tests/ese_hw_echo_tests.cpp
@@ -34,7 +34,7 @@
ASSERT_EQ(0, ese_open(&ese_, NULL));
}
virtual void TearDown() {
- ASSERT_EQ(0, ese_close(&ese_));
+ ese_close(&ese_);
}
struct EseInterface ese_;
};
diff --git a/libese-sysdeps/Android.bp b/libese-sysdeps/Android.bp
new file mode 100644
index 0000000..b9b412a
--- /dev/null
+++ b/libese-sysdeps/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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.
+//
+
+cc_library {
+ name: "libese-sysdeps",
+ host_supported: true,
+ srcs: [],
+ local_include_dirs: [
+ "include",
+ ],
+ cflags: ["-std=c99"],
+ debug: {
+ cflags: ["-DLOG_NDEBUG=0"],
+ },
+ target: {
+ android: {
+ srcs: ["android-sysdeps.c"],
+ },
+
+ darwin: {
+ srcs: ["darwin-sysdeps.c"],
+ },
+
+ linux: {
+ srcs: ["android-sysdeps.c"],
+ },
+ },
+
+ shared_libs: [],
+ export_include_dirs: ["include"],
+}
diff --git a/libese-sysdeps/android-sysdeps.c b/libese-sysdeps/android-sysdeps.c
new file mode 100644
index 0000000..5cfb447
--- /dev/null
+++ b/libese-sysdeps/android-sysdeps.c
@@ -0,0 +1,26 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+
+void *ese_memcpy(void *__dest, const void *__src, uint64_t __n) {
+ return memcpy(__dest, __src, __n);
+}
+
+void *ese_memset(void *__s, int __c, uint64_t __n) {
+ return memset(__s, __c, __n);
+}
diff --git a/libese-sysdeps/darwin-sysdeps.c b/libese-sysdeps/darwin-sysdeps.c
new file mode 100644
index 0000000..5cfb447
--- /dev/null
+++ b/libese-sysdeps/darwin-sysdeps.c
@@ -0,0 +1,26 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+
+void *ese_memcpy(void *__dest, const void *__src, uint64_t __n) {
+ return memcpy(__dest, __src, __n);
+}
+
+void *ese_memset(void *__s, int __c, uint64_t __n) {
+ return memset(__s, __c, __n);
+}
diff --git a/libese-sysdeps/include/ese/sysdeps.h b/libese-sysdeps/include/ese/sysdeps.h
new file mode 100644
index 0000000..94ebfe5
--- /dev/null
+++ b/libese-sysdeps/include/ese/sysdeps.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+#ifndef ESE_SYSDEPS_H__
+#define ESE_SYSDEPS_H__ 1
+
+#include <stdbool.h> /* bool */
+#include <stdint.h> /* uint*_t */
+
+#ifndef NULL
+#define NULL ((void *)(0))
+#endif
+
+extern void *ese_memcpy(void *__dest, const void *__src, uint64_t __n);
+extern void *ese_memset(void *__s, int __c, uint64_t __n);
+
+#endif /* ESE_SYSDEPS_H__ */
diff --git a/libese-teq1/Android.bp b/libese-teq1/Android.bp
index 86b9a41..76454f4 100644
--- a/libese-teq1/Android.bp
+++ b/libese-teq1/Android.bp
@@ -23,24 +23,24 @@
local_include_dirs: ["include"],
// Ensure that only explicitly exported symbols are visible.
- cflags: ["-fvisibility=internal"],
+ cflags: ["-fvisibility=internal", "-std=c99"],
debug: {
- cflags: ["-DLOG_NDEBUG=0"],
+ cflags: ["-DLOG_NDEBUG=0"],
},
- shared_libs: ["liblog", "libese"],
+ shared_libs: ["liblog", "libese", "libese-sysdeps"],
export_include_dirs: ["include"],
}
cc_library {
- name: "libese-teq1-private",
- host_supported: true,
+ name: "libese-teq1-private",
+ host_supported: true,
- srcs: ["teq1.c"],
- local_include_dirs: ["include"],
+ srcs: ["teq1.c"],
+ local_include_dirs: ["include"],
- // Ensure that only explicitly exported symbols are visible.
- shared_libs: ["liblog", "libese"],
- export_include_dirs: ["include", "."],
+ // Ensure that only explicitly exported symbols are visible.
+ shared_libs: ["liblog", "libese", "libese-sysdeps"],
+ export_include_dirs: ["include", "."],
}
subdirs = ["tests"]
diff --git a/libese-teq1/include/ese/teq1.h b/libese-teq1/include/ese/teq1.h
index 61831a5..8a3fa2e 100644
--- a/libese-teq1/include/ese/teq1.h
+++ b/libese-teq1/include/ese/teq1.h
@@ -21,20 +21,22 @@
extern "C" {
#endif
-#include <ese/ese.h>
+#include "../../../libese/include/ese/ese.h"
+#include "../../../libese/include/ese/bit_spec.h"
-/* Reserved codes for T=1 devices. */
-#define TEQ1_ERROR_HARD_FAIL 0
-#define TEQ1_ERROR_ABORT 1
-#define TEQ1_ERROR_DEVICE_RESET 2
-
+/* Reserved codes for T=1 devices in EseOperation>errors. */
+enum Teq1Error {
+ kTeq1ErrorHardFail = 0,
+ kTeq1ErrorAbort,
+ kTeq1ErrorDeviceReset,
+ kTeq1ErrorMax,
+};
enum pcb_type {
kPcbTypeInfo0 = 0x0,
kPcbTypeInfo1 = 0x1,
kPcbTypeReceiveReady = 0x2,
kPcbTypeSupervisory = 0x3,
- kPcbTypeMax,
};
enum super_type {
@@ -44,54 +46,52 @@
kSuperTypeWTX = 0x3,
};
-struct PCB {
- union {
- /* Info bits */
- struct {
- uint8_t reserved:5; /* Should be 0. */
- uint8_t more_data:1;
- uint8_t send_seq:1;
- uint8_t bit8:1; /* Must be 0 for I-blocks. */
- } I; /* Information Block */
- /* receive bits */
- struct {
- uint8_t parity_err:1; /* char parity or redundancy code err */
- uint8_t other_err:1; /* any other errors */
- uint8_t unused_1:2;
- uint8_t next_seq:1; /* If the same seq as last frame, then err even if other bits are 0. */
- uint8_t unused_0:1;
- uint8_t pcb_type:2; /* Always (1, 0)=2 for R */
- } R; /* Receive ready block */
- struct {
- uint8_t type:2;
- uint8_t unused_0:3;
- uint8_t response:1;
- uint8_t pcb_type:2;
- } S; /* Supervisory block */
- struct {
- uint8_t data:6;
- uint8_t type:2; /* I = 0|1, R = 2, S = 3 */
- }; /* Bit7-8 access for block type access. */
- /* Bitwise access */
- struct {
- uint8_t bit0:1; /* lsb */
- uint8_t bit1:1;
- uint8_t bit2:1;
- uint8_t bit3:1;
- uint8_t bit4:1;
- uint8_t bit5:1;
- uint8_t bit6:1;
- uint8_t bit7:1; /* msb */
- } bits;
- uint8_t val;
- };
+struct PcbSpec {
+ struct bit_spec type;
+ struct bit_spec data;
+ struct {
+ struct bit_spec more_data;
+ struct bit_spec send_seq;
+ } I;
+ struct {
+ struct bit_spec parity_err;
+ struct bit_spec other_err;
+ struct bit_spec next_seq;
+ } R;
+ struct {
+ struct bit_spec type;
+ struct bit_spec response;
+ } S;
+};
+
+const static struct PcbSpec PCB = {
+ .type = { .value = 3, .shift = 6, },
+ .data = { .value = 63, .shift = 0, },
+ .I = {
+ .more_data = { .value = 1, .shift = 5, },
+ .send_seq = { .value = 1, .shift = 6, },
+ },
+ .R = {
+ /* char parity or redundancy code err */
+ .parity_err = { .value = 1, .shift = 0, },
+ /* any other errors */
+ .other_err = { .value = 1, .shift = 1, },
+ /* If the same seq as last frame, then err even if other bits are 0. */
+ .next_seq = { .value = 1, .shift = 4, },
+ },
+ .S = {
+ .type = { .value = 3, .shift = 0, },
+ .response = { .value = 1, .shift = 5, },
+ },
};
struct Teq1Header {
uint8_t NAD;
- struct PCB PCB;
+ uint8_t PCB;
uint8_t LEN;
-} __attribute__((packed));
+};
+#define TEQ1HEADER_SIZE 3
+#define TEQ1FRAME_SIZE INF_LEN + 1 + TEQ1HEADER_SIZE
#define INF_LEN 254
#define IFSC 254
@@ -103,10 +103,10 @@
union {
uint8_t INF[INF_LEN + 1]; /* Up to 254 with trailing LRC byte. */
};
- /* uint8_t LRC; If CRC was supported, it would be uint16_t. */
+ /* If CRC was supported, it would be uint16_t. */
};
};
-} __attribute__((packed));
+};
/*
@@ -122,6 +122,7 @@
uint8_t seq_bits;
} seq;
};
+
/* Set "last sent" to 1 so we start at 0. */
#define TEQ1_INIT_CARD_STATE(CARD) \
(CARD)->seq.card = 1; \
@@ -146,7 +147,11 @@
teq1_protocol_preprocess_op_t *preprocess;
};
+/* PCB bits */
+#define kTeq1PcbType (3 << 6)
+
/* I-block bits */
+#define kTeq1InfoType (0 << 6)
#define kTeq1InfoMoreBit (1 << 5)
#define kTeq1InfoSeqBit (1 << 6)
@@ -176,9 +181,10 @@
#define TEQ1_S_ABORT(R) (kTeq1SuperType | ((R) << 5) | kTeq1SuperAbortBit)
#define TEQ1_S_IFS(R) (kTeq1SuperType | ((R) << 5) | kTeq1SuperIfsBit)
-size_t teq1_transceive(struct EseInterface *ese,
- const uint8_t *const tx_buf, size_t tx_len,
- uint8_t *rx_buf, size_t rx_max);
+uint32_t teq1_transceive(struct EseInterface *ese,
+ const struct Teq1ProtocolOptions *opts,
+ const uint8_t *const tx_buf, uint32_t tx_len,
+ uint8_t *rx_buf, uint32_t rx_max);
uint8_t teq1_compute_LRC(const struct Teq1Frame *frame);
diff --git a/libese-teq1/teq1.c b/libese-teq1/teq1.c
index 9d0b2a5..40d2b1d 100644
--- a/libese-teq1/teq1.c
+++ b/libese-teq1/teq1.c
@@ -14,13 +14,9 @@
* limitations under the License.
*/
-#include <string.h>
-
-#include <ese/ese.h>
-#include <ese/teq1.h>
-
-#define LOG_TAG "ESE_T=1"
-#include <ese/log.h>
+#include "include/ese/teq1.h"
+#include "../libese/include/ese/ese.h"
+#include "../libese/include/ese/log.h"
#include "teq1_private.h"
@@ -94,10 +90,15 @@
}
}
-int teq1_transmit(struct EseInterface *ese, struct Teq1Frame *frame) {
- /* Set during ese_open() by teq1_init. */
- const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+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;
@@ -106,40 +107,48 @@
/*
* If the card does something weird, like expect an CRC/LRC based on a
- * different
- * header value, the preprocessing can handle it.
+ * different header value, the preprocessing can handle it.
*/
- if (opts->preprocess)
+ 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.val, frame->header.LEN);
+ 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.
+ /*
+ * 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, float timeout,
+int teq1_receive(struct EseInterface *ese,
+ const struct Teq1ProtocolOptions *opts, float timeout,
struct Teq1Frame *frame) {
- const struct Teq1ProtocolOptions *opts = ese->ops->opts;
/* Poll the bus until we see the start of frame indicator, the interface NAD.
*/
- if (ese->ops->poll(ese, opts->host_address, timeout, 0) < 0) {
+ 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. */
- frame->header.NAD = opts->host_address;
+ /* 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.PCB),
- sizeof(frame->header) - 1, 0);
+ 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. */
@@ -152,15 +161,17 @@
*/
ese->ops->hw_receive(ese, (uint8_t *)(&(frame->INF[0])),
frame->header.LEN + 1, 1);
- teq1_trace_receive(frame->header.PCB.val, frame->header.LEN);
+ 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)
+ 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. */
@@ -168,15 +179,17 @@
uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame) {
uint32_t inf_len = INF_LEN;
- if (state->ifs < inf_len)
+ if (state->ifs < inf_len) {
inf_len = state->ifs;
- switch (frame->header.PCB.type) {
+ }
+ switch (bs_get(PCB.type, frame->header.PCB)) {
case kPcbTypeInfo0:
case kPcbTypeInfo1: {
uint32_t len = state->app_data.tx_len;
- if (len > inf_len)
+ if (len > inf_len) {
len = inf_len;
- memcpy(frame->INF, state->app_data.tx_buf, len);
+ }
+ ese_memcpy(frame->INF, state->app_data.tx_buf, len);
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
@@ -184,9 +197,10 @@
state->app_data.tx_len -= len;
state->app_data.tx_buf += len;
/* Perform chained transmission if needed. */
- frame->header.PCB.I.more_data = 0;
- if (state->app_data.tx_len > 0)
- frame->header.PCB.I.more_data = 1;
+ bs_assign(&frame->header.PCB, PCB.I.more_data, 0);
+ if (state->app_data.tx_len > 0) {
+ frame->header.PCB |= bs_mask(PCB.I.more_data, 1);
+ }
return len;
}
case kPcbTypeSupervisory:
@@ -198,14 +212,15 @@
}
void teq1_get_app_data(struct Teq1State *state, struct Teq1Frame *frame) {
- switch (frame->header.PCB.type) {
+ switch (bs_get(PCB.type, frame->header.PCB)) {
case kPcbTypeInfo0:
case kPcbTypeInfo1: {
uint8_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_len)
+ if (len > state->app_data.rx_len) {
len = state->app_data.rx_len;
- memcpy(state->app_data.rx_buf, frame->INF, len);
+ }
+ ese_memcpy(state->app_data.rx_buf, frame->INF, len);
/* The original caller must retain the starting pointer to determine
* actual available data.
*/
@@ -227,8 +242,9 @@
struct Teq1Frame *rx_frame) {
uint8_t lrc = 0;
int chained = 0;
- if (rx_frame->header.PCB.val == 255)
+ 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) {
@@ -238,46 +254,42 @@
}
/* Check if we were chained and increment the last sent sequence. */
- switch (tx_frame->header.PCB.type) {
+ switch (bs_get(PCB.type, tx_frame->header.PCB)) {
case kPcbTypeInfo0:
case kPcbTypeInfo1:
- chained = tx_frame->header.PCB.I.more_data;
- state->card_state->seq.interface = tx_frame->header.PCB.I.send_seq;
+ 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 (rx_frame->header.PCB.type) {
+ switch (bs_get(PCB.type, rx_frame->header.PCB)) {
case kPcbTypeSupervisory:
- if (rx_frame->header.PCB.val != S(RESYNC, RESPONSE) &&
- rx_frame->header.LEN != 1)
+ if (rx_frame->header.PCB != S(RESYNC, RESPONSE) &&
+ rx_frame->header.LEN != 1) {
return R(0, 1, 0);
+ }
break;
case kPcbTypeReceiveReady:
- if (rx_frame->header.LEN != 0)
+ if (rx_frame->header.LEN != 0) {
return R(0, 1, 0);
-#if 0
-/* Handled explicitly. */
- /* R() blocks are always an error if we're not chaining. */
- if (!chained)
- return R(0, 1, 0);
- /* If we are chaining, the R must be the next seq. */
- if (rx_frame->header.PCB.R.next_seq == state->card_state->seq.interface)
- return R(0, 1, 0);
-#endif
+ }
break;
case kPcbTypeInfo0:
case kPcbTypeInfo1:
/* I-blocks must always alternate for each endpoint. */
- if (rx_frame->header.PCB.I.send_seq == state->card_state->seq.card) {
- ALOGV("Got seq %d expected %d", rx_frame->header.PCB.I.send_seq,
+ if ((bs_get(PCB.I.send_seq, rx_frame->header.PCB)) ==
+ state->card_state->seq.card) {
+ ALOGV("Got seq %d expected %d",
+ bs_get(PCB.I.send_seq, rx_frame->header.PCB),
state->card_state->seq.card);
return R(0, 1, 0);
}
/* Update the card's last I-block seq. */
- state->card_state->seq.card = rx_frame->header.PCB.I.send_seq;
+ state->card_state->seq.card = bs_get(PCB.I.send_seq, rx_frame->header.PCB);
default:
break;
};
@@ -291,25 +303,25 @@
/* 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.val, rx_frame->header.PCB.val);
- struct PCB R_err;
+ 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.val = teq1_frame_error_check(state, tx_frame, rx_frame)) != 0) {
+ if ((R_err = teq1_frame_error_check(state, tx_frame, rx_frame)) != 0) {
ALOGV("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.val, 255);
+ txrx = TEQ1_RULE(tx_frame->header.PCB, 255);
state->errors++;
/* Rule 6.4 */
- if (state->errors >= 6) { /* XXX: Review error count lifetime. */
+ 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 */
- next_tx->header.PCB.val = S(RESYNC, REQUEST);
+ next_tx->header.PCB = S(RESYNC, REQUEST);
/* Resync result in a fresh session, so we should just continue here. */
return kRuleResultContinue;
}
@@ -327,25 +339,22 @@
teq1_get_app_data(state, rx_frame);
return kRuleResultComplete;
- /* 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.val = TEQ1_R(!rx_frame->header.PCB.I.send_seq, 0, 0);
+ 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 */
- next_tx->header.PCB.val = I(0, 0);
- next_tx->header.PCB.I.send_seq = rx_frame->header.PCB.R.next_seq;
+ /* 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;
@@ -358,7 +367,7 @@
case TEQ1_RULE(I(1, 1), S(WTX, REQUEST)):
*/
/* Send S(WTX, RESPONSE) with same INF */
- next_tx->header.PCB.val = S(WTX, RESPONSE);
+ 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];
@@ -377,7 +386,7 @@
/* 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.val = S(IFS, RESPONSE);
+ 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];
@@ -393,7 +402,9 @@
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);
- next_tx->header.PCB.val = TEQ1_R(!rx_frame->header.PCB.I.send_seq, 0, 0);
+ /* 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 */
@@ -414,10 +425,11 @@
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.val = R_err.val;
- next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+ 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));
ALOGV("Rule 7.1,7.5,7.6: bad rx - sending error R: %x = %s",
- next_tx->header.PCB.val, teq1_pcb_to_name(next_tx->header.PCB.val));
+ next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB));
return kRuleResultSingleShot; /* So we still can retransmit the original.
*/
@@ -430,10 +442,10 @@
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.val = R(0, 0, 0);
- next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+ next_tx->header.PCB =
+ TEQ1_R(bs_get(PCB.I.send_seq, tx_frame->header.PCB), 0, 0);
ALOGV("Rule 7.1,7.5,7.6: weird rx - sending error R: %x = %s",
- next_tx->header.PCB.val, teq1_pcb_to_name(next_tx->header.PCB.val));
+ next_tx->header.PCB, teq1_pcb_to_name(next_tx->header.PCB));
return kRuleResultSingleShot;
/* Rule 7.2: Retransmit the _same_ R-block. */
@@ -499,7 +511,7 @@
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.val = 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;
@@ -515,9 +527,9 @@
* 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.val != 255) {
+ if (rx_frame->header.PCB != 255) {
ALOGV("Unexpected frame. Marking error and re-evaluating.");
- rx_frame->header.PCB.val = 255;
+ rx_frame->header.PCB = 255;
continue;
}
@@ -531,68 +543,75 @@
* if testing becomes onerous given the loop below.
*/
-API size_t teq1_transceive(struct EseInterface *ese,
- const uint8_t *const tx_buf, size_t tx_len,
- uint8_t *rx_buf, size_t rx_len) {
+API uint32_t teq1_transceive(struct EseInterface *ese,
+ const struct Teq1ProtocolOptions *opts,
+ const uint8_t *const tx_buf, uint32_t tx_len,
+ uint8_t *rx_buf, uint32_t rx_len) {
struct Teq1Frame tx_frame[2];
struct Teq1Frame rx_frame;
struct Teq1Frame *tx = &tx_frame[0];
- int was_reset = 0;
int active = 0;
- int done = 0;
+ bool was_reset = false;
+ bool done = false;
enum RuleResult result = kRuleResultComplete;
- const struct Teq1ProtocolOptions *opts = ese->ops->opts;
struct Teq1CardState *card_state = (struct Teq1CardState *)(&ese->pad[0]);
struct Teq1State init_state =
TEQ1_INIT_STATE(tx_buf, tx_len, rx_buf, rx_len, card_state);
struct Teq1State state =
TEQ1_INIT_STATE(tx_buf, tx_len, rx_buf, rx_len, 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.val = TEQ1_I(!card_state->seq.interface, 0);
+ 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, tx);
+ 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. */
- memset(&rx_frame, 0xff, sizeof(rx_frame));
+ ese_memset(&rx_frame, 0xff, sizeof(rx_frame));
/* -1 indicates a timeout or failure from hardware. */
- if (teq1_receive(ese, opts->bwt * (float)state.wait_mult, &rx_frame) < 0) {
+ 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.val = 255;
+ 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|. */
- memset(&tx_frame[!active].header, 0, sizeof(tx_frame[!active].header));
+ 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 = 1;
+ done = true;
break;
case kRuleResultRetransmit:
/* TODO(wad) Find a clean way to move into teq1_rules(). */
- if (state.retransmits++ < 3)
+ if (state.retransmits++ < 3) {
continue;
- if (tx->header.PCB.val == S(RESYNC, REQUEST)) {
- ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+ }
+ if (tx->header.PCB == S(RESYNC, REQUEST)) {
+ ese_set_error(ese, kTeq1ErrorHardFail);
return 0;
}
/* Fall through */
- tx_frame[!active].header.PCB.val = S(RESYNC, REQUEST);
+ tx_frame[!active].header.PCB = S(RESYNC, REQUEST);
case kRuleResultContinue:
active = !active;
tx = &tx_frame[active];
@@ -600,10 +619,10 @@
state.errors = 0;
continue;
case kRuleResultHardFail:
- ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+ ese_set_error(ese, kTeq1ErrorHardFail);
return 0;
case kRuleResultAbort:
- ese_set_error(ese, TEQ1_ERROR_ABORT);
+ ese_set_error(ese, kTeq1ErrorAbort);
return 0;
case kRuleResultSingleShot:
/*
@@ -615,19 +634,19 @@
continue;
case kRuleResultResetDevice:
if (was_reset || !ese->ops->hw_reset || ese->ops->hw_reset(ese) == -1) {
- ese_set_error(ese, TEQ1_ERROR_DEVICE_RESET);
+ ese_set_error(ese, kTeq1ErrorDeviceReset);
return 0; /* Don't keep resetting -- hard fail. */
}
- was_reset = 1;
+ was_reset = true;
/* Fall through to session reset. */
case kRuleResultResetSession:
/* Roll back state and reset. */
state = init_state;
TEQ1_INIT_CARD_STATE(state.card_state);
/* Reset the active frame. */
- memset(tx, 0, sizeof(*tx));
+ ese_memset(tx, 0, sizeof(*tx));
/* Load initial I-block. */
- tx->header.PCB.val = I(0, 0);
+ tx->header.PCB = I(0, 0);
teq1_fill_info_block(&state, tx);
continue;
}
diff --git a/libese-teq1/teq1_private.h b/libese-teq1/teq1_private.h
index 3151753..6bd3d3c 100644
--- a/libese-teq1/teq1_private.h
+++ b/libese-teq1/teq1_private.h
@@ -25,6 +25,11 @@
#define API __attribute__ ((visibility("default")))
#endif /* API */
+/* Mimic C11 _Static_assert behavior for a C99 world. */
+#ifndef _static_assert
+#define _static_assert(what, why) { while (!(1 / (!!(what)))); }
+#endif
+
/*
* Enable T=1 format to reduce to case integers.
* Ensure there are tests to map TEQ1_X() to the shorthand below.
@@ -66,9 +71,9 @@
struct Teq1CardState *card_state;
struct {
uint8_t *tx_buf;
- size_t tx_len;
+ uint32_t tx_len;
uint8_t *rx_buf;
- size_t rx_len;
+ uint32_t rx_len;
} app_data;
};
@@ -102,8 +107,13 @@
const char *teq1_rule_result_to_name(enum RuleResult result);
const char *teq1_pcb_to_name(uint8_t pcb);
-int teq1_transmit(struct EseInterface *ese, struct Teq1Frame *frame);
-int teq1_receive(struct EseInterface *ese, float timeout, struct Teq1Frame *frame);
+int teq1_transmit(struct EseInterface *ese,
+ const struct Teq1ProtocolOptions *opts,
+ struct Teq1Frame *frame);
+int teq1_receive(struct EseInterface *ese,
+ const struct Teq1ProtocolOptions *opts,
+ float timeout,
+ struct Teq1Frame *frame);
uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame);
void teq1_get_app_data(struct Teq1State *state, struct Teq1Frame *frame);
uint8_t teq1_frame_error_check(struct Teq1State *state,
@@ -114,6 +124,8 @@
struct Teq1Frame *rx_frame,
struct Teq1Frame *next_tx);
+#define teq1_dump_transmit(_B, _L) teq1_dump_buf("TX", (_B), (_L))
+#define teq1_dump_receive(_B, _L) teq1_dump_buf("RX", (_B), (_L))
#ifdef __cplusplus
} /* extern "C" */
diff --git a/libese-teq1/tests/Android.bp b/libese-teq1/tests/Android.bp
index 9cb2fb4..a54a0af 100644
--- a/libese-teq1/tests/Android.bp
+++ b/libese-teq1/tests/Android.bp
@@ -14,28 +14,13 @@
// limitations under the License.
//
-cc_defaults {
- name: "libese_teq1_tests_default",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
-
-test_libraries = [
- "libese",
- "libese-teq1-private",
- "liblog",
-]
-
cc_test {
name: "ese_teq1_unittests",
- defaults: ["libese_teq1_tests_default"],
srcs: ["teq1_unittests.cpp"],
host_supported: true,
- shared_libs: test_libraries,
+ shared_libs: [
+ "libese",
+ "libese-teq1-private",
+ "liblog",
+ ],
}
diff --git a/libese-teq1/tests/teq1_unittests.cpp b/libese-teq1/tests/teq1_unittests.cpp
index 77118fa..aef5e0a 100644
--- a/libese-teq1/tests/teq1_unittests.cpp
+++ b/libese-teq1/tests/teq1_unittests.cpp
@@ -36,7 +36,6 @@
// - Unittests of each function
// - teq1_rules matches Annex A of ISO 7816-3
-
// Tests teq1_frame_error_check to avoid testing every combo that
// ends in 255 in the rule engine.
class Teq1FrameErrorCheck : public virtual Test {
@@ -60,16 +59,16 @@
/* The PCBs above are all valid for a sent unchained I block with advancing
* sequence #s.
*/
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
state_.card_state = &card_state_;
state_.card_state->seq.card = 1;
while (*pcb != 255) {
- rx_frame_.header.PCB.val = *pcb;
+ rx_frame_.header.PCB = *pcb;
rx_frame_.header.LEN = 2;
rx_frame_.INF[0] = 'A';
rx_frame_.INF[1] = 'B';
rx_frame_.INF[2] = teq1_compute_LRC(&rx_frame_);
- EXPECT_EQ(0, teq1_frame_error_check(&state_, &tx_frame_, &rx_frame_)) << teq1_pcb_to_name(rx_frame_.header.PCB.val);
+ EXPECT_EQ(0, teq1_frame_error_check(&state_, &tx_frame_, &rx_frame_)) << teq1_pcb_to_name(rx_frame_.header.PCB);
rx_frame_.INF[2] = teq1_compute_LRC(&rx_frame_) - 1;
// Reset so we check the LRC error instead of a wrong seq.
state_.card_state->seq.card = !state_.card_state->seq.card;
@@ -94,8 +93,9 @@
tx_data_(INF_LEN, 'A'),
rx_data_(INF_LEN, 'B'),
card_state_({ .seq = { .card = 1, .interface = 1, }, }),
- state_(TEQ1_INIT_STATE(tx_data_.data(), tx_data_.size(),
- rx_data_.data(), rx_data_.size(), &card_state_)) {
+ state_(TEQ1_INIT_STATE(tx_data_.data(), static_cast<uint32_t>(tx_data_.size()),
+ rx_data_.data(), static_cast<uint32_t>(rx_data_.size()),
+ &card_state_)) {
memset(&tx_frame_, 0, sizeof(struct Teq1Frame));
memset(&tx_next_, 0, sizeof(struct Teq1Frame));
memset(&rx_frame_, 0, sizeof(struct Teq1Frame));
@@ -122,12 +122,12 @@
class Teq1CompleteTest : public Teq1ErrorFreeTest {
public:
virtual void SetUp() {
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
teq1_fill_info_block(&state_, &tx_frame_);
// Check that the tx_data was fully consumed.
EXPECT_EQ(0UL, state_.app_data.tx_len);
- rx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ rx_frame_.header.PCB = TEQ1_I(0, 0);
rx_frame_.header.LEN = INF_LEN;
ASSERT_EQ(static_cast<unsigned long>(INF_LEN), tx_data_.size()); // Catch fixture changes.
// Supply TX data and make sure it overwrites RX data on consumption.
@@ -137,15 +137,15 @@
virtual void RunRules() {
teq1_trace_header();
- teq1_trace_transmit(tx_frame_.header.PCB.val, tx_frame_.header.LEN);
- teq1_trace_receive(rx_frame_.header.PCB.val, rx_frame_.header.LEN);
+ teq1_trace_transmit(tx_frame_.header.PCB, tx_frame_.header.LEN);
+ teq1_trace_receive(rx_frame_.header.PCB, rx_frame_.header.LEN);
enum RuleResult result = teq1_rules(&state_, &tx_frame_, &rx_frame_, &tx_next_);
EXPECT_EQ(0, state_.errors);
EXPECT_EQ(NULL, state_.last_error_message)
<< "Last error: " << state_.last_error_message;
- EXPECT_EQ(0, tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(0, tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
EXPECT_EQ(kRuleResultComplete, result)
<< "Actual result name: " << teq1_rule_result_to_name(result);
}
@@ -171,8 +171,8 @@
};
TEST_F(Teq1CompleteTest, I10_I10_data) {
- tx_frame_.header.PCB.val = TEQ1_I(1, 0);
- rx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(1, 0);
+ rx_frame_.header.PCB = TEQ1_I(0, 0);
rx_frame_.INF[INF_LEN] = teq1_compute_LRC(&rx_frame_);
RunRules();
// Ensure that the rx_frame data was copied out to rx_data.
@@ -184,28 +184,28 @@
// Note, IFS is not tested as it is not supported on current hardware.
TEST_F(Teq1ErrorFreeTest, I00_WTX0_WTX1_data) {
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
teq1_fill_info_block(&state_, &tx_frame_);
// Check that the tx_data was fully consumed.
EXPECT_EQ(0UL, state_.app_data.tx_len);
- rx_frame_.header.PCB.val = TEQ1_S_WTX(0);
+ rx_frame_.header.PCB = TEQ1_S_WTX(0);
rx_frame_.header.LEN = 1;
rx_frame_.INF[0] = 2; /* Wait x 2 */
rx_frame_.INF[1] = teq1_compute_LRC(&rx_frame_);
teq1_trace_header();
- teq1_trace_transmit(tx_frame_.header.PCB.val, tx_frame_.header.LEN);
- teq1_trace_receive(rx_frame_.header.PCB.val, rx_frame_.header.LEN);
+ teq1_trace_transmit(tx_frame_.header.PCB, tx_frame_.header.LEN);
+ teq1_trace_receive(rx_frame_.header.PCB, rx_frame_.header.LEN);
enum RuleResult result = teq1_rules(&state_, &tx_frame_, &rx_frame_, &tx_next_);
- teq1_trace_transmit(tx_next_.header.PCB.val, tx_next_.header.LEN);
+ teq1_trace_transmit(tx_next_.header.PCB, tx_next_.header.LEN);
EXPECT_EQ(0, state_.errors);
EXPECT_EQ(NULL, state_.last_error_message)
<< "Last error: " << state_.last_error_message;
- EXPECT_EQ(TEQ1_S_WTX(1), tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(TEQ1_S_WTX(1), tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
EXPECT_EQ(state_.wait_mult, 2);
EXPECT_EQ(state_.wait_mult, rx_frame_.INF[0]);
// Ensure the next call will use the original TX frame.
@@ -221,9 +221,9 @@
state_.app_data.tx_buf = tx_data_.data();
teq1_fill_info_block(&state_, &tx_frame_);
// Ensure More bit was set.
- EXPECT_EQ(1, tx_frame_.header.PCB.I.more_data);
+ EXPECT_EQ(1, bs_get(PCB.I.more_data, tx_frame_.header.PCB));
// Check that the tx_data was fully consumed.
- EXPECT_EQ(static_cast<size_t>(oversized_data_len_ - INF_LEN),
+ EXPECT_EQ(static_cast<uint32_t>(oversized_data_len_ - INF_LEN),
state_.app_data.tx_len);
// No one is checking the TX LRC since there is no card present.
@@ -231,11 +231,11 @@
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
teq1_trace_header();
- teq1_trace_transmit(tx_frame_.header.PCB.val, tx_frame_.header.LEN);
- teq1_trace_receive(rx_frame_.header.PCB.val, rx_frame_.header.LEN);
+ teq1_trace_transmit(tx_frame_.header.PCB, tx_frame_.header.LEN);
+ teq1_trace_receive(rx_frame_.header.PCB, rx_frame_.header.LEN);
enum RuleResult result = teq1_rules(&state_, &tx_frame_, &rx_frame_, &tx_next_);
- teq1_trace_transmit(tx_next_.header.PCB.val, tx_next_.header.LEN);
+ teq1_trace_transmit(tx_next_.header.PCB, tx_next_.header.LEN);
EXPECT_EQ(0, state_.errors);
EXPECT_EQ(NULL, state_.last_error_message)
<< "Last error: " << state_.last_error_message;
@@ -243,7 +243,7 @@
<< "Actual result name: " << teq1_rule_result_to_name(result);
// Check that the tx_buf was drained already for the next frame.
// ...
- EXPECT_EQ(static_cast<size_t>(oversized_data_len_ - (2 * INF_LEN)),
+ EXPECT_EQ(static_cast<uint32_t>(oversized_data_len_ - (2 * INF_LEN)),
state_.app_data.tx_len);
// Belt and suspenders: make sure no RX buf was used.
EXPECT_EQ(rx_data_.size(), state_.app_data.rx_len);
@@ -253,29 +253,29 @@
TEST_F(Teq1ErrorFreeChainingTest, I01_R1_I11_chaining) {
oversized_data_len_ = INF_LEN * 3;
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
- rx_frame_.header.PCB.val = TEQ1_R(1, 0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
+ rx_frame_.header.PCB = TEQ1_R(1, 0, 0);
RunRules();
- EXPECT_EQ(TEQ1_I(1, 1), tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(TEQ1_I(1, 1), tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
};
TEST_F(Teq1ErrorFreeChainingTest, I11_R0_I01_chaining) {
oversized_data_len_ = INF_LEN * 3;
- tx_frame_.header.PCB.val = TEQ1_I(1, 0);
- rx_frame_.header.PCB.val = TEQ1_R(0, 0, 0);
+ tx_frame_.header.PCB = TEQ1_I(1, 0);
+ rx_frame_.header.PCB = TEQ1_R(0, 0, 0);
RunRules();
- EXPECT_EQ(TEQ1_I(0, 1), tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(TEQ1_I(0, 1), tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
};
TEST_F(Teq1ErrorFreeChainingTest, I11_R0_I00_chaining) {
oversized_data_len_ = INF_LEN * 2; // Exactly 2 frames worth.
- tx_frame_.header.PCB.val = TEQ1_I(1, 0);
- rx_frame_.header.PCB.val = TEQ1_R(0, 0, 0);
+ tx_frame_.header.PCB = TEQ1_I(1, 0);
+ rx_frame_.header.PCB = TEQ1_R(0, 0, 0);
RunRules();
- EXPECT_EQ(TEQ1_I(0, 0), tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(TEQ1_I(0, 0), tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
};
//
@@ -290,19 +290,19 @@
state_.app_data.rx_len = 0;
state_.app_data.tx_len = 0;
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
teq1_fill_info_block(&state_, &tx_frame_);
// No one is checking the TX LRC since there is no card present.
// Assume the card may not even set the error bit.
rx_frame_.header.LEN = 0;
- rx_frame_.header.PCB.val = TEQ1_R(0, 0, 0);
+ rx_frame_.header.PCB = TEQ1_R(0, 0, 0);
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
}
virtual void TearDown() {
teq1_trace_header();
- teq1_trace_transmit(tx_frame_.header.PCB.val, tx_frame_.header.LEN);
- teq1_trace_receive(rx_frame_.header.PCB.val, rx_frame_.header.LEN);
+ teq1_trace_transmit(tx_frame_.header.PCB, tx_frame_.header.LEN);
+ teq1_trace_receive(rx_frame_.header.PCB, rx_frame_.header.LEN);
enum RuleResult result = teq1_rules(&state_, &tx_frame_, &rx_frame_, &tx_next_);
// Not counted as an error as it was on the card-side.
@@ -315,22 +315,22 @@
};
TEST_F(Teq1Retransmit, I00_R000_I00) {
- rx_frame_.header.PCB.val = TEQ1_R(0, 0, 0);
+ rx_frame_.header.PCB = TEQ1_R(0, 0, 0);
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
};
TEST_F(Teq1Retransmit, I00_R001_I00) {
- rx_frame_.header.PCB.val = TEQ1_R(0, 0, 1);
+ rx_frame_.header.PCB = TEQ1_R(0, 0, 1);
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
};
TEST_F(Teq1Retransmit, I00_R010_I00) {
- rx_frame_.header.PCB.val = TEQ1_R(0, 1, 0);
+ rx_frame_.header.PCB = TEQ1_R(0, 1, 0);
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
};
TEST_F(Teq1Retransmit, I00_R011_I00) {
- rx_frame_.header.PCB.val = TEQ1_R(0, 1, 1);
+ rx_frame_.header.PCB = TEQ1_R(0, 1, 1);
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
}
@@ -339,25 +339,25 @@
state_.app_data.rx_len = 0;
state_.app_data.tx_len = 0;
- tx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ tx_frame_.header.PCB = TEQ1_I(0, 0);
teq1_fill_info_block(&state_, &tx_frame_);
// No one is checking the TX LRC since there is no card present.
- rx_frame_.header.PCB.val = TEQ1_I(0, 0);
+ rx_frame_.header.PCB = TEQ1_I(0, 0);
rx_frame_.header.LEN = 0;
rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_) - 1;
teq1_trace_header();
- teq1_trace_transmit(tx_frame_.header.PCB.val, tx_frame_.header.LEN);
- teq1_trace_receive(rx_frame_.header.PCB.val, rx_frame_.header.LEN);
+ teq1_trace_transmit(tx_frame_.header.PCB, tx_frame_.header.LEN);
+ teq1_trace_receive(rx_frame_.header.PCB, rx_frame_.header.LEN);
enum RuleResult result = teq1_rules(&state_, &tx_frame_, &rx_frame_, &tx_next_);
EXPECT_EQ(1, state_.errors);
const char *kNull = NULL;
EXPECT_NE(kNull, state_.last_error_message);
EXPECT_STREQ("Invalid frame received", state_.last_error_message);
- EXPECT_EQ(TEQ1_R(0, 0, 1), tx_next_.header.PCB.val)
- << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+ EXPECT_EQ(TEQ1_R(0, 0, 1), tx_next_.header.PCB)
+ << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB);
EXPECT_EQ(kRuleResultSingleShot, result)
<< "Actual result name: " << teq1_rule_result_to_name(result);
};
diff --git a/libese/Android.bp b/libese/Android.bp
index 1511b3a..411001c 100644
--- a/libese/Android.bp
+++ b/libese/Android.bp
@@ -23,16 +23,18 @@
],
local_include_dirs: [
- "include",
+ "include",
],
// Ensure that only explicitly exported symbols are visible.
- cflags: ["-fvisibility=internal"],
+ cflags: ["-fvisibility=internal", "-std=c99"],
+ // This doesn't work yet, but is good documentation for when
+ // debugging is needed.
debug: {
cflags: ["-DLOG_NDEBUG=0"],
- },
+ },
- shared_libs: ["liblog"],
+ shared_libs: ["libese-sysdeps", "liblog"],
export_include_dirs: ["include"],
}
diff --git a/libese/ese.c b/libese/ese.c
index 1e49e22..2a0048f 100644
--- a/libese/ese.c
+++ b/libese/ese.c
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <ese/ese.h>
-#include <ese/log.h>
+#include "include/ese/ese.h"
+#include "include/ese/log.h"
#include "ese_private.h"
@@ -27,84 +27,91 @@
};
#define ESE_MESSAGES(x) (sizeof(x) / sizeof((x)[0]))
-/* TODO(wad): Make the default visibility on this one default default? */
-API const char *ese_name(struct EseInterface *ese) {
- if (!ese)
+API const char *ese_name(const struct EseInterface *ese) {
+ if (!ese) {
return kNullEse;
- if (ese->ops->name)
+ }
+ if (ese->ops->name) {
return ese->ops->name;
+ }
return kUnknownHw;
}
API int ese_open(struct EseInterface *ese, void *hw_opts) {
- if (!ese)
+ if (!ese) {
return -1;
+ }
ALOGV("opening interface '%s'", ese_name(ese));
- if (ese->ops->open)
+ if (ese->ops->open) {
return ese->ops->open(ese, hw_opts);
+ }
return 0;
}
-API const char *ese_error_message(struct EseInterface *ese) {
+API const char *ese_error_message(const struct EseInterface *ese) {
return ese->error.message;
}
-API int ese_error_code(struct EseInterface *ese) { return ese->error.code; }
+API int ese_error_code(const struct EseInterface *ese) {
+ return ese->error.code;
+}
-API int ese_error(struct EseInterface *ese) { return ese->error.is_err; }
+API bool ese_error(const struct EseInterface *ese) { return ese->error.is_err; }
API void ese_set_error(struct EseInterface *ese, int code) {
- if (!ese)
+ if (!ese) {
return;
+ }
/* Negative values are reserved for API wide messages. */
ese->error.code = code;
- ese->error.is_err = 1;
+ ese->error.is_err = true;
if (code < 0) {
code = -(code + 1); /* Start at 0. */
- if ((size_t)(code) >= ESE_MESSAGES(kEseErrorMessages)) {
+ if ((uint32_t)(code) >= ESE_MESSAGES(kEseErrorMessages)) {
LOG_ALWAYS_FATAL("Unknown global error code passed to ese_set_error(%d)",
code);
}
ese->error.message = kEseErrorMessages[code];
return;
}
- if ((size_t)(code) >= ese->errors_count) {
+ if ((uint32_t)(code) >= ese->ops->errors_count) {
LOG_ALWAYS_FATAL("Unknown hw error code passed to ese_set_error(%d)", code);
}
- ese->error.message = ese->errors[code];
+ ese->error.message = ese->ops->errors[code];
}
/* Blocking. */
-API int ese_transceive(struct EseInterface *ese, uint8_t *const tx_buf,
- size_t tx_len, uint8_t *rx_buf, size_t rx_max) {
- size_t recvd = 0;
- if (!ese)
+API int ese_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
+ uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_max) {
+ uint32_t recvd = 0;
+ if (!ese) {
return -1;
- while (1) {
- if (ese->ops->transceive) {
- recvd = ese->ops->transceive(ese, tx_buf, tx_len, rx_buf, rx_max);
- break;
- }
- if (ese->ops->hw_transmit && ese->ops->hw_receive) {
- ese->ops->hw_transmit(ese, tx_buf, tx_len, 1);
- if (ese->error.is_err)
- break;
- recvd = ese->ops->hw_receive(ese, rx_buf, rx_max, 1);
- break;
- }
- ese_set_error(ese, -1);
- break;
}
- if (ese->error.is_err)
- return -1;
- return recvd;
+
+ if (ese->ops->transceive) {
+ recvd = ese->ops->transceive(ese, tx_buf, tx_len, rx_buf, rx_max);
+ return ese_error(ese) ? -1 : recvd;
+ }
+
+ if (ese->ops->hw_transmit && ese->ops->hw_receive) {
+ ese->ops->hw_transmit(ese, tx_buf, tx_len, 1);
+ if (!ese_error(ese)) {
+ recvd = ese->ops->hw_receive(ese, rx_buf, rx_max, 1);
+ }
+ return ese_error(ese) ? -1 : recvd;
+ }
+
+ ese_set_error(ese, kEseGlobalErrorNoTransceive);
+ return -1;
}
-API int ese_close(struct EseInterface *ese) {
- if (!ese)
- return -1;
+API void ese_close(struct EseInterface *ese) {
+ if (!ese) {
+ return;
+ }
ALOGV("closing interface '%s'", ese_name(ese));
- if (!ese->ops->close)
- return 0;
- return ese->ops->close(ese);
+ if (!ese->ops->close) {
+ return;
+ }
+ ese->ops->close(ese);
}
diff --git a/libese/include/ese/bit_spec.h b/libese/include/ese/bit_spec.h
new file mode 100644
index 0000000..4e6c848
--- /dev/null
+++ b/libese/include/ese/bit_spec.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+#ifndef ESE_BIT_SPEC_H__
+#define ESE_BIT_SPEC_H__ 1
+
+struct bit_spec {
+ uint8_t value;
+ uint8_t shift;
+};
+
+static inline uint8_t bs_get(const struct bit_spec b, uint8_t var) {
+ return (var & (b.value << b.shift)) >> b.shift;
+}
+
+static inline uint8_t bs_mask(const struct bit_spec b, uint8_t value) {
+ return (value & b.value) << b.shift;
+}
+
+static inline uint8_t bs_clear(const struct bit_spec b) {
+ return (((uint8_t)~0) ^ bs_mask(b, b.value));
+}
+
+static inline uint8_t bs_set(uint8_t var, const struct bit_spec b, uint8_t value) {
+ return ((var) & bs_clear(b)) | bs_mask(b, value);
+}
+
+static inline void bs_assign(uint8_t *dst, const struct bit_spec b, uint8_t value) {
+ *dst = bs_set(*dst, b, value);
+}
+
+
+#endif /* ESE_BIT_SPEC_H__ */
+
+
diff --git a/libese/include/ese/ese.h b/libese/include/ese/ese.h
index 39827ed..f12ed03 100644
--- a/libese/include/ese/ese.h
+++ b/libese/include/ese/ese.h
@@ -17,10 +17,8 @@
#ifndef ESE_H_
#define ESE_H_ 1
-#include <stdint.h>
-#include <stdint.h>
-
#include "ese_hw_api.h"
+#include "../../../libese-sysdeps/include/ese/sysdeps.h"
#ifdef __cplusplus
extern "C" {
@@ -68,17 +66,14 @@
#define ESE_INITIALIZER __ESE_INITIALIZER
#define ESE_INCLUDE_HW __ESE_INCLUDE_HW
-const char *ese_name(struct EseInterface *ese);
+const char *ese_name(const struct EseInterface *ese);
int ese_open(struct EseInterface *ese, void *hw_opts);
-int ese_close(struct EseInterface *ese);
-int ese_transceive(struct EseInterface *ese, uint8_t *const tx_buf, size_t tx_len, uint8_t *rx_buf, size_t rx_max);
+void ese_close(struct EseInterface *ese);
+int ese_transceive(struct EseInterface *ese, const uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_max);
-int ese_error(struct EseInterface *ese);
-const char *ese_error_message(struct EseInterface *ese);
-int ese_error_code(struct EseInterface *ese);
-
-/* Called by the implementations. */
-void ese_set_error(struct EseInterface *ese, int code);
+bool ese_error(const struct EseInterface *ese);
+const char *ese_error_message(const struct EseInterface *ese);
+int ese_error_code(const struct EseInterface *ese);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/libese/include/ese/ese_hw_api.h b/libese/include/ese/ese_hw_api.h
index dd76391..1d23768 100644
--- a/libese/include/ese/ese_hw_api.h
+++ b/libese/include/ese/ese_hw_api.h
@@ -17,8 +17,7 @@
#ifndef ESE_HW_API_H_
#define ESE_HW_API_H_ 1
-#include <stddef.h>
-#include <stdint.h>
+#include "../../../libese-sysdeps/include/ese/sysdeps.h"
#ifdef __cplusplus
extern "C" {
@@ -28,49 +27,109 @@
* to make use of.
*/
#define __ESE_INCLUDE_HW(name) \
- extern const struct EseOperations * name## _ops; \
- extern const char ** name##_errors; \
- extern const size_t name##_errors_count
+ extern const struct EseOperations * name## _ops
struct EseInterface;
-/* !! Note !!
- * Receive and transmit operations on SPI buses should ensure the CS
- * does not change between subsequent recieve (or transmit) calls unless
- * the |complete| argument is 1.
+
+/* ese_hw_receive_op_t: receives a buffer from the hardware.
+ * Args:
+ * - struct EseInterface *: session handle.
+ * - uint8_t *: pointer to the buffer to receive data into.
+ * - uint32_t: maximum length of the data to receive.
+ * - int: 1 or 0 indicating if it is a complete transaction. This allows the underlying
+ * implementation to batch reads if needed by the underlying wire protocol or if
+ * the hardware needs to be signalled explicitly.
*
- * In practice, this should not require additional state tracking as entry
- * to each function can simply assert the CS state (even if unchanged) and
- * then check whether to unassert based on |complete|.
- *
- * Other communications backends may have different needs which may be solved
- * separately by minimally processing protocol headers.
- *
- * The other option is having a transactional view where we have an explicit
- * begin/end or claim/release.
+ * Returns:
+ * - uint32_t: bytes received.
*/
-typedef size_t (ese_hw_receive_op_t)(struct EseInterface *, uint8_t *, size_t, int);
-typedef size_t (ese_hw_transmit_op_t)(struct EseInterface *, const uint8_t *, size_t, int);
+typedef uint32_t (ese_hw_receive_op_t)(struct EseInterface *, uint8_t *, uint32_t, int);
+/* ese_hw_transmit_op_t: transmits a buffer over the hardware.
+ * Args:
+ * - struct EseInterface *: session handle.
+ * - uint8_t *: pointer to the buffer to transmit.
+ * - uint32_t: length of the data to transmit.
+ * - int: 1 or 0 indicating if it is a complete transaction.
+ *
+ * Returns:
+ * - uint32_t: bytes transmitted.
+ */
+typedef uint32_t (ese_hw_transmit_op_t)(struct EseInterface *, const uint8_t *, uint32_t, int);
+/* ese_hw_reset_op_t: resets the hardware in case of communication desynchronization.
+ * Args:
+ * - struct EseInterface *: session handle.
+ *
+ * Returns:
+ * - int: -1 on error, 0 on success.
+ */
typedef int (ese_hw_reset_op_t)(struct EseInterface *);
-/* Implements wire protocol transceiving and will likely also then require locking. */
-typedef size_t (ese_transceive_op_t)(struct EseInterface *, const uint8_t *, size_t, uint8_t *, size_t);
-/* Returns 0 on timeout, 1 on byte seen, -1 on error. */
+/* ese_transceive_op_t: fully contained transmission and receive operation.
+ *
+ * Must provide an implementation of the wire protocol necessary to transmit
+ * and receive an application payload to and from the eSE. Normally, this
+ * implementation is built on the hw_{receive,transmit} operations also
+ * provided and often requires the poll operation below.
+ *
+ * Args:
+ * - struct EseInterface *: session handle.
+ * - const uint8_t *: pointer to the buffer to transmit.
+ * - uint32_t: length of the data to transmit.
+ * - uint8_t *: pointer to the buffer to receive into.
+ * - uint32_t: maximum length of the data to receive.
+ *
+ * Returns:
+ * - uint32_t: bytes received.
+ */
+typedef uint32_t (ese_transceive_op_t)(struct EseInterface *, const uint8_t *, uint32_t, uint8_t *, uint32_t);
+/* ese_poll_op_t: waits for the hardware to be ready to send data.
+ *
+ * Args:
+ * - struct EseInterface *: session handle.
+ * - uint8_t: byte to wait for. E.g., a start of frame byte.
+ * - float: time in seconds to wait.
+ * - int: whether to complete a transaction when polling іs complete.
+ *
+ * Returns:
+ * - int: On error or timeout, -1.
+ * On success, 1 or 0 depending on if the found byte was consumed.
+ */
typedef int (ese_poll_op_t)(struct EseInterface *, uint8_t, float, int);
+/* ese_hw_open_op_t: prepares the hardware for use.
+ *
+ * This function should prepare the hardware for use and attach
+ * any implementation specific state to the EseInterface handle such
+ * that it is accessible in the other calls.
+ *
+ * Args:
+ * - struct EseInterface *: freshly initialized session handle.
+ * - void *: implementation specific pointer from the libese client.
+ *
+ * Returns:
+ * - int: < 0 on error, 0 on success.
+ */
typedef int (ese_open_op_t)(struct EseInterface *, void *);
-typedef int (ese_close_op_t)(struct EseInterface *);
+/* ese_hw_close_op_t: releases the hardware in use.
+ *
+ * This function should free any dynamic memory and release
+ * the claimed hardware.
+ *
+ * Args:
+ * - struct EseInterface *: freshly initialized session handle.
+ *
+ * Returns:
+ * - Nothing.
+ */
+typedef void (ese_close_op_t)(struct EseInterface *);
#define __ESE_INITIALIZER(TYPE) \
{ \
.ops = TYPE## _ops, \
- .errors = TYPE## _errors, \
- .errors_count = TYPE## _errors_count, \
.pad = { 0 }, \
}
#define __ese_init(_ptr, TYPE) {\
_ptr->ops = TYPE## _ops; \
- _ptr->errors = TYPE## _errors; \
- _ptr->errors_count = TYPE## _errors_count; \
_ptr->pad[0] = 0; \
}
@@ -99,6 +158,10 @@
/* Operational options */
const void *const opts;
+
+ /* Operation error messages. */
+ const char **errors;
+ const uint32_t errors_count;
};
/* Maximum private stack storage on the interface instance. */
@@ -106,24 +169,32 @@
struct EseInterface {
const struct EseOperations * ops;
struct {
- int is_err;
+ bool is_err;
int code;
const char *message;
} error;
- const char **errors;
- size_t errors_count;
/* Reserved to avoid heap allocation requirement. */
uint8_t pad[ESE_INTERFACE_STATE_PAD];
};
+/*
+ * Provided by libese to manage exposing usable errors up the stack to the
+ * libese user.
+ */
+void ese_set_error(struct EseInterface *ese, int code);
-#define ESE_DEFINE_HW_ERRORS(name, ary) \
- const char ** name##_errors = (ary); \
- const size_t name##_errors_count = (sizeof(ary) / sizeof((ary)[0]))
+/*
+ * Global error enums.
+ */
+enum EseGlobalError {
+ kEseGlobalErrorNoTransceive = -1,
+ kEseGlobalErrorPollTimedOut = -2,
+};
#define ESE_DEFINE_HW_OPS(name, obj) \
const struct EseOperations * name##_ops = &obj
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/libese/include/ese/log.h b/libese/include/ese/log.h
index 150c562..8eebe23 100644
--- a/libese/include/ese/log.h
+++ b/libese/include/ese/log.h
@@ -19,6 +19,10 @@
#ifndef ESE_LOG_H_
#define ESE_LOG_H_ 1
+/* Uncomment when doing bring up or other messy debugging.
+ * #define LOG_NDEBUG 0
+ */
+
#if defined(ESE_LOG_NONE) || defined(ESE_LOG_STDIO)
# define ESE_LOG_ANDROID 0
#endif
diff --git a/libese/include/ese/sysdeps.h b/libese/include/ese/sysdeps.h
new file mode 100644
index 0000000..67f91f1
--- /dev/null
+++ b/libese/include/ese/sysdeps.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+#ifndef ESE_PLATFORM_SYSDEPS_H__
+#define ESE_PLATFORM_SYSDEPS_H__ 1
+
+#include <stdint.h> /* uint*_t */
+
+extern void *ese_memcpy(void *__dest, const void *__src, uint64_t __n);
+extern void *ese_memset(void *__s, int __c, uint64_t __n);
+
+#endif /* ESE_PLATFORM_SYSDEPS_H__ */
diff --git a/libese/tests/Android.bp b/libese/tests/Android.bp
index 29abee0..1239eb2 100644
--- a/libese/tests/Android.bp
+++ b/libese/tests/Android.bp
@@ -14,29 +14,13 @@
// limitations under the License.
//
-cc_defaults {
- name: "libese_tests_default",
-
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-}
-
-test_libraries = [
- "libese",
- "libese-hw-fake",
- "liblog",
-]
-
cc_test {
name: "ese_unittests",
- defaults: ["libese_tests_default"],
- srcs: ["ese_unittests.cpp"],
+ srcs: ["ese_unittests.cpp", "bitspec_unittests.cpp"],
host_supported: true,
- shared_libs: test_libraries,
+ shared_libs: [
+ "libese",
+ "libese-hw-fake",
+ "liblog",
+ ],
}
diff --git a/libese/tests/bitspec_unittests.cpp b/libese/tests/bitspec_unittests.cpp
new file mode 100644
index 0000000..1f2fc2a
--- /dev/null
+++ b/libese/tests/bitspec_unittests.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 <stdint.h>
+
+#include <ese/bit_spec.h>
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+
+struct TestSpec {
+ struct {
+ struct bit_spec bit[8];
+ } single;
+ struct {
+ struct bit_spec high;
+ struct bit_spec mid;
+ struct bit_spec low;
+ } adjacent;
+ struct {
+ struct bit_spec all8;
+ struct bit_spec upper6;
+ struct bit_spec lower6;
+ } overlap;
+};
+
+const struct TestSpec kTestSpec = {
+ .single = {
+ .bit = {
+ { .value = 1, .shift = 0 },
+ { .value = 1, .shift = 1 },
+ { .value = 1, .shift = 2 },
+ { .value = 1, .shift = 3 },
+ { .value = 1, .shift = 4 },
+ { .value = 1, .shift = 5 },
+ { .value = 1, .shift = 6 },
+ { .value = 1, .shift = 7 },
+ },
+ },
+ .adjacent = {
+ .high = { .value = 0x3, .shift = 6 },
+ .mid = { .value = 0xf, .shift = 2 },
+ .low = { .value = 0x3, .shift = 0 },
+ },
+ .overlap = {
+ .all8 = { .value = 0xff, .shift = 0 },
+ .upper6 = { .value = 0x3f, .shift = 2 },
+ .lower6 = { .value = 0x3f, .shift = 0 },
+ },
+};
+
+class BitSpecTest : public virtual Test {
+ public:
+ BitSpecTest() {
+ }
+ virtual ~BitSpecTest() { }
+ virtual void SetUp() { }
+ virtual void TearDown() { }
+};
+
+TEST_F(BitSpecTest, single_bits_assign_accrue) {
+ uint8_t byte = 0;
+ uint8_t expected_byte = 0;
+ // Accrue bits.
+ for (int bit = 0; bit < 8; ++bit) {
+ expected_byte |= (1 << bit);
+ bs_assign(&byte, kTestSpec.single.bit[bit], 1);
+ EXPECT_EQ(expected_byte, byte);
+ EXPECT_EQ(1, bs_get(kTestSpec.single.bit[bit], expected_byte));
+ }
+}
+
+TEST_F(BitSpecTest, single_bits_assign1_individual) {
+ // One bit at a time.
+ for (int bit = 0; bit < 8; ++bit) {
+ uint8_t expected_byte = (1 << bit);
+ uint8_t byte = bs_set(0, kTestSpec.single.bit[bit], 1);
+ EXPECT_EQ(expected_byte, byte);
+ EXPECT_EQ(1, bs_get(kTestSpec.single.bit[bit], expected_byte));
+ }
+}
+
+TEST_F(BitSpecTest, single_bits_assign0_individual) {
+ // One bit at a time.
+ for (int bit = 0; bit < 8; ++bit) {
+ uint8_t expected_byte = 0xff ^ (1 << bit);
+ uint8_t byte = bs_set(0xff, kTestSpec.single.bit[bit], 0);
+ EXPECT_EQ(expected_byte, byte);
+ EXPECT_EQ(0, bs_get(kTestSpec.single.bit[bit], expected_byte));
+ }
+}
+
+TEST_F(BitSpecTest, single_bits_clear_individual) {
+ // One bit at a time.
+ for (int bit = 0; bit < 8; ++bit) {
+ uint8_t byte = 0xff;
+ uint8_t expected_byte = 0xff ^ (1 << bit);
+ byte &= bs_clear(kTestSpec.single.bit[bit]);
+ EXPECT_EQ(expected_byte, byte);
+ EXPECT_EQ(0, bs_get(kTestSpec.single.bit[bit], expected_byte));
+ }
+}
+
+TEST_F(BitSpecTest, adjacent_bit_assign) {
+ uint8_t byte = 0;
+ EXPECT_EQ(0, bs_get(kTestSpec.adjacent.high, byte));
+ EXPECT_EQ(0, bs_get(kTestSpec.adjacent.mid, byte));
+ EXPECT_EQ(0, bs_get(kTestSpec.adjacent.low, byte));
+ byte = 0xff;
+ EXPECT_EQ(0x3, bs_get(kTestSpec.adjacent.high, byte));
+ EXPECT_EQ(0xf, bs_get(kTestSpec.adjacent.mid, byte));
+ EXPECT_EQ(0x3, bs_get(kTestSpec.adjacent.low, byte));
+ for (int i = 0; i < 0xf; ++i) {
+ bs_assign(&byte, kTestSpec.adjacent.mid, i);
+ EXPECT_EQ(0x3, bs_get(kTestSpec.adjacent.high, byte));
+ EXPECT_EQ(i, bs_get(kTestSpec.adjacent.mid, byte));
+ EXPECT_EQ(0x3, bs_get(kTestSpec.adjacent.low, byte));
+ }
+ byte = 0xff;
+ for (int i = 0; i < 0x3; ++i) {
+ bs_assign(&byte, kTestSpec.adjacent.low, i);
+ bs_assign(&byte, kTestSpec.adjacent.high, i);
+ EXPECT_EQ(i, bs_get(kTestSpec.adjacent.high, byte));
+ EXPECT_EQ(0xf, bs_get(kTestSpec.adjacent.mid, byte));
+ EXPECT_EQ(i, bs_get(kTestSpec.adjacent.low, byte));
+ }
+}
+
+TEST_F(BitSpecTest, overlap_bit_assign) {
+ uint8_t byte = 0;
+ EXPECT_EQ(0, bs_get(kTestSpec.overlap.upper6, byte));
+ EXPECT_EQ(0, bs_get(kTestSpec.overlap.lower6, byte));
+ EXPECT_EQ(0, bs_get(kTestSpec.overlap.all8, byte));
+ byte = 0xff;
+ EXPECT_EQ(0x3f, bs_get(kTestSpec.overlap.upper6, byte));
+ EXPECT_EQ(0x3f, bs_get(kTestSpec.overlap.lower6, byte));
+ EXPECT_EQ(0xff, bs_get(kTestSpec.overlap.all8, byte));
+
+ byte = 0;
+ for (int i = 0; i < 0x3f; ++i) {
+ bs_assign(&byte, kTestSpec.overlap.lower6, i);
+ EXPECT_EQ((i & (0x3f << 2)) >> 2, bs_get(kTestSpec.overlap.upper6, byte));
+ EXPECT_EQ(i, bs_get(kTestSpec.overlap.lower6, byte));
+ EXPECT_EQ(i, bs_get(kTestSpec.overlap.all8, byte));
+ }
+ byte = 0;
+ for (int i = 0; i < 0x3f; ++i) {
+ bs_assign(&byte, kTestSpec.overlap.upper6, i);
+ EXPECT_EQ(i, bs_get(kTestSpec.overlap.upper6, byte));
+ EXPECT_EQ((i << 2) & 0x3f, bs_get(kTestSpec.overlap.lower6, byte));
+ EXPECT_EQ(i << 2, bs_get(kTestSpec.overlap.all8, byte));
+ }
+ byte = 0;
+ for (int i = 0; i < 0xff; ++i) {
+ bs_assign(&byte, kTestSpec.overlap.all8, i);
+ EXPECT_EQ(i >> 2, bs_get(kTestSpec.overlap.upper6, byte));
+ EXPECT_EQ(i & 0x3f, bs_get(kTestSpec.overlap.lower6, byte));
+ EXPECT_EQ(i, bs_get(kTestSpec.overlap.all8, byte));
+ }
+}
diff --git a/libese/tests/ese_unittests.cpp b/libese/tests/ese_unittests.cpp
index dcfc7f8..c62b758 100644
--- a/libese/tests/ese_unittests.cpp
+++ b/libese/tests/ese_unittests.cpp
@@ -68,7 +68,7 @@
};
TEST_F(EseInterfaceTest, EseCloseNull) {
- EXPECT_EQ(-1, ese_close(NULL));
+ ese_close(NULL);
};
TEST_F(EseInterfaceTest, EseCloseNoOp) {
@@ -79,32 +79,32 @@
.ops = &dummy_ops
};
/* Will pass without an open first. */
- EXPECT_EQ(0, ese_close(&dummy));
+ ese_close(&dummy);
};
TEST_F(EseInterfaceTest, EseCloseOk) {
EXPECT_EQ(0, ese_open(&ese_, NULL));
- EXPECT_EQ(0, ese_close(&ese_));
+ ese_close(&ese_);
};
TEST_F(EseInterfaceTest, EseClosePending) {
EXPECT_EQ(0, ese_open(&ese_, NULL));
ese_.ops->hw_transmit(&ese_, NULL, 0, 0);
- EXPECT_EQ(-1, ese_close(&ese_));
+ ese_close(&ese_);
EXPECT_EQ(0, ese_open(&ese_, NULL));
ese_.ops->hw_transmit(&ese_, NULL, 0, 1);
ese_.ops->hw_receive(&ese_, NULL, 0, 0);
- EXPECT_EQ(-1, ese_close(&ese_));
+ ese_close(&ese_);
EXPECT_EQ(0, ese_open(&ese_, NULL));
ese_.ops->hw_receive(&ese_, NULL, 0, 1);
- EXPECT_EQ(0, ese_close(&ese_));
+ ese_close(&ese_);
};
TEST_F(EseInterfaceTest, EseTransceiveSendNothing) {
EXPECT_EQ(0, ese_open(&ese_, NULL));
EXPECT_EQ(0, ese_transceive(&ese_, NULL, 0, NULL, 0));
- EXPECT_EQ(0, ese_close(&ese_));
+ ese_close(&ese_);
};
TEST_F(EseInterfaceTest, EseTransceiveNoOps) {
diff --git a/tools/ese_relay/Android.bp b/tools/ese_relay/Android.bp
index bbb1dc7..329dabe 100644
--- a/tools/ese_relay/Android.bp
+++ b/tools/ese_relay/Android.bp
@@ -14,38 +14,39 @@
// limitations under the License.
//
-relay_shared_libraries = [
- "liblog",
- "libese",
-]
+cc_defaults {
+ name: "ese-relay-defaults",
+ host_supported: false,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: ["ese_relay.c"],
+ shared_libs: [
+ "liblog",
+ "libese",
+ ],
+}
cc_binary {
- name: "ese-relay-pn80t",
- srcs: ["ese_relay.c", "ese_relay_pn80t.c"],
- host_supported: false,
- shared_libs: ["libese-hw-nxp-pn80t-spidev", "libese-teq1"] +
- relay_shared_libraries,
- target: {
- darwin: {
- enabled: false,
- },
- windows: {
- enabled: false,
- },
- },
+ name: "ese-relay-pn80t-nq-nci",
+ defaults: ["ese-relay-defaults"],
+ srcs: ["ese_relay_pn80t_nq_nci.c"],
+ shared_libs: ["libese-hw-nxp-pn80t-nq-nci", "libese-teq1"],
+}
+
+cc_binary {
+ name: "ese-relay-pn80t-spidev",
+ defaults: ["ese-relay-defaults"],
+ srcs: ["ese_relay_pn80t_spidev.c"],
+ shared_libs: ["libese-hw-nxp-pn80t-spidev", "libese-teq1"],
}
cc_binary {
name: "ese-relay-fake",
- srcs: ["ese_relay.c", "ese_relay_fake.c"],
+ defaults: ["ese-relay-defaults"],
+ srcs: ["ese_relay_fake.c"],
+ shared_libs: ["libese-hw-fake"],
host_supported: true,
- shared_libs: relay_shared_libraries + ["libese-hw-fake"],
- target: {
- darwin: {
- enabled: false,
- },
- windows: {
- enabled: false,
- },
- },
}
diff --git a/tools/ese_relay/ese_relay_fake.c b/tools/ese_relay/ese_relay_fake.c
index 862e5c5..f336709 100644
--- a/tools/ese_relay/ese_relay_fake.c
+++ b/tools/ese_relay/ese_relay_fake.c
@@ -24,7 +24,4 @@
const size_t kAtrLength = sizeof(kAtr);
const void *kEseOpenData = NULL;
-void ese_relay_init(struct EseInterface *ese) {
- const struct EseInterface kInterface = ESE_INITIALIZER(ESE_HW_FAKE);
- memcpy(ese, &kInterface, sizeof(kInterface));
-}
+void ese_relay_init(struct EseInterface *ese) { ese_init(ese, ESE_HW_FAKE); }
diff --git a/tools/ese_relay/ese_relay_pn80t_nq_nci.c b/tools/ese_relay/ese_relay_pn80t_nq_nci.c
new file mode 100644
index 0000000..d9fbb25
--- /dev/null
+++ b/tools/ese_relay/ese_relay_pn80t_nq_nci.c
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/* Pull in NXP PN80T for use in the relay. */
+#include <ese/ese.h>
+ESE_INCLUDE_HW(ESE_HW_NXP_PN80T_NQ_NCI);
+
+/* From
+ * http://stackoverflow.com/questions/27074551/how-to-list-applets-on-jcop-cards
+ */
+static const uint8_t kAtrBytes[] = {
+ 0x3B, 0xF8, 0x13, 0x00, 0x00, 0x81, 0x31, 0xFE, 0x45,
+ 0x4A, 0x43, 0x4F, 0x50, 0x76, 0x32, 0x34, 0x31, 0xB7,
+};
+const uint8_t *kAtr = &kAtrBytes[0];
+const size_t kAtrLength = sizeof(kAtr);
+const void *kEseOpenData = NULL;
+
+void ese_relay_init(struct EseInterface *ese) {
+ ese_init(ese, ESE_HW_NXP_PN80T_NQ_NCI);
+}
diff --git a/tools/ese_relay/ese_relay_pn80t.c b/tools/ese_relay/ese_relay_pn80t_spidev.c
similarity index 100%
rename from tools/ese_relay/ese_relay_pn80t.c
rename to tools/ese_relay/ese_relay_pn80t_spidev.c