libese-hw: move transceive to scattergather

This change migrates hardware callbacks for transceive to a
scatter-gather interface, updates the tests, and adds specific tests for
ese_sg.

Additionally, a small amount of code reorganization is included, like
moving API to ESE_API and into sysdeps to reduce some duplicatation as
well as removing dead code (two sysdeps.h).

As the T=1 implementation already copies data during framing, it was
becoming ungainly to recopy data to frame APDUs which will then be
copied only to be reframed.  This is especially true for extended APDUs
as the payloads may be up to 65k.

Change-Id: I37f586645965d32577f8b724dbe41c9041439887
Test: ese_replay still works (open channel, select, close channel)
Bug: 34467857
diff --git a/libese-hw/ese_hw_echo.c b/libese-hw/ese_hw_echo.c
index 264ab2b..a6fe504 100644
--- a/libese-hw/ese_hw_echo.c
+++ b/libese-hw/ese_hw_echo.c
@@ -152,8 +152,9 @@
     .preprocess = &echo_preprocess,
 };
 
-uint32_t echo_transceive(struct EseInterface *ese, const uint8_t *const tx_buf,
-                         uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len) {
+uint32_t echo_transceive(struct EseInterface *ese,
+                         const struct EseSgBuffer *tx_buf, uint32_t tx_len,
+                         struct EseSgBuffer *rx_buf, uint32_t rx_len) {
   return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len);
 }
 
diff --git a/libese-hw/ese_hw_fake.c b/libese-hw/ese_hw_fake.c
index 310ef06..96139ce 100644
--- a/libese-hw/ese_hw_fake.c
+++ b/libese-hw/ese_hw_fake.c
@@ -107,15 +107,26 @@
   return 0;
 }
 
-uint32_t fake_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
-                         uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len) {
+uint32_t fake_transceive(struct EseInterface *ese,
+                         const struct EseSgBuffer *tx_bufs, uint32_t tx_seg,
+                         struct EseSgBuffer *rx_bufs, uint32_t rx_seg) {
   uint32_t processed = 0;
+  uint32_t offset = 0;
+  struct EseSgBuffer *rx_buf = rx_bufs;
+  const struct EseSgBuffer *tx_buf = tx_bufs;
+
   if (!ese->pad[0] || !ese->pad[1]) {
     ese_set_error(ese, kEseFakeHwErrorTranscieveWhileBusy);
     return 0;
   }
-  while (processed < tx_len) {
-    uint32_t sent = fake_transmit(ese, tx_buf, tx_len, 0);
+  while (tx_buf < tx_bufs + tx_seg) {
+    uint32_t sent =
+        fake_transmit(ese, tx_buf->base + offset, tx_buf->len - offset, 0);
+    if (sent != tx_buf->len - offset) {
+      offset = tx_buf->len - sent;
+      processed += sent;
+      continue;
+    }
     if (sent == 0) {
       if (ese_error(ese)) {
         return 0;
@@ -123,6 +134,8 @@
       ese_set_error(ese, kEseFakeHwErrorEmptyTransmit);
       return 0;
     }
+    tx_buf++;
+    offset = 0;
     processed += sent;
   }
   fake_transmit(ese, NULL, 0, 1); /* Complete. */
@@ -130,8 +143,11 @@
     ese_set_error(ese, kEseGlobalErrorPollTimedOut);
     return 0;
   }
-  /* A real implementation would have protocol errors to contend with. */
-  processed = fake_receive(ese, rx_buf, rx_len, 1);
+  /* This reads the full RX buffer rather than a protocol specified amount. */
+  processed = 0;
+  while (rx_buf < rx_bufs + rx_seg) {
+    processed += fake_receive(ese, rx_buf->base, rx_buf->len, 1);
+  }
   return processed;
 }
 
diff --git a/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h b/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h
index 452d5ad..839983f 100644
--- a/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h
+++ b/libese-hw/nxp/include/ese/hw/nxp/pn80t/common.h
@@ -33,8 +33,8 @@
 
 void nxp_pn80t_close(struct EseInterface *ese);
 uint32_t nxp_pn80t_transceive(struct EseInterface *ese,
-                              const uint8_t *const tx_buf, uint32_t tx_len,
-                              uint8_t *rx_buf, uint32_t rx_len);
+                              const struct EseSgBuffer *tx_buf, uint32_t tx_len,
+                              struct EseSgBuffer *rx_buf, uint32_t rx_len);
 int nxp_pn80t_poll(struct EseInterface *ese, uint8_t poll_for, float timeout,
                    int complete);
 int nxp_pn80t_reset(struct EseInterface *ese);
diff --git a/libese-hw/nxp/pn80t/common.c b/libese-hw/nxp/pn80t/common.c
index ce8d1d2..eb33182 100644
--- a/libese-hw/nxp/pn80t/common.c
+++ b/libese-hw/nxp/pn80t/common.c
@@ -148,8 +148,8 @@
 }
 
 uint32_t nxp_pn80t_transceive(struct EseInterface *ese,
-                              const uint8_t *const tx_buf, uint32_t tx_len,
-                              uint8_t *rx_buf, uint32_t rx_len) {
+                              const struct EseSgBuffer *tx_buf, uint32_t tx_len,
+                              struct EseSgBuffer *rx_buf, uint32_t rx_len) {
   return teq1_transceive(ese, &kTeq1Options, tx_buf, tx_len, rx_buf, rx_len);
 }
 
diff --git a/libese-sysdeps/include/ese/sysdeps.h b/libese-sysdeps/include/ese/sysdeps.h
index 94ebfe5..c829ea8 100644
--- a/libese-sysdeps/include/ese/sysdeps.h
+++ b/libese-sysdeps/include/ese/sysdeps.h
@@ -19,10 +19,22 @@
 #include <stdbool.h> /* bool */
 #include <stdint.h> /* uint*_t */
 
+#define ESE_UINT32_MAX (UINT32_MAX)
+
 #ifndef NULL
 #define NULL ((void *)(0))
 #endif
 
+/* Set visibility for exported functions. */
+#ifndef ESE_API
+#define ESE_API __attribute__ ((visibility("default")))
+#endif  /* ESE_API */
+
+/* Mimic C11 _Static_assert behavior for a C99 world. */
+#ifndef _static_assert
+#define _static_assert(what, why) { while (!(1 / (!!(what)))); }
+#endif
+
 extern void *ese_memcpy(void *__dest, const void *__src, uint64_t __n);
 extern void *ese_memset(void *__s, int __c, uint64_t __n);
 
diff --git a/libese-teq1/include/ese/teq1.h b/libese-teq1/include/ese/teq1.h
index c4cad9b..d5d6ce9 100644
--- a/libese-teq1/include/ese/teq1.h
+++ b/libese-teq1/include/ese/teq1.h
@@ -189,8 +189,8 @@
 
 uint32_t teq1_transceive(struct EseInterface *ese,
                          const struct Teq1ProtocolOptions *opts,
-                         const uint8_t *const tx_buf, uint32_t tx_len,
-                         uint8_t *rx_buf, uint32_t rx_max);
+                         const struct EseSgBuffer *tx_bufs, uint8_t tx_segs,
+                         struct EseSgBuffer *rx_bufs, uint8_t rx_segs);
 
 uint8_t teq1_compute_LRC(const struct Teq1Frame *frame);
 
diff --git a/libese-teq1/teq1.c b/libese-teq1/teq1.c
index 3c45f61..be0011c 100644
--- a/libese-teq1/teq1.c
+++ b/libese-teq1/teq1.c
@@ -185,20 +185,29 @@
   switch (bs_get(PCB.type, frame->header.PCB)) {
   case kPcbTypeInfo0:
   case kPcbTypeInfo1: {
-    uint32_t len = state->app_data.tx_len;
+    uint32_t len = state->app_data.tx_total;
+    uint32_t copied = 0;
     if (len > inf_len) {
       len = inf_len;
     }
-    ese_memcpy(frame->INF, state->app_data.tx_buf, len);
+    copied = ese_sg_to_buf(state->app_data.tx, state->app_data.tx_count,
+                           state->app_data.tx_offset, len, frame->INF);
+    if (copied != len) {
+      ALOGE("Failed to copy %x bytes of app data for transmission",
+            frame->header.LEN);
+      /* TODO(wad): This return code is largely ignored. Is the precondition
+       * checking elsewhere enough? */
+      return 255;
+    }
     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;
+    state->app_data.tx_offset += copied;
+    state->app_data.tx_total -= copied;
     /* Perform chained transmission if needed. */
     bs_assign(&frame->header.PCB, PCB.I.more_data, 0);
-    if (state->app_data.tx_len > 0) {
+    if (state->app_data.tx_total > 0) {
       frame->header.PCB |= bs_mask(PCB.I.more_data, 1);
     }
     return len;
@@ -211,21 +220,22 @@
   return 255; /* Invalid block type. */
 }
 
-void teq1_get_app_data(struct Teq1State *state, struct Teq1Frame *frame) {
+void teq1_get_app_data(struct Teq1State *state, const struct Teq1Frame *frame) {
   switch (bs_get(PCB.type, frame->header.PCB)) {
   case kPcbTypeInfo0:
   case kPcbTypeInfo1: {
-    uint8_t len = frame->header.LEN;
+    uint32_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;
+    if (len > state->app_data.rx_total) {
+      len = state->app_data.rx_total;
     }
-    ese_memcpy(state->app_data.rx_buf, frame->INF, len);
+    ese_sg_from_buf(state->app_data.rx, state->app_data.rx_count,
+                    state->app_data.rx_offset, len, frame->INF);
     /* 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;
+    state->app_data.rx_total -= len;
+    state->app_data.rx_offset += len;
     return;
   }
   case kPcbTypeReceiveReady:
@@ -542,11 +552,11 @@
  *   teq1_transcieve_init() and teq1_transceive_process_one()
  *   if testing becomes onerous given the loop below.
  */
-
-API uint32_t teq1_transceive(struct EseInterface *ese,
-                             const struct Teq1ProtocolOptions *opts,
-                             const uint8_t *const tx_buf, uint32_t tx_len,
-                             uint8_t *rx_buf, uint32_t rx_len) {
+ESE_API uint32_t teq1_transceive(struct EseInterface *ese,
+                                 const struct Teq1ProtocolOptions *opts,
+                                 const struct EseSgBuffer *tx_bufs,
+                                 uint8_t tx_segs, struct EseSgBuffer *rx_bufs,
+                                 uint8_t rx_segs) {
   struct Teq1Frame tx_frame[2];
   struct Teq1Frame rx_frame;
   struct Teq1Frame *tx = &tx_frame[0];
@@ -554,11 +564,14 @@
   bool was_reset = false;
   bool done = false;
   enum RuleResult result = kRuleResultComplete;
+  uint32_t rx_total = ese_sg_length(rx_bufs, rx_segs);
   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);
+  struct Teq1State init_state = TEQ1_INIT_STATE(
+      tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs,
+      ese_sg_length(rx_bufs, rx_segs), card_state);
+  struct Teq1State state = TEQ1_INIT_STATE(
+      tx_bufs, tx_segs, ese_sg_length(tx_bufs, tx_segs), rx_bufs, rx_segs,
+      ese_sg_length(rx_bufs, rx_segs), card_state);
 
   _static_assert(TEQ1HEADER_SIZE == sizeof(struct Teq1Header),
                  "Ensure compiler alignment/padding matches wire protocol.");
@@ -651,11 +664,11 @@
       continue;
     }
   }
-  /* Return the number of bytes used in rx_buf. */
-  return rx_len - state.app_data.rx_len;
+  /* Return the number of bytes used in the RX buffers. */
+  return rx_total - state.app_data.rx_total;
 }
 
-API uint8_t teq1_compute_LRC(const struct Teq1Frame *frame) {
+ESE_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);
diff --git a/libese-teq1/teq1_private.h b/libese-teq1/teq1_private.h
index 6bd3d3c..4f3f25d 100644
--- a/libese-teq1/teq1_private.h
+++ b/libese-teq1/teq1_private.h
@@ -20,16 +20,6 @@
 extern "C" {
 #endif
 
-/* Set visibility for exported functions. */
-#ifndef API
-#define API __attribute__ ((visibility("default")))
-#endif  /* API */
-
-/* Mimic C11 _Static_assert behavior for a C99 world. */
-#ifndef _static_assert
-#define _static_assert(what, why) { while (!(1 / (!!(what)))); }
-#endif
-
 /*
  * Enable T=1 format to reduce to case integers.
  * Ensure there are tests to map TEQ1_X() to the shorthand below.
@@ -70,14 +60,18 @@
   const char *last_error_message;
   struct Teq1CardState *card_state;
   struct {
-    uint8_t *tx_buf;
-    uint32_t tx_len;
-    uint8_t *rx_buf;
-    uint32_t rx_len;
+    const struct EseSgBuffer *tx;
+    struct EseSgBuffer *rx;
+    uint32_t tx_offset;
+    uint32_t tx_count;
+    uint32_t tx_total;
+    uint32_t rx_offset;
+    uint32_t rx_count;
+    uint32_t rx_total;
   } app_data;
 };
 
-#define TEQ1_INIT_STATE(TX_BUF, TX_LEN, RX_BUF, RX_LEN, CSTATE) \
+#define TEQ1_INIT_STATE(TX_BUFS, TX_LEN, TX_TOTAL_LEN, RX_BUFS, RX_LEN, RX_TOTAL_LEN, CSTATE) \
   { \
     .wait_mult = 1, \
     .ifs = IFSC, \
@@ -86,10 +80,14 @@
     .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), \
+      .tx = (TX_BUFS), \
+      .rx = (RX_BUFS), \
+      .tx_offset = 0, \
+      .tx_count = (TX_LEN), \
+      .tx_total = (TX_TOTAL_LEN), \
+      .rx_offset = 0, \
+      .rx_count = (RX_LEN), \
+      .rx_total = (RX_TOTAL_LEN), \
     }, \
   }
 
@@ -115,7 +113,7 @@
                  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);
+void teq1_get_app_data(struct Teq1State *state, const struct Teq1Frame *frame);
 uint8_t teq1_frame_error_check(struct Teq1State *state,
                                struct Teq1Frame *tx_frame,
                                struct Teq1Frame *rx_frame);
diff --git a/libese-teq1/tests/teq1_unittests.cpp b/libese-teq1/tests/teq1_unittests.cpp
index aef5e0a..8eaaac1 100644
--- a/libese-teq1/tests/teq1_unittests.cpp
+++ b/libese-teq1/tests/teq1_unittests.cpp
@@ -42,6 +42,7 @@
  public:
   Teq1FrameErrorCheck() { }
   virtual ~Teq1FrameErrorCheck() { }
+
   struct Teq1Frame tx_frame_, rx_frame_;
   struct Teq1State state_;
   struct Teq1CardState card_state_;
@@ -92,9 +93,11 @@
   Teq1RulesTest() :
     tx_data_(INF_LEN, 'A'),
     rx_data_(INF_LEN, 'B'),
+    tx_sg_({ .base = tx_data_.data(), .len = INF_LEN }),
+    rx_sg_({ .base = rx_data_.data(), .len = INF_LEN }),
     card_state_({ .seq = { .card = 1, .interface = 1, }, }),
-    state_(TEQ1_INIT_STATE(tx_data_.data(), static_cast<uint32_t>(tx_data_.size()),
-                           rx_data_.data(), static_cast<uint32_t>(rx_data_.size()),
+    state_(TEQ1_INIT_STATE(&tx_sg_, 1, INF_LEN,
+                           &rx_sg_, 1, INF_LEN,
                            &card_state_)) {
     memset(&tx_frame_, 0, sizeof(struct Teq1Frame));
     memset(&tx_next_, 0, sizeof(struct Teq1Frame));
@@ -109,6 +112,8 @@
   struct Teq1Frame rx_frame_;
   std::vector<uint8_t> tx_data_;
   std::vector<uint8_t> rx_data_;
+  struct EseSgBuffer tx_sg_;
+  struct EseSgBuffer rx_sg_;
   struct Teq1CardState card_state_;
   struct Teq1State state_;
 };
@@ -125,7 +130,7 @@
     tx_frame_.header.PCB = TEQ1_I(0, 0);
     teq1_fill_info_block(&state_, &tx_frame_);
     // Check that the tx_data was fully consumed.
-    EXPECT_EQ(0UL, state_.app_data.tx_len);
+    EXPECT_EQ(0UL, state_.app_data.tx_total);
 
     rx_frame_.header.PCB = TEQ1_I(0, 0);
     rx_frame_.header.LEN = INF_LEN;
@@ -153,8 +158,8 @@
 
 TEST_F(Teq1CompleteTest, I00_I00_empty) {
   // No data.
-  state_.app_data.tx_len = 0;
-  state_.app_data.rx_len = 0;
+  state_.app_data.tx_total = 0;
+  state_.app_data.rx_total = 0;
   // Re-zero the prepared frames.
   teq1_fill_info_block(&state_, &tx_frame_);
   rx_frame_.header.LEN = 0;
@@ -166,7 +171,7 @@
 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(0UL, state_.app_data.rx_total);
   EXPECT_EQ(tx_data_, rx_data_);
 };
 
@@ -177,7 +182,7 @@
   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(0UL, state_.app_data.rx_total);
   EXPECT_EQ(tx_data_, rx_data_);
 };
 
@@ -187,7 +192,7 @@
   tx_frame_.header.PCB = TEQ1_I(0, 0);
   teq1_fill_info_block(&state_, &tx_frame_);
   // Check that the tx_data was fully consumed.
-  EXPECT_EQ(0UL, state_.app_data.tx_len);
+  EXPECT_EQ(0UL, state_.app_data.tx_total);
 
   rx_frame_.header.PCB = TEQ1_S_WTX(0);
   rx_frame_.header.LEN = 1;
@@ -216,15 +221,16 @@
 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();
+    const_cast<struct EseSgBuffer *>(state_.app_data.tx)->base = tx_data_.data();
+    const_cast<struct EseSgBuffer *>(state_.app_data.tx)->len = oversized_data_len_;
+    state_.app_data.tx_total = oversized_data_len_;
     teq1_fill_info_block(&state_, &tx_frame_);
     // Ensure More bit was set.
     EXPECT_EQ(1, bs_get(PCB.I.more_data, tx_frame_.header.PCB));
     // Check that the tx_data was fully consumed.
     EXPECT_EQ(static_cast<uint32_t>(oversized_data_len_ - INF_LEN),
-              state_.app_data.tx_len);
+              state_.app_data.tx_total);
     // No one is checking the TX LRC since there is no card present.
 
     rx_frame_.header.LEN = 0;
@@ -244,9 +250,9 @@
     // Check that the tx_buf was drained already for the next frame.
     // ...
     EXPECT_EQ(static_cast<uint32_t>(oversized_data_len_ - (2 * INF_LEN)),
-              state_.app_data.tx_len);
+              state_.app_data.tx_total);
     // Belt and suspenders: make sure no RX buf was used.
-    EXPECT_EQ(rx_data_.size(), state_.app_data.rx_len);
+    EXPECT_EQ(rx_data_.size(), state_.app_data.rx_total);
   }
   int oversized_data_len_;
 };
@@ -287,8 +293,8 @@
  public:
   virtual void SetUp() {
     // No data.
-    state_.app_data.rx_len = 0;
-    state_.app_data.tx_len = 0;
+    state_.app_data.rx_total = 0;
+    state_.app_data.tx_total = 0;
 
     tx_frame_.header.PCB = TEQ1_I(0, 0);
     teq1_fill_info_block(&state_, &tx_frame_);
@@ -336,8 +342,8 @@
 
 TEST_F(Teq1ErrorHandlingTest, I00_I00_bad_lrc) {
   // No data.
-  state_.app_data.rx_len = 0;
-  state_.app_data.tx_len = 0;
+  state_.app_data.rx_total = 0;
+  state_.app_data.tx_total = 0;
 
   tx_frame_.header.PCB = TEQ1_I(0, 0);
   teq1_fill_info_block(&state_, &tx_frame_);
diff --git a/libese/Android.bp b/libese/Android.bp
index b42114d..98ed1d0 100644
--- a/libese/Android.bp
+++ b/libese/Android.bp
@@ -43,6 +43,7 @@
 
     srcs: [
         "ese.c",
+        "ese_sg.c",
     ],
 
     shared_libs: ["libese-sysdeps", "liblog"],
diff --git a/libese/ese.c b/libese/ese.c
index 2a0048f..55eb838 100644
--- a/libese/ese.c
+++ b/libese/ese.c
@@ -17,8 +17,6 @@
 #include "include/ese/ese.h"
 #include "include/ese/log.h"
 
-#include "ese_private.h"
-
 static const char kUnknownHw[] = "unknown hw";
 static const char kNullEse[] = "NULL EseInterface";
 static const char *kEseErrorMessages[] = {
@@ -27,7 +25,7 @@
 };
 #define ESE_MESSAGES(x) (sizeof(x) / sizeof((x)[0]))
 
-API const char *ese_name(const struct EseInterface *ese) {
+ESE_API const char *ese_name(const struct EseInterface *ese) {
   if (!ese) {
     return kNullEse;
   }
@@ -37,7 +35,7 @@
   return kUnknownHw;
 }
 
-API int ese_open(struct EseInterface *ese, void *hw_opts) {
+ESE_API int ese_open(struct EseInterface *ese, void *hw_opts) {
   if (!ese) {
     return -1;
   }
@@ -48,21 +46,23 @@
   return 0;
 }
 
-API const char *ese_error_message(const struct EseInterface *ese) {
+ESE_API const char *ese_error_message(const struct EseInterface *ese) {
   return ese->error.message;
 }
 
-API int ese_error_code(const struct EseInterface *ese) {
+ESE_API int ese_error_code(const struct EseInterface *ese) {
   return ese->error.code;
 }
 
-API bool ese_error(const struct EseInterface *ese) { return ese->error.is_err; }
+ESE_API bool ese_error(const struct EseInterface *ese) {
+  return ese->error.is_err;
+}
 
-API void ese_set_error(struct EseInterface *ese, int code) {
+ESE_API void ese_set_error(struct EseInterface *ese, int code) {
   if (!ese) {
     return;
   }
-  /* Negative values are reserved for API wide messages. */
+  /* Negative values are reserved for ESE_API wide messages. */
   ese->error.code = code;
   ese->error.is_err = true;
   if (code < 0) {
@@ -81,23 +81,28 @@
 }
 
 /* Blocking. */
-API int ese_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
-                       uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_max) {
+ESE_API int ese_transceive(struct EseInterface *ese, const uint8_t *tx_buf,
+                           uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_max) {
+  const struct EseSgBuffer tx = {
+      .c_base = tx_buf, .len = tx_len,
+  };
+  struct EseSgBuffer rx = {
+      .base = rx_buf, .len = rx_max,
+  };
+  return ese_transceive_sg(ese, &tx, 1, &rx, 1);
+}
+
+ESE_API int ese_transceive_sg(struct EseInterface *ese,
+                              const struct EseSgBuffer *tx_bufs,
+                              uint32_t tx_segs, struct EseSgBuffer *rx_bufs,
+                              uint32_t rx_segs) {
   uint32_t recvd = 0;
   if (!ese) {
     return -1;
   }
 
   if (ese->ops->transceive) {
-    recvd = ese->ops->transceive(ese, tx_buf, tx_len, rx_buf, rx_max);
-    return ese_error(ese) ? -1 : recvd;
-  }
-
-  if (ese->ops->hw_transmit && ese->ops->hw_receive) {
-    ese->ops->hw_transmit(ese, tx_buf, tx_len, 1);
-    if (!ese_error(ese)) {
-      recvd = ese->ops->hw_receive(ese, rx_buf, rx_max, 1);
-    }
+    recvd = ese->ops->transceive(ese, tx_bufs, tx_segs, rx_bufs, rx_segs);
     return ese_error(ese) ? -1 : recvd;
   }
 
@@ -105,7 +110,7 @@
   return -1;
 }
 
-API void ese_close(struct EseInterface *ese) {
+ESE_API void ese_close(struct EseInterface *ese) {
   if (!ese) {
     return;
   }
diff --git a/libese/ese_private.h b/libese/ese_private.h
deleted file mode 100644
index eb0e805..0000000
--- a/libese/ese_private.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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/ese_sg.c b/libese/ese_sg.c
new file mode 100644
index 0000000..48acebd
--- /dev/null
+++ b/libese/ese_sg.c
@@ -0,0 +1,85 @@
+/*
+ * 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 "include/ese/ese.h"
+
+static inline uint32_t min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
+
+ESE_API uint32_t ese_sg_length(const struct EseSgBuffer *bufs, uint32_t cnt) {
+  const struct EseSgBuffer *buf = bufs;
+  uint32_t len = 0;
+  if (!bufs || cnt == 0) {
+    return 0;
+  }
+  while (buf < bufs + cnt) {
+    /* In reality, this is probably corruption or a mistake. */
+    if (ESE_UINT32_MAX - len < buf->len) {
+      return ESE_UINT32_MAX;
+    }
+    len += buf->len;
+    buf++;
+  }
+  return len;
+}
+
+ESE_API uint32_t ese_sg_to_buf(const struct EseSgBuffer *src, uint32_t src_cnt,
+                               uint32_t start_at, uint32_t length,
+                               uint8_t *dst) {
+  const struct EseSgBuffer *buf = src;
+  uint32_t remaining = length;
+  if (!src || src_cnt == 0) {
+    return 0;
+  }
+  while (remaining && buf < src + src_cnt) {
+    if (start_at > buf->len) {
+      start_at -= buf->len;
+      buf++;
+      continue;
+    }
+    uint32_t copy_len = min_u32(remaining, buf->len - start_at);
+    ese_memcpy(dst, buf->c_base + start_at, copy_len);
+    dst += copy_len;
+    remaining -= copy_len;
+    start_at = 0;
+    buf++;
+  }
+  return length - remaining;
+}
+
+ESE_API uint32_t ese_sg_from_buf(struct EseSgBuffer *dst, uint32_t dst_cnt,
+                                 uint32_t start_at, uint32_t length,
+                                 const uint8_t *src) {
+  const struct EseSgBuffer *buf = dst;
+  uint32_t remaining = length;
+  if (!dst || dst_cnt == 0) {
+    return 0;
+  }
+
+  while (remaining && buf < dst + dst_cnt) {
+    if (start_at >= buf->len) {
+      start_at -= buf->len;
+      buf++;
+      continue;
+    }
+    uint32_t copy_len = min_u32(remaining, buf->len - start_at);
+    ese_memcpy(buf->base + start_at, src, copy_len);
+    src += copy_len;
+    remaining -= copy_len;
+    start_at = 0;
+    buf++;
+  }
+  return length - remaining;
+}
diff --git a/libese/include/ese/ese.h b/libese/include/ese/ese.h
index f12ed03..69a84ef 100644
--- a/libese/include/ese/ese.h
+++ b/libese/include/ese/ese.h
@@ -17,6 +17,7 @@
 #ifndef ESE_H_
 #define ESE_H_ 1
 
+#include "ese_sg.h"
 #include "ese_hw_api.h"
 #include "../../../libese-sysdeps/include/ese/sysdeps.h"
 
@@ -60,6 +61,7 @@
  * (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)
@@ -70,6 +72,8 @@
 int ese_open(struct EseInterface *ese, void *hw_opts);
 void ese_close(struct EseInterface *ese);
 int ese_transceive(struct EseInterface *ese, const uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_max);
+int ese_transceive_sg(struct EseInterface *ese, const struct EseSgBuffer *tx, uint32_t tx_segs,
+                      struct EseSgBuffer *rx, uint32_t rx_segs);
 
 bool ese_error(const struct EseInterface *ese);
 const char *ese_error_message(const struct EseInterface *ese);
diff --git a/libese/include/ese/ese_hw_api.h b/libese/include/ese/ese_hw_api.h
index 1d23768..3f1abb8 100644
--- a/libese/include/ese/ese_hw_api.h
+++ b/libese/include/ese/ese_hw_api.h
@@ -17,6 +17,7 @@
 #ifndef ESE_HW_API_H_
 #define ESE_HW_API_H_ 1
 
+#include "ese_sg.h"
 #include "../../../libese-sysdeps/include/ese/sysdeps.h"
 
 #ifdef __cplusplus
@@ -64,7 +65,7 @@
  * - int: -1 on error, 0 on success.
  */
 typedef int (ese_hw_reset_op_t)(struct EseInterface *);
-/* ese_transceive_op_t:  fully contained transmission and receive operation.
+/* ese_transceive_sg_op_t:  fully contained transmission and receive operation.
  *
  * Must provide an implementation of the wire protocol necessary to transmit
  * and receive an application payload to and from the eSE.  Normally, this
@@ -73,15 +74,16 @@
  *
  * Args:
  * - struct EseInterface *: session handle.
- * - const uint8_t *: pointer to the buffer to transmit.
- * - uint32_t: length of the data to transmit.
- * - uint8_t *: pointer to the buffer to receive into.
- * - uint32_t: maximum length of the data to receive.
+ * - const EseSgBuffer *: array of buffers to transmit
+ * - uint32_t: number of buffers to send
+ * - const EseSgBuffer *: array of buffers to receive into
+ * - uint32_t: number of buffers to receive to
  *
  * Returns:
  * - uint32_t: bytes received.
  */
-typedef uint32_t (ese_transceive_op_t)(struct EseInterface *, const uint8_t *, uint32_t, uint8_t *, uint32_t);
+typedef uint32_t (ese_transceive_op_t)(
+  struct EseInterface *, const struct EseSgBuffer *, uint32_t, struct EseSgBuffer *, uint32_t);
 /* ese_poll_op_t: waits for the hardware to be ready to send data.
  *
  * Args:
diff --git a/libese/include/ese/ese_sg.h b/libese/include/ese/ese_sg.h
new file mode 100644
index 0000000..025520c
--- /dev/null
+++ b/libese/include/ese/ese_sg.h
@@ -0,0 +1,48 @@
+/*
+ * 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_SG_H_
+#define ESE_SG_H_ 1
+
+#include "../../../libese-sysdeps/include/ese/sysdeps.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Scatter-Gather Buffer */
+struct EseSgBuffer {
+  union {
+     uint8_t *base;
+     const uint8_t *c_base;
+  };
+  uint32_t len;
+};
+
+#define ese_sg_set(ese_sg_ptr, buf, len) \
+  (ese_sg_ptr)->base = buf; \
+  (ese_sg_ptr)->len = len
+
+/* Computes the total length/size of the buffer on each call. */
+uint32_t ese_sg_length(const struct EseSgBuffer *bufs, uint32_t cnt);
+uint32_t ese_sg_to_buf(const struct EseSgBuffer *src, uint32_t src_cnt, uint32_t start_at, uint32_t length, uint8_t *dst);
+uint32_t ese_sg_from_buf(struct EseSgBuffer *dst, uint32_t dst_cnt, uint32_t start_at, uint32_t length, const uint8_t *src);
+
+#ifdef __cplusplus
+}  /* extern "C" */
+#endif
+
+#endif  /* ESE_SG_H_ */
diff --git a/libese/include/ese/sysdeps.h b/libese/include/ese/sysdeps.h
deleted file mode 100644
index 67f91f1..0000000
--- a/libese/include/ese/sysdeps.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef ESE_PLATFORM_SYSDEPS_H__
-#define ESE_PLATFORM_SYSDEPS_H__ 1
-
-#include <stdint.h> /* uint*_t */
-
-extern void *ese_memcpy(void *__dest, const void *__src, uint64_t __n);
-extern void *ese_memset(void *__s, int __c, uint64_t __n);
-
-#endif  /* ESE_PLATFORM_SYSDEPS_H__ */
diff --git a/libese/tests/Android.bp b/libese/tests/Android.bp
index 1239eb2..ce9c009 100644
--- a/libese/tests/Android.bp
+++ b/libese/tests/Android.bp
@@ -16,7 +16,11 @@
 
 cc_test {
     name: "ese_unittests",
-    srcs: ["ese_unittests.cpp", "bitspec_unittests.cpp"],
+    srcs: [
+        "ese_unittests.cpp",
+        "bitspec_unittests.cpp",
+        "sg_unittests.cpp",
+    ],
     host_supported: true,
     shared_libs: [
         "libese",
diff --git a/libese/tests/ese_unittests.cpp b/libese/tests/ese_unittests.cpp
index c62b758..9339742 100644
--- a/libese/tests/ese_unittests.cpp
+++ b/libese/tests/ese_unittests.cpp
@@ -103,7 +103,15 @@
 
 TEST_F(EseInterfaceTest, EseTransceiveSendNothing) {
   EXPECT_EQ(0, ese_open(&ese_, NULL));
-  EXPECT_EQ(0, ese_transceive(&ese_, NULL, 0, NULL, 0));
+  // Relying on hw transmit/recieve alone is not supported.
+  uint8_t *tx = NULL;
+  uint8_t *rx = NULL;
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx, 0, rx, 0));
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx+1, 0, rx, 0));
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx, 0, rx+1, 0));
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx, 0, rx, 100));
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx, 220, rx, 100));
+  EXPECT_EQ(-1, ese_transceive(&ese_, tx+1, 0, rx+1, 0));
   ese_close(&ese_);
 };
 
diff --git a/libese/tests/sg_unittests.cpp b/libese/tests/sg_unittests.cpp
new file mode 100644
index 0000000..82f394a
--- /dev/null
+++ b/libese/tests/sg_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 <stdint.h>
+
+#include <ese/ese_sg.h>
+#include <gtest/gtest.h>
+
+using ::testing::Test;
+
+class ScatterGatherTest : public virtual Test {
+ public:
+  ScatterGatherTest() {
+  }
+  virtual ~ScatterGatherTest() { }
+  virtual void SetUp() { }
+  virtual void TearDown() { }
+};
+
+TEST_F(ScatterGatherTest, OneToBuf) {
+  uint8_t sg_data[] = "HELLO WORLD";
+  struct EseSgBuffer sg = {
+    .base = &sg_data[0],
+    .len = sizeof(sg_data),
+  };
+  uint8_t dst[sizeof(sg_data) * 2];
+  uint32_t copied;
+  copied = ese_sg_to_buf(&sg, 1, 0, sizeof(dst), dst);
+  EXPECT_EQ(copied, sizeof(sg_data));
+  EXPECT_STREQ(reinterpret_cast<char *>(dst),
+               reinterpret_cast<char *>(sg_data));
+}
+
+TEST_F(ScatterGatherTest, OneFromBuf) {
+  uint8_t src[] = "HELLO WORLD!";
+  uint8_t sg_data[sizeof(src) * 2];
+  struct EseSgBuffer sg = {
+    .base = &sg_data[0],
+    .len = sizeof(sg_data),
+  };
+  uint32_t copied;
+  copied = ese_sg_from_buf(&sg, 1, 0, sizeof(src), src);
+  EXPECT_EQ(copied, sizeof(src));
+  EXPECT_STREQ(reinterpret_cast<char *>(src),
+               reinterpret_cast<char *>(sg_data));
+}
+
+
+TEST_F(ScatterGatherTest, ThreeToBuf) {
+  uint8_t one[] = {'H', 'E', 'L'};
+  uint8_t two[] = {'L', 'O', ' '};
+  uint8_t three[] = "WORLD";
+  struct EseSgBuffer sg[] = {
+    {
+      .base = one,
+      .len = sizeof(one),
+    },
+    {
+      .base = two,
+      .len = sizeof(two),
+    },
+    {
+      .base = three,
+      .len = sizeof(three),
+    },
+  };
+  uint8_t dst[256];
+  uint32_t copied;
+  copied = ese_sg_to_buf(sg, 3, 0, sizeof(dst), dst);
+  EXPECT_EQ(copied, strlen("HELLO WORLD") + 1);
+  EXPECT_STREQ(reinterpret_cast<char *>(dst),
+               "HELLO WORLD");
+  // Again but offset by the first bufs.
+  copied = ese_sg_to_buf(sg, 3, sizeof(one) + sizeof(two) - 2, sizeof(dst),
+                         dst);
+  EXPECT_EQ(copied, (strlen("HELLO WORLD") + 1) -
+                     (sizeof(one) + sizeof(two) - 2));
+  EXPECT_STREQ(reinterpret_cast<char *>(dst),
+               "O WORLD");
+}
+
+
+TEST_F(ScatterGatherTest, ThreeFromBuf) {
+  uint8_t one[3];
+  uint8_t two[3];
+  uint8_t three[6];
+  struct EseSgBuffer sg[] = {
+    {
+      .base = one,
+      .len = sizeof(one),
+    },
+    {
+      .base = two,
+      .len = sizeof(two),
+    },
+    {
+      .base = three,
+      .len = sizeof(three),
+    },
+  };
+  uint8_t src[] = "HELLO WORLD";
+  uint32_t copied;
+  copied = ese_sg_from_buf(sg, 3, 0, sizeof(src), src);
+  EXPECT_EQ(copied, sizeof(one)  + sizeof(two) + sizeof(three));
+  EXPECT_EQ(one[0], 'H');
+  EXPECT_EQ(one[1], 'E');
+  EXPECT_EQ(one[2], 'L');
+  EXPECT_STREQ(reinterpret_cast<char *>(three), "WORLD");
+  // Again but offset.
+  copied = ese_sg_from_buf(sg, 3, 6, sizeof(src), src);
+  EXPECT_EQ(copied, ese_sg_length(sg, 3) - 6);
+  EXPECT_EQ(three[5], ' ');
+  three[5] = 0; // NUL terminate for the test below.
+  EXPECT_STREQ(reinterpret_cast<char *>(three), "HELLO");
+}
+