libese: initial commit

libese is a low level library for enabling embedded secure
element use from Java or embedded usecases.

This commit defines the client API, the hardware abstraction API,
and supplies functional implementation of the T=1 protocol (frequently
used as the wire protocol for SPI attached smart cards/eSEs).

Included in this is one hardware reference implementation using
a NXP PN80T developer board wired to a HiKey 6220.

Test: baseline unittests; manual run of the examples/ese_nxp_sample; use
of relay with hardware
Bug: 34193615

Change-Id: I98037793bc29b3730c5301ee9cd5e6cd7465117d
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..97af836
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,17 @@
+//
+// 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.
+//
+
+subdirs = ["libese", "libese-teq1", "libese-hw", "examples", "tools"]
diff --git a/CPPLINT.cfg b/CPPLINT.cfg
new file mode 100644
index 0000000..51ff339
--- /dev/null
+++ b/CPPLINT.cfg
@@ -0,0 +1 @@
+exclude_files=.*
diff --git a/MODULE_LICENSE_APACHE b/MODULE_LICENSE_APACHE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..952429e
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,14 @@
+Copyright (C) 2016 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.
+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.
+
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..254a062
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,5 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..efca4c6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# libese
+
+Document last updated: 13 Jan 2017
+
+## Introduction
+
+libese provides a minimal transport wrapper for communicating with
+embedded secure elements. Embedded secure elements typically adhere
+to smart card standards whose translation is not always smooth when
+migrated to an always connected bus, like SPI.  The interfaces
+exposed by libese should enable higher level "terminal" implementations
+to be written on top and/or a service which provides a similar
+interface.
+
+Behind the interface, libese should help smooth over the differences
+between eSEs and smart cards use in the hardware adapter
+implementations. Additionally, a T=1 implementation is supplied,
+as it appears to be the most common wire transport for these chips.
+
+## Usage
+
+(TBD: See tools/ and example/)
+
+## Supported backends
+
+At present, only sample backends and a Linux SPIdev driven NXP
+developer board are supported.
+
diff --git a/examples/Android.bp b/examples/Android.bp
new file mode 100644
index 0000000..e977962
--- /dev/null
+++ b/examples/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+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,
+}
diff --git a/examples/ese_nxp_sample.c b/examples/ese_nxp_sample.c
new file mode 100644
index 0000000..392ce63
--- /dev/null
+++ b/examples/ese_nxp_sample.c
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * Test program to select the CardManager and request the card production
+ * lifcycle data (CPLC).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#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);
+
+/* APDU: CLA INS P1-P2 Lc Data Le */
+struct Apdu {
+  size_t length;
+  const uint8_t *bytes;
+  const char *desc;
+};
+
+struct ApduSession {
+  size_t count;
+  const char *desc;
+  const struct Apdu *apdus[];
+};
+
+const uint8_t kSelectCardManagerBytes[] = {0x00, 0xA4, 0x04, 0x00, 0x00};
+/* Implicitly selects the ISD at A0 00 00 01 51 00 00 */
+const struct Apdu kSelectCardManager = {
+    .length = sizeof(kSelectCardManagerBytes),
+    .bytes = &kSelectCardManagerBytes[0],
+    .desc = "SELECT CARD MANAGER",
+};
+
+const uint8_t kGetCplcBytes[] = {0x80, 0xCA, 0x9F, 0x7F, 0x00};
+const struct Apdu kGetCplc = {
+    .length = sizeof(kGetCplcBytes),
+    .bytes = &kGetCplcBytes[0],
+    .desc = "GET CPLC",
+};
+
+const struct ApduSession kGetCplcSession = {
+    .count = 2,
+    .desc = "GET-CPLC",
+    .apdus =
+        {
+            &kSelectCardManager, &kGetCplc,
+        },
+};
+
+/* Define the loader service sessions here! */
+const uint8_t kSelectJcopIdentifyBytes[] = {
+    0x00, 0xA4, 0x04, 0x00, 0x09, 0xA0, 0x00,
+    0x00, 0x01, 0x67, 0x41, 0x30, 0x00, 0xFF,
+};
+const struct Apdu kSelectJcopIdentify = {
+    .length = sizeof(kSelectJcopIdentifyBytes),
+    .bytes = &kSelectJcopIdentifyBytes[0],
+    .desc = "SELECT JCOP IDENTIFY",
+};
+
+const struct ApduSession *kSessions[] = {
+    &kGetCplcSession,
+};
+
+int main() {
+  struct EseInterface ese = ESE_INITIALIZER(ESE_HW_NXP_PN80T_SPIDEV);
+  size_t s = 0;
+  for (; s < sizeof(kSessions) / sizeof(kSessions[0]); ++s) {
+    int recvd;
+    size_t apdu_index = 0;
+    uint8_t rx_buf[1024];
+    if (ese_open(&ese, (void *)(&nxp_boards_hikey_spidev))) {
+      printf("Cannot open hw\n");
+      if (ese_error(&ese))
+        printf("eSE error (%d): %s\n", ese_error_code(&ese),
+               ese_error_message(&ese));
+      return 1;
+    }
+    printf("Running session %s\n", kSessions[s]->desc);
+    for (; apdu_index < kSessions[s]->count; ++apdu_index) {
+      size_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("TX: ");
+      for (i = 0; i < apdu->length; ++i)
+        printf("%.2X ", apdu->bytes[i]);
+      printf("\n");
+
+      recvd = ese_transceive(&ese, (uint8_t *)apdu->bytes, apdu->length, rx_buf,
+                             sizeof(rx_buf));
+      if (ese_error(&ese)) {
+        printf("An error (%d) occurred: %s", ese_error_code(&ese),
+               ese_error_message(&ese));
+        return -1;
+      }
+      if (recvd > 0) {
+        int i;
+        printf("Read %d bytes from card\n", recvd);
+        printf("RX: ");
+        for (i = 0; i < recvd; ++i)
+          printf("%.2X ", rx_buf[i]);
+        printf("\n");
+      }
+      usleep(1000);
+    }
+    printf("Session completed\n\n");
+    ese_close(&ese);
+  }
+  return 0;
+}
diff --git a/libese-hw/Android.bp b/libese-hw/Android.bp
new file mode 100644
index 0000000..0922f1f
--- /dev/null
+++ b/libese-hw/Android.bp
@@ -0,0 +1,33 @@
+//
+// 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-hw-fake",
+    srcs: ["ese_hw_fake.c"],
+    host_supported: true,
+    cflags: ["-DLOG_NDEBUG=0"],
+    shared_libs: ["liblog", "libese"],
+}
+
+cc_library {
+    name: "libese-hw-echo",
+    host_supported: true,
+    srcs: ["ese_hw_echo.c"],
+    cflags: ["-DLOG_NDEBUG=0"],
+    shared_libs: ["liblog", "libese", "libese-teq1"],
+}
+
+subdirs = ["tests", "nxp"]
diff --git a/libese-hw/ese_hw_echo.c b/libese-hw/ese_hw_echo.c
new file mode 100644
index 0000000..cbb2fea
--- /dev/null
+++ b/libese-hw/ese_hw_echo.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Implement a simple T=1 echo endpoint.
+ */
+
+#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>
+
+struct EchoState {
+  struct Teq1Frame frame;
+  uint8_t *rx_fill;
+  uint8_t *tx_sent;
+  int recvd;
+};
+
+#define ECHO_STATE(ese) (*(struct EchoState **)(&ese->pad[0]))
+
+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 = malloc(sizeof(struct EchoState));
+  if (!*es_ptr)
+    return -1;
+  es = ECHO_STATE(ese);
+  es->rx_fill = &es->frame.header.NAD;
+  es->tx_sent = es->rx_fill;
+  es->recvd = 0;
+  return 0;
+}
+
+static int echo_close(struct EseInterface *ese) {
+  struct EchoState *es;
+  if (!ese)
+    return -1;
+  es = ECHO_STATE(ese);
+  free(es);
+  return 0;
+}
+
+static size_t echo_receive(struct EseInterface *ese, uint8_t *buf, size_t len,
+                           int complete) {
+  struct EchoState *es = ECHO_STATE(ese);
+  ALOGV("interface attempting to read data");
+  if (!es->recvd)
+    return 0;
+
+  if (len > sizeof(es->frame) - (es->tx_sent - &es->frame.header.NAD))
+    return 0;
+
+  /* NAD was polled for so skip it. */
+  memcpy(buf, es->tx_sent, len);
+  es->tx_sent += len;
+  if (complete) {
+    es->tx_sent = &es->frame.header.NAD;
+    es->recvd = 0;
+    ALOGV("card sent a frame");
+  }
+  return sizeof(es->frame.header) + es->frame.header.LEN;
+}
+
+static size_t echo_transmit(struct EseInterface *ese, const uint8_t *buf,
+                            size_t len, int complete) {
+  struct EchoState *es = ECHO_STATE(ese);
+  ALOGV("interface transmitting data");
+  if (len > sizeof(es->frame) - (es->rx_fill - &es->frame.header.NAD))
+    return 0;
+  memcpy(es->rx_fill, buf, len);
+  es->rx_fill += len;
+  es->recvd = complete;
+  if (complete) {
+    es->frame.header.NAD = 0x00;
+    if (teq1_compute_LRC(&es->frame) != es->frame.INF[es->frame.header.LEN]) {
+      ALOGV("card received frame with bad LRC");
+      return 0;
+    }
+    ALOGV("card received valid frame");
+    es->rx_fill = &es->frame.header.NAD;
+  }
+  return len;
+}
+
+static int echo_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
+                     int complete) {
+  struct EchoState *es = ECHO_STATE(ese);
+  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+  ALOGV("interface polling for start of frame/host node address: %x", poll_for);
+  /* In reality, we should be polling at intervals up to the timeout. */
+  if (timeout > 0.0)
+    usleep(timeout * 1000);
+  if (poll_for == opts->host_address) {
+    ALOGV("interface received NAD");
+    if (!complete) {
+      es->tx_sent++; /* Consume the polled byte: NAD */
+    }
+    return 1;
+  }
+  return -1;
+}
+
+int echo_preprocess(const struct Teq1ProtocolOptions *const opts,
+                    struct Teq1Frame *frame, int tx) {
+  if (tx) {
+    /* Recompute the LRC with the NAD of 0x00 */
+    frame->header.NAD = 0x00;
+    frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
+    frame->header.NAD = opts->node_address;
+    ALOGV("interface is preprocessing outbound frame");
+  } else {
+    /* Replace the NAD with 0x00 so the LRC check passes. */
+    frame->header.NAD = 0x00;
+    ALOGV("interface is preprocessing inbound frame");
+  }
+  return 0;
+}
+
+static const struct Teq1ProtocolOptions teq1_options = {
+    .host_address = 0xAA,
+    .node_address = 0xBB,
+    .bwt = 3.14152f,
+    .etu = 1.0f,
+    .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);
+
+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);
diff --git a/libese-hw/ese_hw_fake.c b/libese-hw/ese_hw_fake.c
new file mode 100644
index 0000000..8fe514f
--- /dev/null
+++ b/libese-hw/ese_hw_fake.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Minimal functions that only validate arguments.
+ */
+
+#include <ese/ese.h>
+
+static int fake_open(struct EseInterface *ese,
+                     void *hw_opts __attribute__((unused))) {
+  ese->pad[0] = 1; /* rx complete */
+  ese->pad[1] = 1; /* tx complete */
+  return 0;
+}
+
+static int fake_close(struct EseInterface *ese) {
+  if (!ese)
+    return -1;
+  if (!ese->pad[0] || !ese->pad[1]) {
+    /* Set by caller. ese->error.is_error = 1; */
+    ese_set_error(ese, 0);
+    return -1;
+  }
+  return 0;
+}
+
+static size_t fake_receive(struct EseInterface *ese, uint8_t *buf, size_t len,
+                           int complete) {
+  if (!ese)
+    return -1;
+  if (!ese->pad[1]) {
+    ese_set_error(ese, 1);
+    return -1;
+  }
+  ese->pad[0] = complete;
+  if (!buf && len) {
+    ese_set_error(ese, 2);
+    return -1;
+  }
+  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;
+  if (!ese->pad[0]) {
+    ese_set_error(ese, 3);
+    return -1;
+  }
+  ese->pad[1] = complete;
+  if (!buf && len) {
+    ese_set_error(ese, 4);
+    return -1;
+  }
+  if (!len)
+    return 0;
+  return len;
+}
+
+static int fake_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
+                     int complete) {
+  /* Poll begins a receive-train so transmit needs to be completed. */
+  if (!ese->pad[1]) {
+    ese_set_error(ese, 1);
+    return -1;
+  }
+  if (timeout == 0.0f) {
+    /* Instant timeout. */
+    return 0;
+  }
+  /* Only expect one value to work. */
+  if (poll_for == 0xad) {
+    return 1;
+  }
+  ese->pad[0] = complete;
+  return 0;
+}
+
+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;
+  if (!ese->pad[0] || !ese->pad[1]) {
+    ese_set_error(ese, 5);
+    return 0;
+  }
+  while (processed < tx_len) {
+    size_t sent = fake_transmit(ese, tx_buf, tx_len, 0);
+    if (sent == 0) {
+      if (ese->error.is_err)
+        return 0;
+      ese_set_error(ese, 6);
+      return 0;
+    }
+    processed += sent;
+  }
+  fake_transmit(ese, NULL, 0, 1); /* Complete. */
+  if (fake_poll(ese, 0xad, 10, 0) != 1) {
+    ese_set_error(ese, -2);
+    return 0;
+  }
+  /* A real implementation would have protocol errors to contend with. */
+  processed = fake_receive(ese, rx_buf, rx_len, 1);
+  return processed;
+}
+
+static const struct EseOperations ops = {
+    .name = "eSE Fake Hardware",
+    .open = &fake_open,
+    .hw_receive = &fake_receive,
+    .hw_transmit = &fake_transmit,
+    .transceive = &fake_transceive,
+    .poll = &fake_poll,
+    .close = &fake_close,
+    .opts = NULL,
+};
+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
new file mode 100644
index 0000000..c6f4dcc
--- /dev/null
+++ b/libese-hw/nxp/Android.bp
@@ -0,0 +1,27 @@
+//
+// 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-hw-nxp-pn80t-spidev",
+    host_supported: false,
+    debug: {
+      cflags: ["-DLOG_NDEBUG=0"],
+    },
+    srcs: ["pn80t_spidev.c"],
+    local_include_dirs: ["include"],
+    shared_libs: ["liblog", "libese", "libese-teq1"],
+    export_include_dirs: ["include"]
+}
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
new file mode 100644
index 0000000..4f73003
--- /dev/null
+++ b/libese-hw/nxp/include/ese/hw/nxp/pn80t/boards/hikey-spidev.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.
+ */
+
+/* No guard is intentional given the definition below. */
+#include <ese/hw/nxp/spi_board.h>
+
+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 */
+};
diff --git a/libese-hw/nxp/include/ese/hw/nxp/spi_board.h b/libese-hw/nxp/include/ese/hw/nxp/spi_board.h
new file mode 100644
index 0000000..109e348
--- /dev/null
+++ b/libese-hw/nxp/include/ese/hw/nxp/spi_board.h
@@ -0,0 +1,28 @@
+/*
+ * 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_SPI_BOARD_H_
+#define ESE_HW_NXP_SPI_BOARD_H_ 1
+
+struct NxpSpiBoard {
+  const char *dev_path;
+  int reset_gpio;
+  int svdd_pwr_req_gpio;
+};
+
+#endif  /* ESE_HW_NXP_SPI_BOARD_H_ */
+
+
diff --git a/libese-hw/nxp/pn80t_spidev.c b/libese-hw/nxp/pn80t_spidev.c
new file mode 100644
index 0000000..3f63103
--- /dev/null
+++ b/libese-hw/nxp/pn80t_spidev.c
@@ -0,0 +1,346 @@
+/*
+ * 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
new file mode 100644
index 0000000..1ce0b6b
--- /dev/null
+++ b/libese-hw/tests/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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_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,
+}
diff --git a/libese-hw/tests/ese_hw_echo_tests.cpp b/libese-hw/tests/ese_hw_echo_tests.cpp
new file mode 100644
index 0000000..4c62822
--- /dev/null
+++ b/libese-hw/tests/ese_hw_echo_tests.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * Tests a very simple end to end T=1 using the echo backend.
+ */
+
+#include <vector>
+#include <gtest/gtest.h>
+
+#include <ese/ese.h>
+
+ESE_INCLUDE_HW(ESE_HW_ECHO);
+
+using ::testing::Test;
+
+class EseInterfaceTest : public virtual Test {
+ public:
+  EseInterfaceTest() : ese_(ESE_INITIALIZER(ESE_HW_ECHO)) {
+  }
+  virtual ~EseInterfaceTest() { }
+  virtual void SetUp() {
+    ASSERT_EQ(0, ese_open(&ese_, NULL));
+  }
+  virtual void TearDown() {
+    ASSERT_EQ(0, ese_close(&ese_));
+  }
+  struct EseInterface ese_;
+};
+
+TEST_F(EseInterfaceTest, EseTranscieveOneBlock) {
+  std::vector<uint8_t> apdu;
+  std::vector<uint8_t> apdu_reply;
+  uint8_t apdu_len = 252;
+  apdu.resize(apdu_len, 'A');
+  apdu_reply.resize(256, 'B');
+  EXPECT_EQ(apdu_len, ese_transceive(&ese_, apdu.data(), apdu_len, apdu_reply.data(), apdu_reply.size()));
+  apdu_reply.resize(apdu_len);
+  EXPECT_EQ(apdu, apdu_reply);
+};
diff --git a/libese-teq1/Android.bp b/libese-teq1/Android.bp
new file mode 100644
index 0000000..86b9a41
--- /dev/null
+++ b/libese-teq1/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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-teq1",
+    host_supported: true,
+
+    srcs: ["teq1.c"],
+
+    local_include_dirs: ["include"],
+
+    // Ensure that only explicitly exported symbols are visible.
+    cflags: ["-fvisibility=internal"],
+    debug: {
+      cflags: ["-DLOG_NDEBUG=0"],
+    },
+    shared_libs: ["liblog", "libese"],
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+  name: "libese-teq1-private",
+  host_supported: true,
+
+  srcs: ["teq1.c"],
+  local_include_dirs: ["include"],
+
+  // Ensure that only explicitly exported symbols are visible.
+  shared_libs: ["liblog", "libese"],
+  export_include_dirs: ["include", "."],
+}
+
+subdirs = ["tests"]
diff --git a/libese-teq1/include/ese/teq1.h b/libese-teq1/include/ese/teq1.h
new file mode 100644
index 0000000..61831a5
--- /dev/null
+++ b/libese-teq1/include/ese/teq1.h
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#ifndef ESE_TEQ1_H_
+#define ESE_TEQ1_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ese/ese.h>
+
+/* Reserved codes for T=1 devices. */
+#define TEQ1_ERROR_HARD_FAIL 0
+#define TEQ1_ERROR_ABORT 1
+#define TEQ1_ERROR_DEVICE_RESET 2
+
+
+enum pcb_type {
+  kPcbTypeInfo0 = 0x0,
+  kPcbTypeInfo1 = 0x1,
+  kPcbTypeReceiveReady = 0x2,
+  kPcbTypeSupervisory = 0x3,
+  kPcbTypeMax,
+};
+
+enum super_type {
+  kSuperTypeResync = 0x0,
+  kSuperTypeIFS = 0x1,
+  kSuperTypeAbort = 0x2,
+  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 Teq1Header {
+  uint8_t NAD;
+  struct PCB PCB;
+  uint8_t LEN;
+} __attribute__((packed));
+
+#define INF_LEN 254
+#define IFSC 254
+struct Teq1Frame {
+  union {
+    uint8_t val[sizeof(struct Teq1Header) + INF_LEN + 1];
+    struct {
+      struct Teq1Header header;
+      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. */
+    };
+  };
+} __attribute__((packed));
+
+
+/*
+ * Required to be the header for all EseInterface pad[]s for
+ * cards implementing T=1.
+ */
+struct Teq1CardState {
+  union {
+    struct {
+      int card:1;
+      int interface:1;
+    };
+    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; \
+  (CARD)->seq.interface = 1;
+
+/*
+ * Used by devices implementing T=1 to set specific options
+ * or callback behavior.
+ */
+struct Teq1ProtocolOptions;
+typedef int (teq1_protocol_preprocess_op_t)(const struct Teq1ProtocolOptions *const, struct Teq1Frame *, int);
+
+struct Teq1ProtocolOptions {
+  uint8_t host_address;  /* NAD to listen for */
+  uint8_t node_address;  /* NAD to send to */
+  float bwt;
+  float etu;
+  /*
+   * If not NULL, is called immediately before transmit (1)
+   * and immediately after receive.
+   */
+  teq1_protocol_preprocess_op_t *preprocess;
+};
+
+/* I-block bits */
+#define kTeq1InfoMoreBit     (1 << 5)
+#define kTeq1InfoSeqBit      (1 << 6)
+
+/* R-block bits */
+#define kTeq1RrType         (1 << 7)
+#define kTeq1RrSeqBit       (1 << 4)
+#define kTeq1RrParityError  (1)
+#define kTeq1RrOtherError   (1 << 1)
+
+/* S-block bits */
+#define kTeq1SuperType      (3 << 6)
+#define kTeq1SuperRequestBit (0)
+#define kTeq1SuperResponseBit (1 << 5)
+#define kTeq1SuperResyncBit (0)
+#define kTeq1SuperIfsBit (1)
+#define kTeq1SuperAbortBit (1 << 1)
+#define kTeq1SuperWtxBit (3)
+
+/* I(Seq, More-bit) */
+#define TEQ1_I(S, M) ((S) << 6) | ((M) << 5)
+
+/* R(Seq, Other Error, Parity Error) */
+#define TEQ1_R(S, O, P) (kTeq1RrType | ((S) << 4) | (P) | ((O) << 1))
+/* S_<TYPE>(response) */
+#define TEQ1_S_RESYNC(R) (kTeq1SuperType | ((R) << 5) | kTeq1SuperResyncBit)
+#define TEQ1_S_WTX(R)  (kTeq1SuperType | ((R) << 5) | kTeq1SuperWtxBit)
+#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);
+
+uint8_t teq1_compute_LRC(const struct Teq1Frame *frame);
+
+#define teq1_trace_header() ALOGI("%-20s --- %20s", "Interface", "Card")
+#define teq1_trace_transmit(PCB, LEN) ALOGI("%-20s --> %20s [%3hhu]", teq1_pcb_to_name(PCB), "", LEN)
+#define teq1_trace_receive(PCB, LEN) ALOGI("%-20s <-- %20s [%3hhu]", "", teq1_pcb_to_name(PCB), LEN)
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* ESE_TEQ1_H_ */
diff --git a/libese-teq1/teq1.c b/libese-teq1/teq1.c
new file mode 100644
index 0000000..9d0b2a5
--- /dev/null
+++ b/libese-teq1/teq1.c
@@ -0,0 +1,647 @@
+/*
+ * 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 <string.h>
+
+#include <ese/ese.h>
+#include <ese/teq1.h>
+
+#define LOG_TAG "ESE_T=1"
+#include <ese/log.h>
+
+#include "teq1_private.h"
+
+const char *teq1_rule_result_to_name(enum RuleResult result) {
+  switch (result) {
+  case kRuleResultComplete:
+    return "Complete";
+  case kRuleResultAbort:
+    return "Abort";
+  case kRuleResultContinue:
+    return "Continue";
+  case kRuleResultHardFail:
+    return "Hard failure";
+  case kRuleResultResetDevice:
+    return "Reset device";
+  case kRuleResultResetSession:
+    return "Reset session";
+  case kRuleResultRetransmit:
+    return "Retransmit";
+  case kRuleResultSingleShot:
+    return "Single shot";
+  };
+}
+
+const char *teq1_pcb_to_name(uint8_t pcb) {
+  switch (pcb) {
+  case I(0, 0):
+    return "I(0, 0)";
+  case I(0, 1):
+    return "I(0, 1)";
+  case I(1, 0):
+    return "I(1, 0)";
+  case I(1, 1):
+    return "I(1, 1)";
+  case R(0, 0, 0):
+    return "R(0, 0, 0)";
+  case R(0, 0, 1):
+    return "R(0, 0, 1)";
+  case R(0, 1, 0):
+    return "R(0, 1, 0)";
+  case R(0, 1, 1):
+    return "R(0, 1, 1)";
+  case R(1, 0, 0):
+    return "R(1, 0, 0)";
+  case R(1, 0, 1):
+    return "R(1, 0, 1)";
+  case R(1, 1, 0):
+    return "R(1, 1, 0)";
+  case R(1, 1, 1):
+    return "R(1, 1, 1)";
+  case S(RESYNC, REQUEST):
+    return "S(RESYNC, REQUEST)";
+  case S(RESYNC, RESPONSE):
+    return "S(RESYNC, RESPONSE)";
+  case S(IFS, REQUEST):
+    return "S(IFS, REQUEST)";
+  case S(IFS, RESPONSE):
+    return "S(IFS, RESPONSE)";
+  case S(ABORT, REQUEST):
+    return "S(ABORT, REQUEST)";
+  case S(ABORT, RESPONSE):
+    return "S(ABORT, RESPONSE)";
+  case S(WTX, REQUEST):
+    return "S(WTX, REQUEST)";
+  case S(WTX, RESPONSE):
+    return "S(WTX, RESPONSE)";
+  case 255:
+    return "INTERNAL-ERROR";
+  default:
+    return "???";
+  }
+}
+
+int teq1_transmit(struct EseInterface *ese, struct Teq1Frame *frame) {
+  /* Set during ese_open() by teq1_init. */
+  const struct Teq1ProtocolOptions *opts = ese->ops->opts;
+
+  /* Set correct node address. */
+  frame->header.NAD = opts->node_address;
+
+  /* Compute the LRC */
+  frame->INF[frame->header.LEN] = teq1_compute_LRC(frame);
+
+  /*
+   * If the card does something weird, like expect an CRC/LRC based on a
+   * different
+   * header value, the preprocessing can handle it.
+   */
+  if (opts->preprocess)
+    opts->preprocess(opts, frame, 1);
+
+  /*
+   * Begins transmission and ignore errors.
+   * Failed transmissions will result eventually in a resync then reset.
+   */
+  teq1_trace_transmit(frame->header.PCB.val, frame->header.LEN);
+  ese->ops->hw_transmit(ese, frame->val,
+                        sizeof(frame->header) + frame->header.LEN + 1, 1);
+  /* Even though in practice any WTX BWT extension starts when the above
+   * transmit ends.
+   * it is easier to implement it in the polling timeout of receive.
+   */
+  return 0;
+}
+
+int teq1_receive(struct EseInterface *ese, 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) {
+    /* Timed out or comm error. */
+    return -1;
+  }
+  /* We polled for the NAD above. */
+  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);
+  if (frame->header.LEN == 255) {
+    ALOGV("received invalid LEN of 255");
+    /* Close the receive window and return failure. */
+    ese->ops->hw_receive(ese, NULL, 0, 1);
+    return -1;
+  }
+  /*
+   * Get the data and the first byte of CRC data.
+   * Note, CRC support is not implemented. Only a single LRC byte is expected.
+   */
+  ese->ops->hw_receive(ese, (uint8_t *)(&(frame->INF[0])),
+                       frame->header.LEN + 1, 1);
+  teq1_trace_receive(frame->header.PCB.val, frame->header.LEN);
+
+  /*
+   * If the card does something weird, like expect an CRC/LRC based on a
+   * different
+   * header value, the preprocessing should fix up here prior to the LRC check.
+   */
+  if (opts->preprocess)
+    opts->preprocess(opts, frame, 0);
+
+  /* LRC and other protocol goodness checks are not done here. */
+  return frame->header.LEN; /* Return data bytes read. */
+}
+
+uint8_t teq1_fill_info_block(struct Teq1State *state, struct Teq1Frame *frame) {
+  uint32_t inf_len = INF_LEN;
+  if (state->ifs < inf_len)
+    inf_len = state->ifs;
+  switch (frame->header.PCB.type) {
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1: {
+    uint32_t len = state->app_data.tx_len;
+    if (len > inf_len)
+      len = inf_len;
+    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
+     * data. */
+    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;
+    return len;
+  }
+  case kPcbTypeSupervisory:
+  case kPcbTypeReceiveReady:
+  default:
+    break;
+  }
+  return 255; /* Invalid block type. */
+}
+
+void teq1_get_app_data(struct Teq1State *state, struct Teq1Frame *frame) {
+  switch (frame->header.PCB.type) {
+  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)
+      len = state->app_data.rx_len;
+    memcpy(state->app_data.rx_buf, frame->INF, len);
+    /* The original caller must retain the starting pointer to determine
+     * actual available data.
+     */
+    state->app_data.rx_len -= len;
+    state->app_data.rx_buf += len;
+    return;
+  }
+  case kPcbTypeReceiveReady:
+  case kPcbTypeSupervisory:
+  default:
+    break;
+  }
+  return;
+}
+
+/* Returns an R(0) frame with error bits set. */
+uint8_t teq1_frame_error_check(struct Teq1State *state,
+                               struct Teq1Frame *tx_frame,
+                               struct Teq1Frame *rx_frame) {
+  uint8_t lrc = 0;
+  int chained = 0;
+  if (rx_frame->header.PCB.val == 255)
+    return R(0, 1, 0); /* Other error */
+
+  lrc = teq1_compute_LRC(rx_frame);
+  if (rx_frame->INF[rx_frame->header.LEN] != lrc) {
+    ALOGV("Invalid LRC %x instead of %x", rx_frame->INF[rx_frame->header.LEN],
+          lrc);
+    return R(0, 0, 1); /* Parity error */
+  }
+
+  /* Check if we were chained and increment the last sent sequence. */
+  switch (tx_frame->header.PCB.type) {
+  case kPcbTypeInfo0:
+  case kPcbTypeInfo1:
+    chained = tx_frame->header.PCB.I.more_data;
+    state->card_state->seq.interface = tx_frame->header.PCB.I.send_seq;
+  }
+
+  /* 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) {
+  case kPcbTypeSupervisory:
+    if (rx_frame->header.PCB.val != S(RESYNC, RESPONSE) &&
+        rx_frame->header.LEN != 1)
+      return R(0, 1, 0);
+    break;
+  case kPcbTypeReceiveReady:
+    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,
+            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;
+  default:
+    break;
+  };
+  return 0;
+}
+
+enum RuleResult teq1_rules(struct Teq1State *state, struct Teq1Frame *tx_frame,
+                           struct Teq1Frame *rx_frame,
+                           struct Teq1Frame *next_tx) {
+  /* Rule 1 is enforced by first call∴ Start with I(0,M). */
+  /* 0 = TX, 1 = RX */
+  /* msb = tx pcb, lsb = rx pcb */
+  /* BUG_ON(!rx_frame && !tx_frame && !next_tx); */
+  uint16_t txrx = TEQ1_RULE(tx_frame->header.PCB.val, rx_frame->header.PCB.val);
+  struct PCB 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) {
+      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);
+      state->errors++;
+      /* Rule 6.4 */
+      if (state->errors >= 6) { /* XXX: Review error count lifetime. */
+        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);
+        /* Resync result in a fresh session, so we should just continue here. */
+        return kRuleResultContinue;
+      }
+    }
+
+    /* Specific matches */
+    switch (txrx) {
+    /*** Rule 2.1: I() -> I() ***/
+    /* Error check will determine if the card seq is right. */
+    case TEQ1_RULE(I(0, 0), I(0, 0)):
+    case TEQ1_RULE(I(0, 0), I(1, 0)):
+    case TEQ1_RULE(I(1, 0), I(1, 0)):
+    case TEQ1_RULE(I(1, 0), I(0, 0)):
+      /* Read app data & return. */
+      teq1_get_app_data(state, rx_frame);
+      return kRuleResultComplete;
+
+      /* 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.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;
+      teq1_fill_info_block(state, next_tx); /* Sets M-bit and LEN. */
+      return kRuleResultContinue;
+
+    /*** Rule 3 ***/
+    case TEQ1_RULE(I(0, 0), S(WTX, REQUEST)):
+    case TEQ1_RULE(I(1, 0), S(WTX, REQUEST)):
+      /* Note: Spec is unclear on if WTX can occur during chaining so we make it
+      an error for now.
+      case TEQ1_RULE(I(0, 1), S(WTX, REQUEST)):
+      case TEQ1_RULE(I(1, 1), S(WTX, REQUEST)):
+      */
+      /* Send S(WTX, RESPONSE) with same INF */
+      next_tx->header.PCB.val = S(WTX, RESPONSE);
+      next_tx->header.LEN = 1;
+      next_tx->INF[0] = rx_frame->INF[0];
+      state->wait_mult = rx_frame->INF[0];
+      /* Then wait BWT*INF[0] after transmission. */
+      return kRuleResultSingleShot; /* Send then call back in with same tx_frame
+                                       and new rx_frame. */
+
+    /*** Rule 4 ***/
+    case TEQ1_RULE(S(IFS, REQUEST), S(IFS, RESPONSE)):
+      /* XXX: Check INFs match. */
+      return kRuleResultComplete; /* This is treated as an unique operation. */
+    case TEQ1_RULE(I(0, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(0, 1), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(1, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(I(1, 1), S(IFS, REQUEST)):
+    /* Don't support a IFS_REQUEST if we sent an error R-block. */
+    case TEQ1_RULE(R(0, 0, 0), S(IFS, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 0), S(IFS, REQUEST)):
+      next_tx->header.PCB.val = S(IFS, RESPONSE);
+      next_tx->header.LEN = 1;
+      next_tx->INF[0] = rx_frame->INF[0];
+      state->ifs = rx_frame->INF[0];
+      return kRuleResultSingleShot;
+
+    /*** Rule 5  (see Rule 2.2 for the chained-tx side. ) ***/
+    case TEQ1_RULE(R(0, 0, 0), I(0, 0)):
+    case TEQ1_RULE(R(1, 0, 0), I(1, 0)):
+      /* Chaining ended with terminal I-block. */
+      teq1_get_app_data(state, rx_frame);
+      return kRuleResultComplete;
+    case TEQ1_RULE(R(0, 0, 0), I(0, 1)):
+    case TEQ1_RULE(R(1, 0, 0), I(1, 1)):
+      /* Chaining continued; consume partial data and send R(N(S)) */
+      teq1_get_app_data(state, rx_frame);
+      next_tx->header.PCB.val = TEQ1_R(!rx_frame->header.PCB.I.send_seq, 0, 0);
+      return kRuleResultContinue;
+
+    /* Rule 6: Interface can send a RESYNC */
+    /* Rule 6.1: timeout BWT right. No case here. */
+    /* Rule 6.2, 6.3 */
+    case TEQ1_RULE(S(RESYNC, REQUEST), S(RESYNC, RESPONSE)):
+      /* Rule 6.5: indicates that the card should assume its prior
+       * block was lost _and_ the interface gets transmit privilege,
+       * so we just start fresh.
+       */
+      return kRuleResultResetSession; /* Start a new exchange (rule 6.3) */
+    case TEQ1_RULE(S(RESYNC, REQUEST), 255):
+      /* Retransmit the same frame up to 3 times. */
+      return kRuleResultRetransmit;
+
+    /* Rule 7.1, 7.5, 7.6 */
+    case TEQ1_RULE(I(0, 0), 255):
+    case TEQ1_RULE(I(1, 0), 255):
+    case TEQ1_RULE(I(0, 1), 255):
+    case TEQ1_RULE(I(1, 1), 255):
+      next_tx->header.PCB.val = R_err.val;
+      next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+      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));
+      return kRuleResultSingleShot; /* So we still can retransmit the original.
+                                       */
+
+    /* Caught in the error check. */
+    case TEQ1_RULE(I(0, 0), R(1, 0, 0)):
+    case TEQ1_RULE(I(0, 0), R(1, 0, 1)):
+    case TEQ1_RULE(I(0, 0), R(1, 1, 0)):
+    case TEQ1_RULE(I(0, 0), R(1, 1, 1)):
+    case TEQ1_RULE(I(1, 0), R(0, 0, 0)):
+    case TEQ1_RULE(I(1, 0), R(0, 0, 1)):
+    case TEQ1_RULE(I(1, 0), R(0, 1, 0)):
+    case TEQ1_RULE(I(1, 0), R(0, 1, 1)):
+      next_tx->header.PCB.val = R(0, 0, 0);
+      next_tx->header.PCB.R.next_seq = tx_frame->header.PCB.I.send_seq;
+      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));
+      return kRuleResultSingleShot;
+
+    /* Rule 7.2: Retransmit the _same_ R-block. */
+    /* The remainder of this rule is implemented in the next switch. */
+    case TEQ1_RULE(R(0, 0, 0), 255):
+    case TEQ1_RULE(R(0, 0, 1), 255):
+    case TEQ1_RULE(R(0, 1, 0), 255):
+    case TEQ1_RULE(R(0, 1, 1), 255):
+    case TEQ1_RULE(R(1, 0, 0), 255):
+    case TEQ1_RULE(R(1, 0, 1), 255):
+    case TEQ1_RULE(R(1, 1, 0), 255):
+    case TEQ1_RULE(R(1, 1, 1), 255):
+      return kRuleResultRetransmit;
+
+    /* Rule 7.3 request */
+    /* Note, 7.3 for transmission of S(*, RESPONSE) won't be seen because they
+     * are
+     * single shots.
+     * Instead, the invalid block will be handled as invalid for the prior TX.
+     * This should yield the correct R-block.
+     */
+    case TEQ1_RULE(I(0, 0), R(0, 0, 0)):
+    case TEQ1_RULE(I(0, 0), R(0, 0, 1)):
+    case TEQ1_RULE(I(0, 0), R(0, 1, 0)):
+    case TEQ1_RULE(I(0, 0), R(0, 1, 1)):
+    case TEQ1_RULE(I(1, 0), R(1, 0, 0)):
+    case TEQ1_RULE(I(1, 0), R(1, 1, 0)):
+    case TEQ1_RULE(I(1, 0), R(1, 0, 1)):
+    case TEQ1_RULE(I(1, 0), R(1, 1, 1)):
+    case TEQ1_RULE(I(0, 1), R(0, 0, 0)):
+    case TEQ1_RULE(I(0, 1), R(0, 1, 0)):
+    case TEQ1_RULE(I(0, 1), R(0, 0, 1)):
+    case TEQ1_RULE(I(0, 1), R(0, 1, 1)):
+    case TEQ1_RULE(I(1, 1), R(1, 0, 0)):
+    case TEQ1_RULE(I(1, 1), R(1, 1, 0)):
+    case TEQ1_RULE(I(1, 1), R(1, 0, 1)):
+    case TEQ1_RULE(I(1, 1), R(1, 1, 1)):
+      /* Retransmit I-block */
+      return kRuleResultRetransmit;
+
+    /* Rule 8 is card only. */
+    /* Rule 9: aborting a chain.
+     * If a S(ABORT) is injected into this engine, then we may have sent an
+     * abort.
+     */
+    case TEQ1_RULE(S(ABORT, REQUEST), S(ABORT, RESPONSE)):
+      /* No need to send back a R() because we want to keep transmit. */
+      return kRuleResultComplete; /* If we sent it, then we are complete. */
+    case TEQ1_RULE(S(ABORT, RESPONSE), R(0, 0, 0)):
+    case TEQ1_RULE(S(ABORT, RESPONSE), R(1, 0, 0)):
+      /* Card triggered abortion complete but we can resume sending. */
+      return kRuleResultAbort;
+    /* An abort request can interrupt a chain anywhere and could occur
+     * after a failure path too.
+     */
+    case TEQ1_RULE(I(0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(I(1, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 0, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 1, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(0, 1, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 0, 1), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 1, 0), S(ABORT, REQUEST)):
+    case TEQ1_RULE(R(1, 1, 1), S(ABORT, REQUEST)):
+      next_tx->header.PCB.val = S(ABORT, REQUEST);
+      return kRuleResultContinue; /* Takes over prior flow. */
+    case TEQ1_RULE(S(ABORT, RESPONSE), 255):
+      return kRuleResultRetransmit;
+    /* Note, other blocks should be caught below. */
+    default:
+      break;
+    }
+
+    /* Note, only S(ABORT, REQUEST) and S(IFS, REQUEST) are supported
+     * for transmitting to the card. Others will result in error
+     * flows.
+     *
+     * For supported flows: If an operation was paused to
+     * send it, the caller may then switch to that state and resume.
+     */
+    if (rx_frame->header.PCB.val != 255) {
+      ALOGV("Unexpected frame. Marking error and re-evaluating.");
+      rx_frame->header.PCB.val = 255;
+      continue;
+    }
+
+    return kRuleResultHardFail;
+  }
+}
+
+/*
+ * TODO(wad): Consider splitting teq1_transcieve() into
+ *   teq1_transcieve_init() and teq1_transceive_process_one()
+ *   if testing becomes onerous given the loop below.
+ */
+
+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) {
+  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;
+  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);
+
+  /* First I-block is always I(0, M). After that, modulo 2. */
+  tx->header.PCB.val = 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);
+
+    /* 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));
+
+    /* -1 indicates a timeout or failure from hardware. */
+    if (teq1_receive(ese, 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;
+    }
+    /* 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));
+
+    result = teq1_rules(&state, tx, &rx_frame, &tx_frame[!active]);
+    ALOGV("[ %s ]", teq1_rule_result_to_name(result));
+    switch (result) {
+    case kRuleResultComplete:
+      done = 1;
+      break;
+    case kRuleResultRetransmit:
+      /* TODO(wad) Find a clean way to move into teq1_rules(). */
+      if (state.retransmits++ < 3)
+        continue;
+      if (tx->header.PCB.val == S(RESYNC, REQUEST)) {
+        ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+        return 0;
+      }
+      /* Fall through */
+      tx_frame[!active].header.PCB.val = S(RESYNC, REQUEST);
+    case kRuleResultContinue:
+      active = !active;
+      tx = &tx_frame[active];
+      state.retransmits = 0;
+      state.errors = 0;
+      continue;
+    case kRuleResultHardFail:
+      ese_set_error(ese, TEQ1_ERROR_HARD_FAIL);
+      return 0;
+    case kRuleResultAbort:
+      ese_set_error(ese, TEQ1_ERROR_ABORT);
+      return 0;
+    case kRuleResultSingleShot:
+      /*
+       * Send the next_tx on loop, but tell the rule engine that
+       * the last sent state hasn't changed. This allows for easy error
+       * and supervisory block paths without nesting state.
+       */
+      tx = &tx_frame[!active];
+      continue;
+    case kRuleResultResetDevice:
+      if (was_reset || !ese->ops->hw_reset || ese->ops->hw_reset(ese) == -1) {
+        ese_set_error(ese, TEQ1_ERROR_DEVICE_RESET);
+        return 0; /* Don't keep resetting -- hard fail. */
+      }
+      was_reset = 1;
+    /* 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));
+      /* Load initial I-block. */
+      tx->header.PCB.val = I(0, 0);
+      teq1_fill_info_block(&state, tx);
+      continue;
+    }
+  }
+  /* Return the number of bytes used in rx_buf. */
+  return rx_len - state.app_data.rx_len;
+}
+
+API uint8_t teq1_compute_LRC(const struct Teq1Frame *frame) {
+  uint8_t lrc = 0;
+  const uint8_t *buffer = frame->val;
+  const uint8_t *end = buffer + frame->header.LEN + sizeof(frame->header);
+  while (buffer < end) {
+    lrc ^= *buffer++;
+  }
+  return lrc;
+}
diff --git a/libese-teq1/teq1_private.h b/libese-teq1/teq1_private.h
new file mode 100644
index 0000000..3151753
--- /dev/null
+++ b/libese-teq1/teq1_private.h
@@ -0,0 +1,121 @@
+/*
+ * 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_TEQ1_PRIVATE_H__
+#define ESE_TEQ1_PRIVATE_H__ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Set visibility for exported functions. */
+#ifndef API
+#define API __attribute__ ((visibility("default")))
+#endif  /* API */
+
+/*
+ * Enable T=1 format to reduce to case integers.
+ * Ensure there are tests to map TEQ1_X() to the shorthand below.
+ */
+#define I(S, M) I_##S##_##M
+#define I_0_0 0
+#define I_0_1 32
+#define I_1_0 64
+#define I_1_1 96
+#define R(S, O, P) R_ ## S ##_## O ##_## P
+#define R_0_0_0 128
+#define R_0_0_1 129
+#define R_0_1_0 130
+#define R_0_1_1 131
+#define R_1_0_0 144
+#define R_1_0_1 145
+#define R_1_1_0 146
+#define R_1_1_1 147
+
+#define _S(x) x
+#define S(N, R) S_##N##_##R
+#define S_RESYNC_REQUEST 192
+#define S_IFS_REQUEST 193
+#define S_ABORT_REQUEST 194
+#define S_WTX_REQUEST 195
+#define S_RESYNC_RESPONSE 224
+#define S_IFS_RESPONSE 225
+#define S_ABORT_RESPONSE 226
+#define S_WTX_RESPONSE 227
+
+#define TEQ1_RULE(TX, RX) (((TX & 255) << 8)|(RX & 255))
+
+struct Teq1State {
+  uint8_t wait_mult;
+  uint8_t ifs;
+  uint8_t errors;
+  int retransmits;
+  const char *last_error_message;
+  struct Teq1CardState *card_state;
+  struct {
+    uint8_t *tx_buf;
+    size_t tx_len;
+    uint8_t *rx_buf;
+    size_t rx_len;
+  } app_data;
+};
+
+#define TEQ1_INIT_STATE(TX_BUF, TX_LEN, RX_BUF, RX_LEN, CSTATE) \
+  { \
+    .wait_mult = 1, \
+    .ifs = IFSC, \
+    .errors = 0, \
+    .retransmits = 0, \
+    .last_error_message = NULL, \
+    .card_state = (CSTATE), \
+    .app_data = { \
+      .tx_buf = (uint8_t *const)(TX_BUF), \
+      .tx_len = (TX_LEN), \
+      .rx_buf = (RX_BUF), \
+      .rx_len = (RX_LEN), \
+    }, \
+  }
+
+enum RuleResult {
+  kRuleResultComplete,
+  kRuleResultAbort,
+  kRuleResultContinue,
+  kRuleResultHardFail,
+  kRuleResultResetDevice,
+  kRuleResultResetSession,
+  kRuleResultRetransmit,
+  kRuleResultSingleShot,
+};
+
+
+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);
+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,
+                               struct Teq1Frame *tx_frame,
+                               struct Teq1Frame *rx_frame);
+enum RuleResult teq1_rules(struct Teq1State *state,
+                           struct Teq1Frame *tx_frame,
+                           struct Teq1Frame *rx_frame,
+                           struct Teq1Frame *next_tx);
+
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* ESE_TEQ1_PRIVATE_H__ */
diff --git a/libese-teq1/tests/Android.bp b/libese-teq1/tests/Android.bp
new file mode 100644
index 0000000..9cb2fb4
--- /dev/null
+++ b/libese-teq1/tests/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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_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,
+}
diff --git a/libese-teq1/tests/teq1_unittests.cpp b/libese-teq1/tests/teq1_unittests.cpp
new file mode 100644
index 0000000..77118fa
--- /dev/null
+++ b/libese-teq1/tests/teq1_unittests.cpp
@@ -0,0 +1,365 @@
+/*
+ * 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.
+ *
+ * Tests a very simple end to end T=1 using the echo backend.
+ */
+
+#include <string.h>
+
+#include <vector>
+#include <gtest/gtest.h>
+
+#include <ese/ese.h>
+#include <ese/teq1.h>
+#define LOG_TAG "TEQ1_UNITTESTS"
+#include <ese/log.h>
+
+#include "teq1_private.h"
+
+ESE_INCLUDE_HW(ESE_HW_FAKE);
+
+using ::testing::Test;
+
+// TODO:
+// - 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 {
+ public:
+  Teq1FrameErrorCheck() { }
+  virtual ~Teq1FrameErrorCheck() { }
+  struct Teq1Frame tx_frame_, rx_frame_;
+  struct Teq1State state_;
+  struct Teq1CardState card_state_;
+};
+
+TEST_F(Teq1FrameErrorCheck, info_parity) {
+  static const uint8_t kRxPCBs[] = {
+    TEQ1_I(0, 0),
+    TEQ1_I(1, 0),
+    TEQ1_I(0, 1),
+    TEQ1_I(1, 1),
+    255,
+  };
+  const uint8_t *pcb = &kRxPCBs[0];
+  /* 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);
+  state_.card_state = &card_state_;
+  state_.card_state->seq.card = 1;
+  while (*pcb != 255) {
+    rx_frame_.header.PCB.val = *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);
+    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;
+    EXPECT_EQ(TEQ1_R(0, 0, 1), teq1_frame_error_check(&state_, &tx_frame_, &rx_frame_));
+    state_.card_state->seq.card = !state_.card_state->seq.card;
+    pcb++;
+  }
+};
+
+TEST_F(Teq1FrameErrorCheck, length_mismatch) {
+};
+
+TEST_F(Teq1FrameErrorCheck, unchained_r_block) {
+};
+
+TEST_F(Teq1FrameErrorCheck, unexpected_seq) {
+};
+
+class Teq1RulesTest : public virtual Test {
+ public:
+  Teq1RulesTest() :
+    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_)) {
+    memset(&tx_frame_, 0, sizeof(struct Teq1Frame));
+    memset(&tx_next_, 0, sizeof(struct Teq1Frame));
+    memset(&rx_frame_, 0, sizeof(struct Teq1Frame));
+  }
+  virtual ~Teq1RulesTest() { }
+  virtual void SetUp() {}
+  virtual void TearDown() { }
+
+  struct Teq1Frame tx_frame_;
+  struct Teq1Frame tx_next_;
+  struct Teq1Frame rx_frame_;
+  std::vector<uint8_t> tx_data_;
+  std::vector<uint8_t> rx_data_;
+  struct Teq1CardState card_state_;
+  struct Teq1State state_;
+};
+
+class Teq1ErrorFreeTest : public Teq1RulesTest {
+};
+
+class Teq1ErrorHandlingTest : public Teq1RulesTest {
+};
+
+class Teq1CompleteTest : public Teq1ErrorFreeTest {
+ public:
+  virtual void SetUp() {
+    tx_frame_.header.PCB.val = 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.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.
+    memcpy(rx_frame_.INF, tx_data_.data(), INF_LEN);
+    rx_frame_.INF[INF_LEN] = teq1_compute_LRC(&rx_frame_);
+  }
+
+  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);
+
+    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(kRuleResultComplete, result)
+     << "Actual result name: " << teq1_rule_result_to_name(result);
+  }
+};
+
+TEST_F(Teq1CompleteTest, I00_I00_empty) {
+  // No data.
+  state_.app_data.tx_len = 0;
+  state_.app_data.rx_len = 0;
+  // Re-zero the prepared frames.
+  teq1_fill_info_block(&state_, &tx_frame_);
+  rx_frame_.header.LEN = 0;
+  rx_frame_.INF[0] = teq1_compute_LRC(&rx_frame_);
+  RunRules();
+  EXPECT_EQ(0U, rx_frame_.header.LEN);
+};
+
+TEST_F(Teq1CompleteTest, I00_I00_data) {
+  RunRules();
+  // Ensure that the rx_frame data was copied out to rx_data.
+  EXPECT_EQ(0UL, state_.app_data.rx_len);
+  EXPECT_EQ(tx_data_, rx_data_);
+};
+
+TEST_F(Teq1CompleteTest, I10_I10_data) {
+  tx_frame_.header.PCB.val = TEQ1_I(1, 0);
+  rx_frame_.header.PCB.val = 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.
+  EXPECT_EQ(INF_LEN, rx_frame_.header.LEN);
+  EXPECT_EQ(0UL, state_.app_data.rx_len);
+  EXPECT_EQ(tx_data_, rx_data_);
+};
+
+// 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);
+  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.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);
+
+  enum RuleResult result = teq1_rules(&state_,  &tx_frame_, &rx_frame_, &tx_next_);
+  teq1_trace_transmit(tx_next_.header.PCB.val, 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(state_.wait_mult, 2);
+  EXPECT_EQ(state_.wait_mult, rx_frame_.INF[0]);
+  // Ensure the next call will use the original TX frame.
+  EXPECT_EQ(kRuleResultSingleShot, result)
+   << "Actual result name: " << teq1_rule_result_to_name(result);
+};
+
+class Teq1ErrorFreeChainingTest : public Teq1ErrorFreeTest {
+ public:
+  virtual void RunRules() {
+    state_.app_data.tx_len = oversized_data_len_;
+    tx_data_.resize(oversized_data_len_, 'C');
+    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);
+    // Check that the tx_data was fully consumed.
+    EXPECT_EQ(static_cast<size_t>(oversized_data_len_ - INF_LEN),
+              state_.app_data.tx_len);
+    // No one is checking the TX LRC since there is no card present.
+
+    rx_frame_.header.LEN = 0;
+    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);
+
+    enum RuleResult result = teq1_rules(&state_,  &tx_frame_, &rx_frame_, &tx_next_);
+    teq1_trace_transmit(tx_next_.header.PCB.val, tx_next_.header.LEN);
+    EXPECT_EQ(0, state_.errors);
+    EXPECT_EQ(NULL,  state_.last_error_message)
+      << "Last error: " << state_.last_error_message;
+    EXPECT_EQ(kRuleResultContinue, result)
+      << "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)),
+              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);
+  }
+  int oversized_data_len_;
+};
+
+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);
+  RunRules();
+  EXPECT_EQ(TEQ1_I(1, 1), tx_next_.header.PCB.val)
+    << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+};
+
+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);
+  RunRules();
+  EXPECT_EQ(TEQ1_I(0, 1), tx_next_.header.PCB.val)
+    << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+};
+
+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);
+  RunRules();
+  EXPECT_EQ(TEQ1_I(0, 0), tx_next_.header.PCB.val)
+    << "Actual next TX: " << teq1_pcb_to_name(tx_next_.header.PCB.val);
+};
+
+//
+// Error handling tests
+//
+//
+
+class Teq1Retransmit : public Teq1ErrorHandlingTest {
+ public:
+  virtual void SetUp() {
+    // No data.
+    state_.app_data.rx_len = 0;
+    state_.app_data.tx_len = 0;
+
+    tx_frame_.header.PCB.val = 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_.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);
+
+    enum RuleResult result = teq1_rules(&state_,  &tx_frame_, &rx_frame_, &tx_next_);
+    // Not counted as an error as it was on the card-side.
+    EXPECT_EQ(0, state_.errors);
+    const char *kNull = NULL;
+    EXPECT_EQ(kNull, state_.last_error_message) << state_.last_error_message;
+    EXPECT_EQ(kRuleResultRetransmit, result)
+     << "Actual result name: " << teq1_rule_result_to_name(result);
+  }
+};
+
+TEST_F(Teq1Retransmit, I00_R000_I00) {
+  rx_frame_.header.PCB.val = 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_.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_.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_.INF[0] = teq1_compute_LRC(&rx_frame_);
+}
+
+TEST_F(Teq1ErrorHandlingTest, I00_I00_bad_lrc) {
+  // No data.
+  state_.app_data.rx_len = 0;
+  state_.app_data.tx_len = 0;
+
+  tx_frame_.header.PCB.val = 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.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);
+
+  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(kRuleResultSingleShot, result)
+   << "Actual result name: " << teq1_rule_result_to_name(result);
+};
+
+
diff --git a/libese/Android.bp b/libese/Android.bp
new file mode 100644
index 0000000..cfc7512
--- /dev/null
+++ b/libese/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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",
+    host_supported: true,
+
+    srcs: [
+        "ese.c",
+    ],
+
+    local_include_dirs: [
+       "include",
+    ],
+
+    // Ensure that only explicitly exported symbols are visible.
+    cflags: ["-fvisibility=internal"],
+    debug:  {
+        cflags: ["-DLOG_NDEBUG=0"],
+     },
+    target: {
+        android: {
+        },
+
+        darwin: {
+           enabled: false,
+        },
+
+        linux: {
+        },
+
+        windows: {
+          enabled: false,
+        },
+    },
+
+    shared_libs: ["liblog"],
+    export_include_dirs: ["include"],
+}
+
+subdirs = ["tests"]
diff --git a/libese/ese.c b/libese/ese.c
new file mode 100644
index 0000000..1e49e22
--- /dev/null
+++ b/libese/ese.c
@@ -0,0 +1,110 @@
+/*
+ * 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 <ese/ese.h>
+#include <ese/log.h>
+
+#include "ese_private.h"
+
+static const char kUnknownHw[] = "unknown hw";
+static const char kNullEse[] = "NULL EseInterface";
+static const char *kEseErrorMessages[] = {
+    "Hardware supplied no transceive implementation.",
+    "Timed out polling for value.",
+};
+#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)
+    return kNullEse;
+  if (ese->ops->name)
+    return ese->ops->name;
+  return kUnknownHw;
+}
+
+API int ese_open(struct EseInterface *ese, void *hw_opts) {
+  if (!ese)
+    return -1;
+  ALOGV("opening interface '%s'", ese_name(ese));
+  if (ese->ops->open)
+    return ese->ops->open(ese, hw_opts);
+  return 0;
+}
+
+API const char *ese_error_message(struct EseInterface *ese) {
+  return ese->error.message;
+}
+
+API int ese_error_code(struct EseInterface *ese) { return ese->error.code; }
+
+API int ese_error(struct EseInterface *ese) { return ese->error.is_err; }
+
+API void ese_set_error(struct EseInterface *ese, int code) {
+  if (!ese)
+    return;
+  /* Negative values are reserved for API wide messages. */
+  ese->error.code = code;
+  ese->error.is_err = 1;
+  if (code < 0) {
+    code = -(code + 1); /* Start at 0. */
+    if ((size_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) {
+    LOG_ALWAYS_FATAL("Unknown hw error code passed to ese_set_error(%d)", code);
+  }
+  ese->error.message = ese->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)
+    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;
+}
+
+API int ese_close(struct EseInterface *ese) {
+  if (!ese)
+    return -1;
+  ALOGV("closing interface '%s'", ese_name(ese));
+  if (!ese->ops->close)
+    return 0;
+  return ese->ops->close(ese);
+}
diff --git a/libese/ese_private.h b/libese/ese_private.h
new file mode 100644
index 0000000..eb0e805
--- /dev/null
+++ b/libese/ese_private.h
@@ -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.
+ */
+
+#ifndef ESE_PRIVATE_H_
+#define ESE_PRIVATE_H_ 1
+
+/*
+ * All exported functions should be tagged such that we can avoid namespace
+ * collision trivially as a shared library.
+ */
+#define API __attribute__ ((visibility("default")))
+
+#endif  /* ESE_PRIVATE_H_ */
diff --git a/libese/include/ese/ese.h b/libese/include/ese/ese.h
new file mode 100644
index 0000000..39827ed
--- /dev/null
+++ b/libese/include/ese/ese.h
@@ -0,0 +1,87 @@
+/*
+ * 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_H_
+#define ESE_H_ 1
+
+#include <stdint.h>
+#include <stdint.h>
+
+#include "ese_hw_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 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 ;).
+ */
+struct EseInterface;
+#define ese_init(ese_ptr, HW_TYPE)  __ese_init(ese_ptr, HW_TYPE)
+#define ESE_DECLARE(name, HW_TYPE, ...) \
+  struct EseInterface name = __ESE_INTIALIZER(HW_TYPE)
+#define ESE_INITIALIZER __ESE_INITIALIZER
+#define ESE_INCLUDE_HW __ESE_INCLUDE_HW
+
+const char *ese_name(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);
+
+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);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* ESE_H_ */
diff --git a/libese/include/ese/ese_hw_api.h b/libese/include/ese/ese_hw_api.h
new file mode 100644
index 0000000..dd76391
--- /dev/null
+++ b/libese/include/ese/ese_hw_api.h
@@ -0,0 +1,130 @@
+/*
+ * 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_API_H_
+#define ESE_HW_API_H_ 1
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Pulls the hardware declarations in to scope for the current file
+ * 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
+
+
+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.
+ *
+ * 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.
+ */
+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 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. */
+typedef int (ese_poll_op_t)(struct EseInterface *, uint8_t, float, int);
+typedef int (ese_open_op_t)(struct EseInterface *, void *);
+typedef int (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; \
+}
+
+struct EseOperations {
+  const char *const name;
+  /* Used to prepare any implementation specific internal data and
+   * state needed for robust communication.
+   */
+  ese_open_op_t *const open;
+  /* Used to receive raw data from the ese. */
+  ese_hw_receive_op_t *const hw_receive;
+  /* Used to transmit raw data to the ese. */
+  ese_hw_transmit_op_t *const hw_transmit;
+  /* Used to perform a power reset on the device. */
+  ese_hw_reset_op_t *const hw_reset;
+  /* Wire-specific protocol polling for readiness. */
+  ese_poll_op_t *const poll;
+  /* Wire-specific protocol for transmitting and receiving
+   * application data to the eSE. By default, this may point to
+   * a generic implementation, like teq1_transceive, which uses
+   * the hw_* ops above.
+   */
+  ese_transceive_op_t *const transceive;
+  /* Cleans up any required state: file descriptors or heap allocations. */
+  ese_close_op_t *const close;
+
+  /* Operational options */
+  const void *const opts;
+};
+
+/* Maximum private stack storage on the interface instance. */
+#define ESE_INTERFACE_STATE_PAD 16
+struct EseInterface {
+  const struct EseOperations * ops;
+  struct {
+    int 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];
+};
+
+
+#define ESE_DEFINE_HW_ERRORS(name, ary) \
+  const char ** name##_errors = (ary); \
+  const size_t name##_errors_count = (sizeof(ary) / sizeof((ary)[0]))
+
+#define ESE_DEFINE_HW_OPS(name, obj) \
+  const struct EseOperations * name##_ops = &obj
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+#endif  /* ESE_HW_API_H_ */
diff --git a/libese/include/ese/log.h b/libese/include/ese/log.h
new file mode 100644
index 0000000..150c562
--- /dev/null
+++ b/libese/include/ese/log.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * Android logging wrapper to make it easy to interchange based on environment.
+ */
+
+#ifndef ESE_LOG_H_
+#define ESE_LOG_H_ 1
+
+#if defined(ESE_LOG_NONE) || defined(ESE_LOG_STDIO)
+#  define ESE_LOG_ANDROID 0
+#endif
+
+#if !defined(ESE_LOG_ANDROID)
+#define ESE_LOG_ANDROID 1
+#endif
+
+#if !defined(LOG_TAG)
+#  define LOG_TAG "libese"
+#endif
+
+#if ESE_LOG_ANDROID == 1
+
+#include <log/log.h>
+
+#else  /* ESE_LOG_ANDROID */
+
+#ifndef ALOGV
+#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#if LOG_NDEBUG
+#define ALOGV(...) do { if (0) { __ALOGV(__VA_ARGS__); } } while (0)
+#else
+#define ALOGV(...) __ALOGV(__VA_ARGS__)
+#endif
+#endif
+
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#if defined(ESE_LOG_STDIO)
+#include <stdio.h>
+#include <stdlib.h>
+#define ALOG(priority, tag, format, ...) \
+  fprintf(stderr, "[%s: %s] " format "\n", #priority, tag, ##__VA_ARGS__)
+#define LOG_ALWAYS_FATAL(format, ...) { \
+  ALOG(LOG_ERROR, LOG_TAG, format, ##__VA_ARGS__);  \
+  abort(); \
+}
+
+#elif defined(ESE_LOG_NONE)
+  #define ALOG(...) {}
+  #define LOG_ALWAYS_FATAL(...) while (1);
+#endif
+
+#endif  /* !ESE_LOG_ANDROID */
+
+#endif  /* ESE_LOG_H_ */
diff --git a/libese/tests/Android.bp b/libese/tests/Android.bp
new file mode 100644
index 0000000..29abee0
--- /dev/null
+++ b/libese/tests/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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_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"],
+    host_supported: true,
+    shared_libs: test_libraries,
+}
diff --git a/libese/tests/ese_unittests.cpp b/libese/tests/ese_unittests.cpp
new file mode 100644
index 0000000..dcfc7f8
--- /dev/null
+++ b/libese/tests/ese_unittests.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <ese/ese.h>
+#include <gtest/gtest.h>
+
+ESE_INCLUDE_HW(ESE_HW_FAKE);
+
+using ::testing::Test;
+
+class EseInterfaceTest : public virtual Test {
+ public:
+  EseInterfaceTest() : ese_(ESE_INITIALIZER(ESE_HW_FAKE)) {
+  }
+  virtual ~EseInterfaceTest() { }
+  virtual void SetUp() { }
+  virtual void TearDown() { }
+  struct EseInterface ese_;
+};
+
+TEST_F(EseInterfaceTest, EseNameNull) {
+  EXPECT_STREQ("NULL EseInterface", ese_name(NULL));
+};
+
+TEST_F(EseInterfaceTest, EseNameOk) {
+  EXPECT_STREQ("eSE Fake Hardware", ese_name(&ese_));
+};
+
+TEST_F(EseInterfaceTest, EseNameUnknown) {
+  struct EseOperations dummy_ops = {
+    .name = NULL,
+  };
+  struct EseInterface dummy = {
+    .ops = &dummy_ops
+  };
+  EXPECT_STREQ("unknown hw", ese_name(&dummy));
+};
+
+TEST_F(EseInterfaceTest, EseOpenNull) {
+  EXPECT_EQ(-1, ese_open(NULL, NULL));
+};
+
+TEST_F(EseInterfaceTest, EseOpenNoOp) {
+  struct EseOperations dummy_ops = {
+    .open = NULL,
+  };
+  struct EseInterface dummy = {
+    .ops = &dummy_ops
+  };
+  EXPECT_EQ(0, ese_open(&dummy, NULL));
+};
+
+TEST_F(EseInterfaceTest, EseOpenOk) {
+  EXPECT_EQ(0, ese_open(&ese_, NULL));
+};
+
+TEST_F(EseInterfaceTest, EseCloseNull) {
+  EXPECT_EQ(-1, ese_close(NULL));
+};
+
+TEST_F(EseInterfaceTest, EseCloseNoOp) {
+  struct EseOperations dummy_ops = {
+    .close = NULL,
+  };
+  struct EseInterface dummy = {
+    .ops = &dummy_ops
+  };
+  /* Will pass without an open first. */
+  EXPECT_EQ(0, ese_close(&dummy));
+};
+
+TEST_F(EseInterfaceTest, EseCloseOk) {
+  EXPECT_EQ(0, ese_open(&ese_, NULL));
+  EXPECT_EQ(0, 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_));
+  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_));
+  EXPECT_EQ(0, ese_open(&ese_, NULL));
+  ese_.ops->hw_receive(&ese_, NULL, 0, 1);
+  EXPECT_EQ(0, 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_));
+};
+
+TEST_F(EseInterfaceTest, EseTransceiveNoOps) {
+  struct EseOperations dummy_ops = {
+    .open = NULL,
+    .close = NULL,
+    .transceive = NULL,
+    .hw_transmit = NULL,
+    .hw_receive = NULL,
+  };
+  struct EseInterface dummy = {
+    .ops = &dummy_ops
+  };
+  /* Will pass without an open first. */
+  EXPECT_EQ(-1, ese_transceive(&dummy, NULL, 0, NULL, 0));
+  EXPECT_EQ(1, ese_error(&dummy));
+  EXPECT_EQ(-1, ese_error_code(&dummy));
+  EXPECT_STREQ("Hardware supplied no transceive implementation.",
+               ese_error_message(&dummy));
+};
+
+
diff --git a/tools/Android.bp b/tools/Android.bp
new file mode 100644
index 0000000..e6bdfd8
--- /dev/null
+++ b/tools/Android.bp
@@ -0,0 +1,17 @@
+//
+// 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.
+//
+
+subdirs = ["ese_relay"]
diff --git a/tools/ese_relay/Android.bp b/tools/ese_relay/Android.bp
new file mode 100644
index 0000000..40a3eb4
--- /dev/null
+++ b/tools/ese_relay/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+relay_shared_libraries = [
+    "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,
+}
+
+cc_binary {
+    name: "ese-relay-fake",
+    srcs: ["ese_relay.c", "ese_relay_fake.c"],
+    host_supported: true,
+    shared_libs: relay_shared_libraries + ["libese-hw-fake"],
+}
diff --git a/tools/ese_relay/README.md b/tools/ese_relay/README.md
new file mode 100644
index 0000000..28a43bf
--- /dev/null
+++ b/tools/ese_relay/README.md
@@ -0,0 +1,56 @@
+# What?
+
+ese-relay connects libese's functionality to a local abstract socket on
+an Android device.  The primary purpose is to ease development and provision
+with test hardware without bringing up all the development tools needed.
+
+ese-relay uses the same wire protocol as the
+[Virtual Smart Card](http://frankmorgner.github.io/vsmartcard/) project by acting
+as the "viccd" service.  This enables use of any tool that supports
+[pcsc-lite](https://pcsclite.alioth.debian.org/) without any additional
+development.
+
+# Wire protocol
+
+The format is always
+    Ln d0..dn
+Ln is a network byte order 16-bit unsigned integer length of the data.
+d0..dn are uint8_t bytes to tunneled directly to/from the card.
+
+If Ln == 1, it indicates an out of band control message. Supported messages
+are 1:0 - 1:4:
+  * 0: Power off (ignored)
+  * 1: Power on (implemented with a reset)
+  * 3: Reset
+  * 4: ATR - returns a fake ATR
+
+# Prerequisites
+
+  * pcscd is installed on your system.
+  * Build and install https://frankmorgner.github.io/vsmartcard/virtualsmartcard
+  * Configure /etc/reader.d/vpcd as below.
+  * Build ese-relay configured for the hardware in use.
+
+## /etc/reader.conf.d/vpcd:
+
+    FRIENDLYNAME "Virtual PCD"
+    DEVICENAME   localhost:0x1000
+    LIBPATH      /usr/lib/pcsc/drivers/serial/libifdvpcd.so
+    CHANNELID    0x1000
+
+This will cause pcscd to connect to localhost port 4096 on start as per the vsmartcard
+documentation.
+
+# Usage
+## In one terminal, run the service and forward the abstract UNIX socket to a local TCP socket:
+  $ adb shell ese-relay-<hw>
+  $ adb forward tcp:4096 localabstract:ese-relay
+
+## In another terminal, restart pcscd configured with an appropriate reader:
+  $ /etc/init.d/pcscd restart # (Or whatever your init system requires.)
+
+## In yet another terminal, use your preferred* pcsc client:
+
+  $ java -jar gp.jar -info
+
+* https://github.com/martinpaljak/GlobalPlatformPro is used here.
diff --git a/tools/ese_relay/ese_relay.c b/tools/ese_relay/ese_relay.c
new file mode 100644
index 0000000..1bfe82a
--- /dev/null
+++ b/tools/ese_relay/ese_relay.c
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ *
+ * Hack-y server to forward communication with an eSE during development.
+ * See README.md for more information.
+ */
+
+#include <arpa/inet.h>
+#include <linux/un.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#define LOG_TAG "ese-relay"
+#include <ese/ese.h>
+#include <ese/log.h>
+
+extern const uint8_t *kAtr;
+extern size_t kAtrLength;
+extern void *kEseOpenData;
+void ese_relay_init(struct EseInterface *ese);
+
+/*
+ * Aligned with vpcd.h in
+ * https://frankmorgner.github.io/vsmartcard/virtualsmartcard
+ */
+#define CMD_POWER_OFF 0
+#define CMD_POWER_ON 1
+#define CMD_RESET 2
+#define CMD_ATR 4
+
+int setup_socket(const char *name) {
+  int fd;
+  struct sockaddr_un addr;
+
+  memset(&addr, 0, sizeof(struct sockaddr_un));
+  addr.sun_family = AF_UNIX;
+  if (strlen(name) > UNIX_PATH_MAX - 1) {
+    ALOGE("Abstract listener name too long.");
+    return -1;
+  }
+  strncpy(&addr.sun_path[1], name, strlen(name));
+  fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (fd == -1) {
+    ALOGE("Could not open socket.");
+    return fd;
+  }
+  if (bind(fd, (struct sockaddr *)&addr,
+           sizeof(sa_family_t) + strlen(name) + 1) == -1) {
+    ALOGE("Failed to bind to abstract socket name");
+    close(fd);
+    return -1;
+  }
+  return fd;
+}
+
+int main() {
+  int server_fd = setup_socket(LOG_TAG);
+  int client_fd = -1;
+  struct EseInterface ese;
+  ese_relay_init(&ese);
+
+  if (listen(server_fd, 4)) {
+    ALOGE("Failed to listen on socket.");
+    close(server_fd);
+    return -1;
+  }
+
+  while (server_fd) {
+    struct sockaddr client_info;
+    socklen_t client_info_len = (socklen_t)sizeof(&client_info);
+    int client_fd;
+    uint32_t tx_len, data_read;
+    uint32_t rx_len;
+    uint16_t network_tx_len;
+    uint16_t network_rx_len;
+    uint8_t tx_buf[4096];
+    uint8_t rx_buf[4096];
+    int connected = 0;
+
+    if ((client_fd = accept(server_fd, &client_info, &client_info_len)) == -1) {
+      ALOGE("Fatal error accept()ing a client connection.");
+      return -1;
+    }
+    printf("Client connected.\n");
+    connected = 1;
+    if (ese_open(&ese, kEseOpenData)) {
+      ALOGE("Cannot open hw");
+      if (ese_error(&ese))
+        ALOGE("eSE error (%d): %s", ese_error_code(&ese),
+              ese_error_message(&ese));
+      return 1;
+    }
+    printf("eSE is open\n");
+
+    while (connected) {
+      printf("Listening for data from client\n");
+      if (read(client_fd, &network_tx_len, sizeof(network_tx_len)) !=
+          sizeof(network_tx_len)) {
+        ALOGE("Client disconnected.");
+        break;
+      }
+      tx_len = (uint32_t)ntohs(network_tx_len);
+      printf("tx_len: %u\n", tx_len);
+      if (tx_len == 0) {
+        ALOGE("Client had nothing to say. Goodbye.");
+        break;
+      }
+      if (tx_len > sizeof(tx_buf)) {
+        ALOGE("Client payload too large: %u", tx_len);
+        break;
+      }
+      for (data_read = 0; data_read < tx_len;) {
+        printf("Reading payload: %u of %u remaining\n", data_read, tx_len);
+        ssize_t bytes = read(client_fd, tx_buf + data_read, tx_len - data_read);
+        if (bytes < 0) {
+          ALOGE("Client abandoned hope during transmission.");
+          connected = 0;
+          break;
+        }
+        data_read += bytes;
+      }
+      /* Finally, we can transcieve. */
+      if (tx_len) {
+        uint32_t i;
+        printf("Sending %u bytes to card\n", tx_len);
+        printf("TX: ");
+        for (i = 0; i < tx_len; ++i)
+          printf("%.2X ", tx_buf[i]);
+        printf("\n");
+      }
+
+      if (tx_len == 1) { /* Control request */
+        printf("Received a control request: %x\n", tx_buf[0]);
+        rx_len = 0;
+        switch (tx_buf[0]) {
+        case CMD_POWER_OFF:
+          ese.ops->hw_reset(&ese);
+          break;
+        case CMD_POWER_ON:
+          break;
+        case CMD_RESET:
+          ese.ops->hw_reset(&ese);
+          break;
+        case CMD_ATR:
+          /* Send a dummy ATR for another JCOP card */
+          rx_len = kAtrLength;
+          printf("Filling card RX buf with fake ATR (%u)\n", rx_len);
+          memcpy(rx_buf, kAtr, rx_len);
+          printf("Sending back ATR of length %u\n", rx_len);
+          break;
+        default:
+          ALOGE("Unknown control byte seen: %x", tx_buf[0]);
+        }
+        if (!rx_len)
+          continue;
+      } else {
+        rx_len = ese_transceive(&ese, tx_buf, tx_len, rx_buf, sizeof(rx_buf));
+        if (ese_error(&ese)) {
+          ALOGE("An error (%d) occurred: %s", ese_error_code(&ese),
+                ese_error_message(&ese));
+          return -1;
+        }
+      }
+      if (rx_len > 0) {
+        uint32_t i;
+        printf("Read %d bytes from card\n", rx_len);
+        printf("RX: ");
+        for (i = 0; i < rx_len; ++i)
+          printf("%.2X ", rx_buf[i]);
+        printf("\n");
+      }
+
+      /* Send to client */
+      network_rx_len = htons((uint16_t)rx_len);
+      if (write(client_fd, &network_rx_len, sizeof(network_rx_len)) !=
+          sizeof(network_rx_len)) {
+        ALOGE("Client abandoned hope during response size.");
+        break;
+      }
+
+      for (data_read = 0; data_read < rx_len;) {
+        ssize_t bytes =
+            write(client_fd, rx_buf + data_read, rx_len - data_read);
+        if (bytes < 0) {
+          ALOGE("Client abandoned hope during response.");
+          connected = 0;
+          break;
+        }
+        data_read += bytes;
+      }
+      usleep(1000);
+    }
+    close(client_fd);
+    printf("Session ended\n\n");
+    ese_close(&ese);
+  }
+  return 0;
+}
diff --git a/tools/ese_relay/ese_relay_fake.c b/tools/ese_relay/ese_relay_fake.c
new file mode 100644
index 0000000..862e5c5
--- /dev/null
+++ b/tools/ese_relay/ese_relay_fake.c
@@ -0,0 +1,30 @@
+/*
+ * 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 <string.h>
+
+#include <ese/ese.h>
+ESE_INCLUDE_HW(ESE_HW_FAKE);
+
+/* Minimal ATR */
+const uint8_t kAtr[] = {0x00, 0x00};
+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));
+}
diff --git a/tools/ese_relay/ese_relay_pn80t.c b/tools/ese_relay/ese_relay_pn80t.c
new file mode 100644
index 0000000..cb4cd75
--- /dev/null
+++ b/tools/ese_relay/ese_relay_pn80t.c
@@ -0,0 +1,35 @@
+/*
+ * 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>
+#include <ese/hw/nxp/pn80t/boards/hikey-spidev.h>
+ESE_INCLUDE_HW(ESE_HW_NXP_PN80T_SPIDEV);
+
+/* 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 = (void *)(&nxp_boards_hikey_spidev);
+
+void ese_relay_init(struct EseInterface *ese) {
+  ese_init(ese, ESE_HW_NXP_PN80T_SPIDEV);
+}