diff --git a/zircon/system/ulib/ftl/ftl.h b/zircon/system/ulib/ftl/ftl.h
new file mode 100644
index 0000000000000000000000000000000000000000..2e3dd9c609731e384b1acf13913d13adb3a6bf2a
--- /dev/null
+++ b/zircon/system/ulib/ftl/ftl.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <stdint.h>    // For fixed width types.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Type Declarations.
+//
+
+// NDM Control Block
+typedef struct ndm* NDM;
+typedef const struct ndm* CNDM;
+
+// FTL Interface Structure
+typedef struct XfsVol {
+    // Driver functions
+    int (*write_pages)(const void* buf, uint32_t page0, int cnt, void* vol);
+    int (*read_pages)(void* buf, uint32_t page0, int cnt, void* vol);
+    int (*report)(void *vol, uint32_t msg, ...);
+
+    const char* name;       // volume name
+    uint32_t flags;         // option flags
+    uint32_t num_pages;     // number of pages in volume
+    uint32_t page_size;     // page size in bytes
+    void* vol;              // driver's volume pointer
+    void* ftl_volume;       // ftl layer (block device) volume
+} XfsVol;
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/zircon/system/ulib/ftl/ftl_private.h b/zircon/system/ulib/ftl/ftl_private.h
new file mode 100644
index 0000000000000000000000000000000000000000..742e875db5b58ac3d99a2339e09c116e81faa0a0
--- /dev/null
+++ b/zircon/system/ulib/ftl/ftl_private.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#pragma once
+
+#include <ftl.h>   // For API definition.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Symbol Definitions.
+//
+#define ui8 uint8_t
+
+//
+// Function Prototypes.
+//
+
+// TargetNDM interface to TargetFTL
+int ndmEraseBlock(uint32_t pn, NDM ndm);
+int ndmReadPages(uint32_t pn0, uint32_t count, ui8* buf, ui8* spare, NDM ndm);
+int ndmReadSpare(uint32_t vpn, ui8* spare, NDM ndm);
+int ndmWritePages(uint32_t pn0, uint32_t cnt, const ui8* buf, ui8* spare, NDM ndm);
+int ndmWritePage(uint32_t vpn, const ui8* buf, ui8* spare, NDM ndm);
+int ndmTransferPage(uint32_t old_vpn, uint32_t new_vpn, ui8* buf, ui8* spare, NDM ndm);
+int ndmCheckPage(uint32_t pn, ui8* data, ui8* spare, NDM ndm);
+uint32_t ndmPairOffset(uint32_t page_offset, CNDM ndm);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/zircon/system/ulib/ftl/ftln/ftln_init.c b/zircon/system/ulib/ftl/ftln/ftln_init.c
index f22b1995712e21189a25ed08039507fa73fe370e..9754559f6570e3cdaa52c50cae5c0b5ffc72f13e 100644
--- a/zircon/system/ulib/ftl/ftln/ftln_init.c
+++ b/zircon/system/ulib/ftl/ftln/ftln_init.c
@@ -91,7 +91,7 @@ static int map_page_check(FTLN ftl, ui32 apn, int process) {
 
     // Call driver validity check. Return -1 if error.
     ++ftl->stats.page_check;
-    status = ftl->page_check(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
+    status = ndmCheckPage(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
     if (status < 0)
         return FtlnFatErr(ftl);
 
@@ -140,23 +140,17 @@ static int map_page_check(FTLN ftl, ui32 apn, int process) {
 
                     // Process each elist page, from last to first.
                     for (;;) {
-                        //---------------------------------------------------------
                         // Verify and apply elist page. Return if page invalid.
-                        //---------------------------------------------------------
                         status = proc_elist(ftl);
                         if (status != NDM_PAGE_VALID)
                             return status;
 
-                        //---------------------------------------------------------
                         // If first (perhaps only) page was processed, finished!
-                        //---------------------------------------------------------
                         if (apn == ap0)
                             break;
 
-                        //---------------------------------------------------------
                         // Move to next written page in backwards direction. If
                         // MLC flash, move to page whose pair has higher offset.
-                        //---------------------------------------------------------
 #if INC_FTL_NDM_SLC
                         --apn;
 #else
@@ -170,29 +164,21 @@ static int map_page_check(FTLN ftl, ui32 apn, int process) {
                         }
 #endif
 
-                        //---------------------------------------------------------
                         // Call driver to read/check next page. Return -1 if error.
-                        //---------------------------------------------------------
                         ++ftl->stats.page_check;
-                        status = ftl->page_check(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
+                        status = ndmCheckPage(apn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
                         if (status < 0)
                             return FtlnFatErr(ftl);
 
-                        //---------------------------------------------------------
                         // If page is erased or invalid, return its status.
-                        //---------------------------------------------------------
                         if (status != NDM_PAGE_VALID)
                             return status;
 
-                        //---------------------------------------------------------
                         // Verify the metadata version is correct.
-                        //---------------------------------------------------------
                         if (RD32_LE(&ppns[0]) != FTLN_META_VER1)
                             return NDM_PAGE_INVALID;
 
-                        //---------------------------------------------------------
                         // Verify the metadata type is correct.
-                        //---------------------------------------------------------
                         if (RD32_LE(&ppns[1]) != ERASED_LIST)
                             return NDM_PAGE_INVALID;
                     }
@@ -294,7 +280,7 @@ static int build_map(FTLN ftl) {
             else {
                 // Read page's spare area.
                 ++ftl->stats.read_spare;
-                status = ftl->read_spare(pn, ftl->spare_buf, ftl->ndm);
+                status = ndmReadSpare(pn, ftl->spare_buf, ftl->ndm);
 
                 // Return if fatal error.
                 if (status == -2) {
@@ -309,11 +295,11 @@ static int build_map(FTLN ftl) {
 
             // If first page, retrieve block count. Otherwise compare with
             // block count of block's already-checked-valid first page.
-            if (po == 0)
+            if (po == 0) {
                 bc = GET_SA_BC(ftl->spare_buf);
-            else if (bc != GET_SA_BC(ftl->spare_buf)) {
+            } else if (bc != GET_SA_BC(ftl->spare_buf)) {
 #if FTLN_DEBUG > 1
-                printf("build_ma: b = %u, po = %u, i_bc = %u vs 0_bc = %u\n", b, po,
+                printf("build_map: b = %u, po = %u, i_bc = %u vs 0_bc = %u\n", b, po,
                        GET_SA_BC(ftl->spare_buf), bc);
 #endif
 
@@ -332,7 +318,7 @@ static int build_map(FTLN ftl) {
             mpn = GET_SA_VPN(ftl->spare_buf);
             if (mpn > ftl->num_map_pgs) {
 #if FTLN_DEBUG > 1
-                printf("build_ma: b = %u, po = %u, mpn = %u, max = %u\n", b, po, mpn,
+                printf("build_map: b = %u, po = %u, mpn = %u, max = %u\n", b, po, mpn,
                        ftl->num_map_pgs);
 #endif
 
@@ -361,7 +347,7 @@ static int build_map(FTLN ftl) {
                     INC_USED(ftl->bdata[b]);
                 }
 #if FTLN_DEBUG > 1
-                printf("build_ma: mpn = %u, old_pn = %d, new_pn = %u\n", mpn, ftl->mpns[mpn],
+                printf("build_map: mpn = %u, old_pn = %d, new_pn = %u\n", mpn, ftl->mpns[mpn],
                        b * ftl->pgs_per_blk + po);
 #endif
 
@@ -533,7 +519,7 @@ static int format_status(FTLN ftl) {
 
         // Read spare area for first page. Return -1 if fatal error.
         ++ftl->stats.read_spare;
-        rc = ftl->read_spare(pn, ftl->spare_buf, ftl->ndm);
+        rc = ndmReadSpare(pn, ftl->spare_buf, ftl->ndm);
         if (rc == -2)
             return FtlnFatErr(ftl);
 
@@ -565,7 +551,7 @@ static int format_status(FTLN ftl) {
 
                         // Read spare area for higher page. Return -1 if fatal error.
                         ++ftl->stats.read_spare;
-                        rc = ftl->read_spare(pn + n, ftl->spare_buf, ftl->ndm);
+                        rc = ndmReadSpare(pn + n, ftl->spare_buf, ftl->ndm);
                         if (rc == -2)
                             return FtlnFatErr(ftl);
 
@@ -602,7 +588,7 @@ static int format_status(FTLN ftl) {
 #endif
             // Call driver validity check. Return -1 if error.
             ++ftl->stats.page_check;
-            rc = ftl->page_check(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
+            rc = ndmCheckPage(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
             if (rc < 0)
                 return FtlnFatErr(ftl);
 
@@ -624,7 +610,7 @@ static int format_status(FTLN ftl) {
 
                 // Read spare data. Return if fatal error. Skip if ECC error.
                 ++ftl->stats.read_spare;
-                rc = ftl->read_spare(pn + n, ftl->spare_buf, ftl->ndm);
+                rc = ndmReadSpare(pn + n, ftl->spare_buf, ftl->ndm);
                 if (rc == -2)
                     return FtlnFatErr(ftl);
                 if (rc)
@@ -642,7 +628,7 @@ static int format_status(FTLN ftl) {
 #endif
                     // Read and check the copy-end page. Return -1 if error.
                     ++ftl->stats.page_check;
-                    rc = ftl->page_check(pn + n, ftl->main_buf, ftl->spare_buf, ftl->ndm);
+                    rc = ndmCheckPage(pn + n, ftl->main_buf, ftl->spare_buf, ftl->ndm);
                     if (rc < 0)
                         return FtlnFatErr(ftl);
 
@@ -819,7 +805,7 @@ static int copy_end_mark(CFTLN ftl, ui32 b) {
     SET_SA_WC(0, ftl->spare_buf);
 
     // Write page that marks the end of a volume resume copy block.
-    return ftl->write_page(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
+    return ndmWritePage(pn, ftl->main_buf, ftl->spare_buf, ftl->ndm);
 }
 
 // resume_copy: Copy one volume block
@@ -842,7 +828,7 @@ static int resume_copy(FTLN ftl, ui32 src_b, ui32 dst_b, ui32 bc) {
     for (po = 0; po <= ftl->resume_po; ++po) {
         // Read source page's spare area.
         ++ftl->stats.read_spare;
-        rc = ftl->read_spare(src_pg0 + po, ftl->spare_buf, ftl->ndm);
+        rc = ndmReadSpare(src_pg0 + po, ftl->spare_buf, ftl->ndm);
 
         // Return -1 if fatal error, skip page if ECC error on spare read.
         if (rc) {
@@ -865,8 +851,10 @@ static int resume_copy(FTLN ftl, ui32 src_b, ui32 dst_b, ui32 bc) {
 
         // Invoke page transfer routine. If error, return -1.
         ++ftl->stats.transfer_page;
-        if (ftl->xfer_page(src_pg0 + po, dst_pg0 + po, ftl->main_buf, ftl->spare_buf, ftl->ndm))
+        if (ndmTransferPage(src_pg0 + po, dst_pg0 + po, ftl->main_buf,
+                            ftl->spare_buf, ftl->ndm)) {
             return FtlnFatErr(ftl);
+        }
     }
 
     // Return success.
@@ -1133,31 +1121,31 @@ static int rd_map_pg(void* vol, ui32 mpn, void* buf, int* unmapped) {
 
 // FtlnAddVol: Create a new FTL volume
 //
-//      Inputs: ftl_dvr = pointer to FTL NDM driver control block
+//      Inputs: ftl_cfg = pointer to FTL configuration structure
 //              xfs = pointer to FTL interface structure
 //
 //     Returns: -1 if error, 0 for success.
 //
-int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
+int FtlnAddVol(FtlNdmVol* ftl_cfg, XfsVol* xfs) {
     ui32 n, vol_blks;
     ui8* buf;
     FTLN ftl;
 
     // If number of blocks less than 7, FTL-NDM cannot work.
-    if (ftl_dvr->num_blocks < 7) {
+    if (ftl_cfg->num_blocks < 7) {
         FsError2(FTL_CFG_ERR, EINVAL);
         return -1;
     }
 
     // Ensure FTL flags are valid.
-    if (ftl_dvr->flags & ~(FSF_EXTRA_FREE | FSF_READ_WEAR_LIMIT | FSF_READ_ONLY_INIT)) {
+    if (ftl_cfg->flags & ~(FSF_EXTRA_FREE | FSF_READ_WEAR_LIMIT | FSF_READ_ONLY_INIT)) {
         FsError2(FTL_CFG_ERR, EINVAL);
         return -1;
     }
 
 #if CACHE_LINE_SIZE
     // Ensure driver page size is a multiple of the CPU cache line size.
-    if (ftl_dvr->page_size % CACHE_LINE_SIZE) {
+    if (ftl_cfg->page_size % CACHE_LINE_SIZE) {
         FsError2(FTL_CFG_ERR, EINVAL);
         return -1;
     }
@@ -1165,8 +1153,8 @@ int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
 
     // Ensure physical page size is a multiple of FAT sector size and
     // not bigger than the device block size.
-    if (ftl_dvr->page_size % FAT_SECT_SZ || ftl_dvr->page_size == 0 ||
-        ftl_dvr->page_size > ftl_dvr->block_size) {
+    if (ftl_cfg->page_size % FAT_SECT_SZ || ftl_cfg->page_size == 0 ||
+        ftl_cfg->page_size > ftl_cfg->block_size) {
         FsError2(FTL_CFG_ERR, EINVAL);
         return -1;
     }
@@ -1181,17 +1169,16 @@ int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
     Ftln = ftl;
 #endif
 
-    // Set all FTL driver dependent variables.
-    ftl->num_blks = ftl_dvr->num_blocks;
-    ftl->page_size = ftl_dvr->page_size;
-    ftl->eb_size = ftl_dvr->eb_size;
-    ftl->block_size = ftl_dvr->block_size;
+    // Initialize the FTL control block.
+    ftl->num_blks = ftl_cfg->num_blocks;
+    ftl->page_size = ftl_cfg->page_size;
+    ftl->eb_size = ftl_cfg->eb_size;
+    ftl->block_size = ftl_cfg->block_size;
     ftl->pgs_per_blk = ftl->block_size / ftl->page_size;
     ftl->num_pages = ftl->pgs_per_blk * ftl->num_blks;
-    ftl->start_pn = ftl_dvr->start_page;
-    ftl->ndm = ftl_dvr->ndm;
-    ftl->type = ftl_dvr->type;
-    ftl->flags = ftl_dvr->flags;
+    ftl->start_pn = ftl_cfg->start_page;
+    ftl->ndm = ftl_cfg->ndm;
+    ftl->flags = ftl_cfg->flags;
     strcpy(ftl->vol_name, xfs->name);
 
     // Ensure pages per block doesn't exceed alloted metadata field width.
@@ -1207,18 +1194,6 @@ int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
     }
 #endif
 
-    // Copy the NDM interface functions.
-    ftl->write_page = ftl_dvr->write_data_and_spare;
-    ftl->write_pages = ftl_dvr->write_pages;
-    ftl->read_spare = ftl_dvr->read_spare;
-    ftl->read_pages = ftl_dvr->read_pages;
-    ftl->page_check = ftl_dvr->page_check;
-    ftl->xfer_page = ftl_dvr->transfer_page;
-    ftl->erase_block = ftl_dvr->erase_block;
-#if INC_FTL_NDM_MLC
-    ftl->pair_offset = ftl_dvr->pair_offset;
-#endif
-
     // Compute how many volume pages are mapped by a single map page.
     ftl->mappings_per_mpg = ftl->page_size / FTLN_PN_SZ;
 
@@ -1283,8 +1258,8 @@ int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
     // 2%. Increasing number of map pages makes recycles more efficient
     // because the ratio of used to dirty pages is lower in map blocks.
     ftl->num_vpages = vol_blks * ftl->pgs_per_blk;
-    n = ftl_dvr->extra_free;
-    if (FLAG_IS_CLR(ftl_dvr->flags, FSF_EXTRA_FREE) || n < 2 || n > 50)
+    n = ftl_cfg->extra_free;
+    if (FLAG_IS_CLR(ftl_cfg->flags, FSF_EXTRA_FREE) || n < 2 || n > 50)
         n = 2;
     n = (n * ftl->num_vpages) / 100;
     if (n == 0)
@@ -1340,25 +1315,25 @@ int FtlnAddVol(FtlNdmVol* ftl_dvr, XfsVol* xfs) {
 
     // For SLC devices, adjust driver cached MPNs if too big or zero.
 #if INC_FTL_NDM_SLC
-    if (ftl->num_map_pgs < ftl_dvr->cached_map_pages || ftl_dvr->cached_map_pages == 0)
-        ftl_dvr->cached_map_pages = ftl->num_map_pgs;
+    if (ftl->num_map_pgs < ftl_cfg->cached_map_pages || ftl_cfg->cached_map_pages == 0)
+        ftl_cfg->cached_map_pages = ftl->num_map_pgs;
 
 #else
 
     // For MLC devices, cache all map pages so that no map write occurs
     // due to cache preemption.
-    ftl_dvr->cached_map_pages = ftl->num_map_pgs;
+    ftl_cfg->cached_map_pages = ftl->num_map_pgs;
 #endif
 
     // Allocate map page cache for new volume.
-    ftl->map_cache = ftlmcNew(ftl, ftl_dvr->cached_map_pages, FtlnMapWr, rd_map_pg, ftl->page_size);
+    ftl->map_cache = ftlmcNew(ftl, ftl_cfg->cached_map_pages, FtlnMapWr, rd_map_pg, ftl->page_size);
     if (ftl->map_cache == NULL)
         goto FtlnAddV_err;
 
     // Set block read wear limit.
-    if (FLAG_IS_SET(ftl_dvr->flags, FSF_READ_WEAR_LIMIT))
-        ftl->max_rc = ftl_dvr->read_wear_limit;
-    else {
+    if (FLAG_IS_SET(ftl_cfg->flags, FSF_READ_WEAR_LIMIT)) {
+        ftl->max_rc = ftl_cfg->read_wear_limit;
+    } else {
 #if INC_FTL_NDM_SLC
         ftl->max_rc = SLC_NAND_RC_LIMIT;
 #else
diff --git a/zircon/system/ulib/ftl/ftln/ftln_intrnl.c b/zircon/system/ulib/ftl/ftln/ftln_intrnl.c
index a522ed8a5db7d5bdf51bd058742b0c4d126acef1..dcd0dab02ee223c621c39d9b90c1f74365622f7a 100644
--- a/zircon/system/ulib/ftl/ftln/ftln_intrnl.c
+++ b/zircon/system/ulib/ftl/ftln/ftln_intrnl.c
@@ -250,13 +250,13 @@ static int wr_vol_page(FTLN ftl, ui32 vpn, void* buf, ui32 old_ppn) {
     // If page data in buffer, write it. Returns 0 or -2.
     if (buf) {
         ++ftl->stats.write_page;
-        rc = ftl->write_page(ftl->start_pn + ppn, buf, ftl->spare_buf, ftl->ndm);
+        rc = ndmWritePage(ftl->start_pn + ppn, buf, ftl->spare_buf, ftl->ndm);
 
     // Else invoke page transfer routine. Returns 0, -2, or 1.
     } else {
         ++ftl->stats.transfer_page;
-        rc = ftl->xfer_page(ftl->start_pn + old_ppn, ftl->start_pn + ppn, ftl->main_buf,
-                            ftl->spare_buf, ftl->ndm);
+        rc = ndmTransferPage(ftl->start_pn + old_ppn, ftl->start_pn + ppn,
+                             ftl->main_buf, ftl->spare_buf, ftl->ndm);
     }
 
     // Return -1 for any error. Any write error is fatal.
@@ -566,7 +566,7 @@ static int recycle_vblk(FTLN ftl, ui32 recycle_b) {
 
         // Read page's spare area.
         ++ftl->stats.read_spare;
-        rc = ftl->read_spare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm);
+        rc = ndmReadSpare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm);
 
         // Return -1 if fatal error, skip page if ECC error on spare read.
         if (rc) {
@@ -670,8 +670,8 @@ static int flush_pending_writes(FTLN ftl, StagedWr* staged) {
 
     // Issue driver multi-page write request. Return -1 if error.
     ftl->stats.write_page += staged->cnt;
-    if (ftl->write_pages(ftl->start_pn + staged->ppn0, staged->cnt, staged->buf,
-                         ftl->spare_buf, ftl->ndm)) {
+    if (ndmWritePages(ftl->start_pn + staged->ppn0, staged->cnt, staged->buf,
+                      ftl->spare_buf, ftl->ndm)) {
         return FtlnFatErr(ftl);
     }
 
@@ -886,7 +886,7 @@ int FtlnRecycleMapBlk(FTLN ftl, ui32 recycle_b) {
 
         // Read page's spare area. Return -1 if fatal I/O error.
         ++ftl->stats.read_spare;
-        rc = ftl->read_spare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm);
+        rc = ndmReadSpare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm);
         if (rc == -2)
             return FtlnFatErr(ftl);
 
@@ -1178,13 +1178,13 @@ int FtlnMapWr(void* vol, ui32 mpn, void* buf) {
     // If page data in buffer, invoke write_page().
     if (buf) {
         ++ftl->stats.write_page;
-        status = ftl->write_page(ftl->start_pn + pn, buf, ftl->spare_buf, ftl->ndm);
+        status = ndmWritePage(ftl->start_pn + pn, buf, ftl->spare_buf, ftl->ndm);
 
     // Else source data is in flash. Invoke page transfer routine.
     } else {
         ++ftl->stats.transfer_page;
-        status = ftl->xfer_page(ftl->start_pn + old_pn, ftl->start_pn + pn, ftl->main_buf,
-                                ftl->spare_buf, ftl->ndm);
+        status = ndmTransferPage(ftl->start_pn + old_pn, ftl->start_pn + pn,
+                                 ftl->main_buf, ftl->spare_buf, ftl->ndm);
     }
 
     // I/O or ECC decode error is fatal.
diff --git a/zircon/system/ulib/ftl/ftln/ftln_rd.c b/zircon/system/ulib/ftl/ftln/ftln_rd.c
index 15b61b0c84fa5f2418d8b2c3f75ecda6a2420d43..70a0912ea2f11e32b5598be32e063dfe1806ee5a 100644
--- a/zircon/system/ulib/ftl/ftln/ftln_rd.c
+++ b/zircon/system/ulib/ftl/ftln/ftln_rd.c
@@ -27,8 +27,8 @@ static int flush_pending_reads(FTLN ftl, StagedRd* staged) {
 
     // Issue pending reads.
     ftl->stats.read_page += staged->run_cnt;
-    status = ftl->read_pages(ftl->start_pn + staged->ppn0, staged->run_cnt, staged->buf,
-                             ftl->spare_buf, ftl->ndm);
+    status = ndmReadPages(ftl->start_pn + staged->ppn0, staged->run_cnt, staged->buf,
+                          ftl->spare_buf, ftl->ndm);
 
     // Adjust data buffer pointer.
     staged->buf += staged->run_cnt * ftl->page_size;
@@ -183,7 +183,7 @@ int FtlnRdPage(FTLN ftl, ui32 ppn, void* rd_buf) {
 
     // Read page from flash. If error, set errno/fatal flag/return -1.
     ++ftl->stats.read_page;
-    status = ftl->read_pages(ftl->start_pn + ppn, 1, rd_buf, ftl->spare_buf, ftl->ndm);
+    status = ndmReadPages(ftl->start_pn + ppn, 1, rd_buf, ftl->spare_buf, ftl->ndm);
     if (status < 0)
         return FtlnFatErr(ftl);
 
diff --git a/zircon/system/ulib/ftl/ftln/ftln_util.c b/zircon/system/ulib/ftl/ftln/ftln_util.c
index 4197c7cd1981cf7bc4e3da97d2e46020d7c2f828..6b1f132ce004ef33fe40dab043d02640d3a55235 100644
--- a/zircon/system/ulib/ftl/ftln/ftln_util.c
+++ b/zircon/system/ulib/ftl/ftln/ftln_util.c
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ftlnp.h"
+#include <stdarg.h>
+#include <string.h>
 
-#include <strings.h>
+#include "ftlnp.h"
 
 // Local Function Definitions
 
@@ -386,7 +387,7 @@ int FtlnReport(void* vol, ui32 msg, ...) {
 
             // Set TargetFTL-NDM driver call counts and reset internal ones.
             buf->xfs.drvr_stats.ftl.ndm = ftl->stats;
-            bzero(&ftl->stats, sizeof(ftl_ndm_stats));
+            memset(&ftl->stats, 0, sizeof(ftl_ndm_stats));
 
             // Return success.
             return 0;
@@ -462,7 +463,7 @@ int FtlnEraseBlk(FTLN ftl, ui32 b) {
 
     // Call driver to erase block. Return -1 if error.
     ++ftl->stats.erase_block;
-    if (ftl->erase_block(ftl->start_pn + b * ftl->pgs_per_blk, ftl->ndm))
+    if (ndmEraseBlock(ftl->start_pn + b * ftl->pgs_per_blk, ftl->ndm))
         return FtlnFatErr(ftl);
 
     // Increment block wear count and possibly adjust highest.
@@ -622,7 +623,7 @@ void FtlnDecUsed(FTLN ftl, ui32 pn, ui32 vpn) {
 #if FTLN_DEBUG
     // Read page spare area and assert VPNs match.
     ++ftl->stats.read_spare;
-    PfAssert(ftl->read_spare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm) >= 0);
+    PfAssert(ndmReadSpare(ftl->start_pn + pn, ftl->spare_buf, ftl->ndm) >= 0);
     PfAssert(GET_SA_VPN(ftl->spare_buf) == vpn);
 #endif
 } //lint !e818
diff --git a/zircon/system/ulib/ftl/ftln/ftlnp.h b/zircon/system/ulib/ftl/ftln/ftlnp.h
index 8389072af5d5a9b8225eda96f92358d5c4e9557a..044472087d2dabc026b4b41f4a5841f821dc2d63 100644
--- a/zircon/system/ulib/ftl/ftln/ftlnp.h
+++ b/zircon/system/ulib/ftl/ftln/ftlnp.h
@@ -8,10 +8,10 @@
 
 #include <errno.h>
 #include <string.h>
-#include <sys.h>
+#include <ftl_private.h>
 #include <fsprivate.h>
 #include <kprivate/ndm.h>
-#include <kprivate/ftl_mc.h>
+#include <ftl_mc.h>
 
 //
 // Configuration.
@@ -164,68 +164,55 @@
 typedef struct ftln* FTLN;
 typedef const struct ftln* CFTLN;
 struct ftln {
-    CircLink link; // volume list link
-
-    // Driver Functions
-    int (*write_page)(ui32 pn, const void* data, void* spare, void* ndm);
-    int (*read_spare)(ui32 pn, void* spare, void* ndm);
-    int (*read_pages)(ui32 start_pn, ui32 count, void* data, void* spare, void* ndm);
-    int (*write_pages)(ui32 start_pn, ui32 count, const void* data, void* spare, void* ndm);
-    int (*page_check)(ui32 pn, ui8* data, ui8* spare, void* ndm);
-    int (*xfer_page)(ui32 old_pn, ui32 new_pn, ui8* data, ui8* spare, void* ndm);
-    int (*erase_block)(ui32 pn, void* ndm);
-#if INC_FTL_NDM_MLC
-    ui32 (*pair_offset)(ui32 page_offset, void* ndm);
-#endif
+    CircLink link;          // volume list link
 
     // Driver Dependent Variables
-    ui32 num_pages;   // total number of pages
-    ui32 pgs_per_blk; // number of pages in a block
-    ui32 block_size;  // block size in bytes
-    ui32 num_blks;    // number of blocks
-    ui32 page_size;   // page size in bytes
-    ui32 start_pn;    // first page on device for volume
-    void* ndm;        // pointer to NDM this FTL belongs to
+    ui32 num_pages;         // total number of pages
+    ui32 pgs_per_blk;       // number of pages in a block
+    ui32 block_size;        // block size in bytes
+    ui32 num_blks;          // number of blocks
+    ui32 page_size;         // page size in bytes
+    ui32 start_pn;          // first page on device for volume
+    void* ndm;              // pointer to NDM this FTL belongs to
 
-    ui32 flags; // holds various FTL flags
-    ui32* bdata;     // block metadata: flags and counts
-    ui8* blk_wc_lag; // amount block erase counts lag 'high_wc'
-    ui32* mpns;      // array holding phy page # of map pages
+    ui32 flags;             // holds various FTL flags
+    ui32* bdata;            // block metadata: flags and counts
+    ui8* blk_wc_lag;        // amount block erase counts lag 'high_wc'
+    ui32* mpns;             // array holding phy page # of map pages
 
-    FTLMC* map_cache;      // handle to map page cache
-    ui32 free_vpn;         // next free page for volume page write
-    ui32 free_mpn;         // next free page for map page write
-    ui32 mappings_per_mpg; // number of phys page numbers per map page
-    ui32 num_vpages;       // number of volume pages
-    ui32 num_free_blks;    // number of free blocks
-    ui32 num_map_pgs;      // number of pages holding map data
-    ui32 high_wc;          // highest block wear count
-    ui32 high_bc;          // highest map block write count
-    ui32 max_rc;           // per block read wear limit
-    ui32 max_rc_blk;       // if not -1, # of block w/high read cnt
-    ui32 high_bc_mblk;     // last map block
-    ui32 high_bc_mblk_po;  // used page offset on last map block
-    ui32 resume_vblk;      // vblk in interrupted recycle recovery
-    ui32 resume_tblk;      // tmp blk for interrupted recycle recovery
-    ui32 resume_po;        // resume vblk's highest used page offset
+    FTLMC* map_cache;       // handle to map page cache
+    ui32 free_vpn;          // next free page for volume page write
+    ui32 free_mpn;          // next free page for map page write
+    ui32 mappings_per_mpg;  // number of phys page numbers per map page
+    ui32 num_vpages;        // number of volume pages
+    ui32 num_free_blks;     // number of free blocks
+    ui32 num_map_pgs;       // number of pages holding map data
+    ui32 high_wc;           // highest block wear count
+    ui32 high_bc;           // highest map block write count
+    ui32 max_rc;            // per block read wear limit
+    ui32 max_rc_blk;        // if not -1, # of block w/high read cnt
+    ui32 high_bc_mblk;      // last map block
+    ui32 high_bc_mblk_po;   // used page offset on last map block
+    ui32 resume_vblk;       // vblk in interrupted recycle recovery
+    ui32 resume_tblk;       // tmp blk for interrupted recycle recovery
+    ui32 resume_po;         // resume vblk's highest used page offset
 #if INC_ELIST
-    ui32 elist_blk; // if valid, # of block holding erased list
+    ui32 elist_blk;         // if valid, # of block holding erased list
 #endif
-    ftl_ndm_stats stats; // driver call counts
+    ftl_ndm_stats stats;    // driver call counts
 
-    ui8* main_buf; // NAND main page buffer
-    ui8* spare_buf; // spare buffer for single/multi-pg access
+    ui8* main_buf;          // NAND main page buffer
+    ui8* spare_buf;         // spare buffer for single/multi-pg access
 
-    ui8 type;           // type of NAND - SLC or MLC
-    ui8 eb_size;        // spare area size in bytes
-    ui8 copy_end_found; // vblk resume copy-end mark found
-    ui8 deferment;      // # of recycles before applying wear limit
+    ui8 eb_size;            // spare area size in bytes
+    ui8 copy_end_found;     // vblk resume copy-end mark found
+    ui8 deferment;          // # of recycles before applying wear limit
 #if FTLN_DEBUG
-    ui8 max_wc_lag;  // maximum observed lag below hi wear count
-    ui8 max_wc_over; // # of times max WC (0xFF) was exceeded
+    ui8 max_wc_lag;         // maximum observed lag below hi wear count
+    ui8 max_wc_over;        // # of times max WC (0xFF) was exceeded
 #endif
 #if FS_ASSERT
-    ui8 assert_no_recycle; // test no recycle changes physical page #
+    ui8 assert_no_recycle;  // test no recycle changes physical page #
 #endif
     char vol_name[FILENAME_MAX]; // volume name
 };
diff --git a/zircon/system/ulib/ftl/ftln/ndm-driver.cpp b/zircon/system/ulib/ftl/ftln/ndm-driver.cpp
index ce322331de3ddb0b6207d81d5dcb93a6a0698395..507bd12d1a9957f1bea8df710a7f6a6004de82c7 100644
--- a/zircon/system/ulib/ftl/ftln/ndm-driver.cpp
+++ b/zircon/system/ulib/ftl/ftln/ndm-driver.cpp
@@ -6,7 +6,7 @@
 
 #include <zircon/assert.h>
 
-#include "kprivate/fsdriver.h"
+#include <ftl_private.h>
 #include "kprivate/fsprivate.h"
 #include "kprivate/ndm.h"
 #include "posix.h"
diff --git a/zircon/system/ulib/ftl/ftln/volume.cpp b/zircon/system/ulib/ftl/ftln/volume.cpp
index 7d7c1453527ba62d14a77487dae2d5ef70c47e57..e534505815dd547f00c004aa57e703f6f7fee5bf 100644
--- a/zircon/system/ulib/ftl/ftln/volume.cpp
+++ b/zircon/system/ulib/ftl/ftln/volume.cpp
@@ -5,6 +5,7 @@
 #include <lib/ftl/volume.h>
 #include <zircon/assert.h>
 
+#include <ftl_private.h>
 #include "kprivate/ndm.h"
 #include "posix.h"
 
diff --git a/zircon/system/ulib/ftl/inc/kprivate/fsdriver.h b/zircon/system/ulib/ftl/inc/kprivate/fsdriver.h
index 6f659d6443b646a0b308733f369408b12f87d361..2da63349a3e5b1ea0e7a0f064991a157f1ce1160 100644
--- a/zircon/system/ulib/ftl/inc/kprivate/fsdriver.h
+++ b/zircon/system/ulib/ftl/inc/kprivate/fsdriver.h
@@ -61,46 +61,18 @@ void AssertError(int line, const char* file);
 /* Type Definitions                                                    */
 /***********************************************************************/
 
-// XFS structure holding all driver information
-typedef struct XfsVol {
-    // Driver functions
-    int (*write_pages)(const void* buf, ui32 frst_pg, int cnt, void* vol);
-    int (*read_pages)(void* buf, ui32 frst_pg, int cnt, void* vol);
-    int (*report)(void* vol, ui32 msg, ...);
-
-    const char* name;       // volume name
-    ui32 flags;             // option flags
-    ui32 num_pages;         // number of pages in volume
-    ui32 page_size;         // page size in bytes
-    void* vol;              // driver's volume pointer
-    void* ftl_volume;       // ftl layer (block device) volume
-} XfsVol;
-
 // FTL NDM structure holding all driver information
 typedef struct {
-    ui32 block_size;       // size of a block in bytes
-    ui32 num_blocks;       // total number of blocks
-    ui32 page_size;        // flash page data size in bytes
-    ui32 eb_size;          // flash page spare size in bytes
-    ui32 start_page;       // volume first page on flash
-    ui32 cached_map_pages; // number of map pages to be cached
-    ui32 extra_free;      // volume percentage left unused
-    ui32 read_wear_limit; // device read-wear limit
-    void* ndm;            // driver's NDM pointer
-    ui32 flags;           // option flags
-    ui32 type;            // device type
-
-    // Driver functions:
-    int (*write_data_and_spare)(ui32 pn, const void* data, void* spare, void* ndm);
-    int (*write_pages)(ui32 start_pn, ui32 count, const void* data, void* spare, void* ndm);
-    int (*read_spare)(ui32 pn, void* spare, void* ndm);
-    int (*read_pages)(ui32 start_pn, ui32 count, void* data, void* spare, void* ndm);
-    int (*page_check)(ui32 pn, ui8* data, ui8* spare, void* ndm);
-    int (*transfer_page)(ui32 old_pn, ui32 new_pn, ui8* data, ui8* spare, void* ndm);
-    int (*erase_block)(ui32 pn, void* ndm);
-#if INC_FTL_NDM_MLC
-    ui32 (*pair_offset)(ui32 page_offset, void* ndm);
-#endif
+    ui32 block_size;        // size of a block in bytes
+    ui32 num_blocks;        // total number of blocks
+    ui32 page_size;         // flash page data size in bytes
+    ui32 eb_size;           // flash page spare size in bytes
+    ui32 start_page;        // volume first page on flash
+    ui32 cached_map_pages;  // number of map pages to be cached
+    ui32 extra_free;        // volume percentage left unused
+    ui32 read_wear_limit;   // device read-wear limit
+    void* ndm;              // driver's NDM pointer
+    ui32 flags;             // option flags
 } FtlNdmVol;
 
 // FS Report Events
diff --git a/zircon/system/ulib/ftl/inc/kprivate/ndm.h b/zircon/system/ulib/ftl/inc/kprivate/ndm.h
index 792c8d3780f924e7e0a3527f5fa463f685eef1d3..7dbfcde93b78ff2e12a1da8f955c9ae9174b7719 100644
--- a/zircon/system/ulib/ftl/inc/kprivate/ndm.h
+++ b/zircon/system/ulib/ftl/inc/kprivate/ndm.h
@@ -68,7 +68,7 @@ typedef struct {
     ui32 is_block_bad;    // number of is_block_bad() calls
 } NdmDvrStats;
 
-// Driver Interface Structure
+// TargetNDM Configuration Structure
 typedef struct {
     ui32 num_blocks;     // total number of blocks on device
     ui32 max_bad_blocks; // maximum number of bad blocks
@@ -103,10 +103,6 @@ typedef struct {
 #endif
 } NDMDrvr;
 
-// NDM Control Block
-typedef struct ndm* NDM;
-typedef const struct ndm* CNDM;
-
 /***********************************************************************/
 /* Functions Prototypes                                                */
 /***********************************************************************/
@@ -128,11 +124,6 @@ int ndmSavePartitionTable(NDM ndm);
 int ndmDelVols(CNDM ndm);
 int ndmDelVol(CNDM ndm, ui32 part_num);
 
-// User Volume API
-int ndmEraseBlock(ui32 pn, void* ndm_ptr);
-int ndmReadPages(ui32 start_pn, ui32 count, void* data, void* spare, void* ndm_ptr);
-int ndmWritePages(ui32 start_pn, ui32 count, const void* data, void* spare, void* ndm_ptr);
-
 // FTL Volume API
 int ndmAddVolFTL(NDM ndm, ui32 part_no, FtlNdmVol* ftl, XfsVol* xfs);
 
diff --git a/zircon/system/ulib/ftl/inc/sys.h b/zircon/system/ulib/ftl/inc/sys.h
index 91ae7a1dfe3adce7df2605d2ec2e23e0dbeedc42..67887992ddefff23d82df490edfb181075945b04 100644
--- a/zircon/system/ulib/ftl/inc/sys.h
+++ b/zircon/system/ulib/ftl/inc/sys.h
@@ -8,38 +8,9 @@
 extern "C" {
 #endif
 
-#include <stdio.h>
-#include <stdarg.h>
 #include <stdlib.h>
-#include <time.h>
 #include <targetos.h>
 
-/***********************************************************************/
-/* Symbol Definitions                                                  */
-/***********************************************************************/
-#define VERBOSE 1
-
-/***********************************************************************/
-/* Definitions related to reading/writing NVRAM memory.                */
-/***********************************************************************/
-int NvRead(const char* name, void* data, int type);
-int NvReadBin(const char* name, void* data, int len);
-int NvReadStr(const char* name, void* data, int maxlen);
-int NvWrite(const char* name, const void* data, int type);
-int NvWriteBin(const char* name, const void* data, int len);
-int NvReadBinSize(const char* name);
-#define NV_BYTE 0
-#define NV_SHORT 1
-#define NV_LONG 2
-#define NV_STRING 3
-#define NV_IP_ADDR 4
-#define NV_ETH_ADDR 5
-#define NV_ERR_LOG 6
-#define NV_IP6_ADDR 7
-#define NV_BINARY 8
-int NvDelete(const char* name, int type);
-void NvSave(void);
-
 void free_clear(void* alloc_ptr_addr);
 
 // Cache Line-Aligned Allocation/Deallocation Routines
diff --git a/zircon/system/ulib/ftl/ndm/ndm_init.c b/zircon/system/ulib/ftl/ndm/ndm_init.c
index e87040a70858cfc6071c87824bc532cb05bc6764..7aaa52e368419a6bd3576b168e992521070230c6 100644
--- a/zircon/system/ulib/ftl/ndm/ndm_init.c
+++ b/zircon/system/ulib/ftl/ndm/ndm_init.c
@@ -1153,10 +1153,9 @@ NDM ndmAddDev(const NDMDrvr* dvr) {
     if (FLAG_IS_SET(dvr->flags, FSF_TRANSFER_PAGE)) {
         ndm->dev_ndm = ndm->dev;
         ndm->xfr_page = dvr->transfer_page;
-    }
 
     // Else use internal read-page/write-page substitute.
-    else {
+    } else {
         ndm->dev_ndm = ndm;
         ndm->xfr_page = ndm_xfr_page;
     }
@@ -1167,9 +1166,6 @@ NDM ndmAddDev(const NDMDrvr* dvr) {
         ndm->write_pages = dvr->write_pages;
     }
 
-    // Set the device type.
-    ndm->dev_type = dvr->type;
-
     // Initialize the NDM.
     if (init_ndm(ndm))
         goto ndmAddDe_err;
diff --git a/zircon/system/ulib/ftl/ndm/ndm_intrnl.c b/zircon/system/ulib/ftl/ndm/ndm_intrnl.c
index a0e0b23258e7e2d8c52c8a8f6d9f089ab6259637..a3e8919c034c9b18b323a03ad3c35cca49517909 100644
--- a/zircon/system/ulib/ftl/ndm/ndm_intrnl.c
+++ b/zircon/system/ulib/ftl/ndm/ndm_intrnl.c
@@ -165,11 +165,10 @@ static int wr_ctrl_page(NDM ndm, ui32 cpc, ui32* curr_pnp, ui32* badblkp) {
         if (rc == -2) {
             FsError2(NDM_EIO, EIO);
             return rc;
-        }
 
         // Else bad block caused write failure, output block number and
         // return bad block indication.
-        else {
+        } else {
             PfAssert(rc == -1);
 #if NDM_DEBUG
             printf("wr_ctrl_page: bad block for #%u at page #%u\n", cpc, cpn);
@@ -688,8 +687,7 @@ static ui32 get_pbn(NDM ndm, ui32 vbn, int reason) {
     if (reason == WR_MAPPING) {
         ndm->last_wr_pbn = bn;
         ndm->last_wr_vbn = vbn;
-    } else // reason == RD_MAPPING
-    {
+    } else { // reason == RD_MAPPING
         ndm->last_rd_pbn = bn;
         ndm->last_rd_vbn = vbn;
     }
@@ -701,15 +699,15 @@ static ui32 get_pbn(NDM ndm, ui32 vbn, int reason) {
 //  write_page: Write a page to flash for FTL
 //
 //      Inputs: ndm = pointer to NDM control block
-//              pn = virtual page number
+//              vpn = virtual page number
 //              buf = pointer to main page buffer array
 //              spare = pointer to spare page buffer array
 //              action = NDM_NONE, NDM_ECC, or NDM_ECC_VAL
 //
 //     Returns: 0 on success, -2 on fatal error
 //
-static int write_page(NDM ndm, ui32 vpn, const ui8* data, ui8* spare, int action) {
-    ui32 vbn, bn, pn;
+static int write_page(NDM ndm, uint32_t vpn, const ui8* buf, ui8* spare, int action) {
+    uint32_t vbn, bn, pn;
     int rc;
 
     // Compute the virtual block number and check for error.
@@ -730,7 +728,7 @@ static int write_page(NDM ndm, ui32 vpn, const ui8* data, ui8* spare, int action
         pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
 
         // Call TargetNDM driver to write the page.
-        rc = ndm->write_page(pn, data, spare, action, ndm->dev);
+        rc = ndm->write_page(pn, buf, spare, action, ndm->dev);
 
         // Return 0 if successful.
         if (rc == 0)
@@ -751,21 +749,72 @@ static int write_page(NDM ndm, ui32 vpn, const ui8* data, ui8* spare, int action
     }
 } //lint !e818
 
-//   ftl_wr_pg: FTL driver function - write page (data + spare)
+//   read_page:  FTL driver function - read page (data only)
+//
+//      Inputs: vpn = virtual page number
+//              buf = pointer to buffer to copy data to
+//              ndm = NDM control block handle
+//
+//     Returns: 0 on success, -1 on uncorrectable ECC error, -2 on
+//              permanent fatal error, 1 if block page belongs to
+//              needs to be recycled
+//
+static int read_page(ui32 vpn, void* buf, NDM ndm) {
+    ui32 vbn, bn, pn;
+    int status;
+
+    // Compute the virtual block number based on virtual page number.
+    vbn = vpn / ndm->pgs_per_blk;
+    if (vbn >= ndm->num_vblks) {
+        FsError2(NDM_ASSERT, EINVAL);
+        return -2;
+    }
+
+    // Grab exclusive access to TargetNDM internals.
+    semPend(ndm->sem, WAIT_FOREVER);
+
+    // Get the physical block number from virtual one.
+    bn = get_pbn(ndm, vbn, RD_MAPPING);
+    if (bn == (ui32)-1) {
+        semPostBin(ndm->sem);
+        return -2;
+    }
+
+    // Compute physical page number.
+    pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
+
+    // Read decode page data.
+    status = ndm->read_page(pn, buf, ndm->spare_buf, ndm->dev);
+
+    // Release exclusive access to TargetNDM internals.
+    semPostBin(ndm->sem);
+
+    // If error, set errno. Return status.
+    if (status) {
+        if (status == -1)
+            FsError2(NDM_RD_ECC_FAIL, EINVAL);
+        else if (status == -2)
+            FsError2(NDM_EIO, EIO);
+    }
+    return status;
+}
+
+// Global Function Definitions
+
+//   ndmWritePage: FTL driver function - write page (data + spare)
 //
 //      Inputs: vpn = virtual page number
 //              data = pointer to buffer containing data to write
 //              spare = pointer to buffer containing spare bytes
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: 0 on success, -2 on fatal error
 //
-static int ftl_wr_pg(ui32 vpn, const void* data, void* spare, void* ndm_ptr) {
+int ndmWritePage(uint32_t vpn, const ui8* data, ui8* spare, NDM ndm) {
     int action, status;
-    NDM ndm = ndm_ptr;
 
     // If volume block, just ECC, else request validity checks too.
-    if (RD32_LE(&((ui8*)spare)[5]) == (ui32)-1)
+    if (RD32_LE(&spare[5]) == (ui32)-1)
         action = NDM_ECC;
     else
         action = NDM_ECC_VAL;
@@ -781,18 +830,17 @@ static int ftl_wr_pg(ui32 vpn, const void* data, void* spare, void* ndm_ptr) {
     return status;
 } //lint !e818
 
-// ftl_rd_spare: FTL driver function - read/decode page spare area
+// ndmReadSpare: FTL driver function - read/decode page spare area
 //
 //      Inputs: vpn = virtual page number
 //              spare = buffer to read sparea area into
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: 0 on success, -1 on ECC error, -2 on fatal error, 1 if
 //              block page belongs to needs to be recycled
 //
-static int ftl_rd_spare(ui32 vpn, void* spare, void* ndm_ptr) {
-    ui32 vbn, bn, pn;
-    NDM ndm = ndm_ptr;
+int ndmReadSpare(uint32_t vpn, ui8* spare, NDM ndm) {
+    uint32_t vbn, bn, pn;
     int status;
 
     // Compute virtual block number. Return -2 if out-of-range.
@@ -825,19 +873,18 @@ static int ftl_rd_spare(ui32 vpn, void* spare, void* ndm_ptr) {
     return status;
 }
 
-// ftl_check_pg: FTL driver function - determine status of a page
+// ndmCheckPage: FTL driver function - determine status of a page
 //
 //      Inputs: vpn = virtual page number
 //              data = buffer that will hold page data
 //              spare = buffer that will hold page spare
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: -1 if error, else NDM_PAGE_ERASED (0), NDM_PAGE_VALID
 //              (1), or NDM_PAGE_INVALID (2)
 //
-static int ftl_check_pg(ui32 vpn, ui8* data, ui8* spare, void* ndm_ptr) {
-    NDM ndm = ndm_ptr;
-    ui32 vbn, bn, pn;
+int ndmCheckPage(uint32_t vpn, ui8* data, ui8* spare, NDM ndm) {
+    uint32_t vbn, bn, pn;
     int status;
 
     // Compute the virtual block number.
@@ -869,71 +916,19 @@ static int ftl_check_pg(ui32 vpn, ui8* data, ui8* spare, void* ndm_ptr) {
     return status;
 }
 
-//   read_page:  FTL driver function - read page (data only)
-//
-//      Inputs: vpn = virtual page number
-//              buf = pointer to buffer to copy data to
-//              ndm_ptr = NDM control block handle
-//
-//     Returns: 0 on success, -1 on uncorrectable ECC error, -2 on
-//              permanent fatal error, 1 if block page belongs to
-//              needs to be recycled
-//
-static int read_page(ui32 vpn, void* buf, void* ndm_ptr) {
-    NDM ndm = ndm_ptr;
-    ui32 vbn, bn, pn;
-    int status;
-
-    // Compute the virtual block number based on virtual page number.
-    vbn = vpn / ndm->pgs_per_blk;
-    if (vbn >= ndm->num_vblks) {
-        FsError2(NDM_ASSERT, EINVAL);
-        return -2;
-    }
-
-    // Grab exclusive access to TargetNDM internals.
-    semPend(ndm->sem, WAIT_FOREVER);
-
-    // Get the physical block number from virtual one.
-    bn = get_pbn(ndm, vbn, RD_MAPPING);
-    if (bn == (ui32)-1) {
-        semPostBin(ndm->sem);
-        return -2;
-    }
-
-    // Compute physical page number.
-    pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
-
-    // Read decode page data.
-    status = ndm->read_page(pn, buf, ndm->spare_buf, ndm->dev);
-
-    // Release exclusive access to TargetNDM internals.
-    semPostBin(ndm->sem);
-
-    // If error, set errno. Return status.
-    if (status) {
-        if (status == -1)
-            FsError2(NDM_RD_ECC_FAIL, EINVAL);
-        else if (status == -2)
-            FsError2(NDM_EIO, EIO);
-    }
-    return status;
-}
-
-// ftl_xfr_page: FTL driver function - transfer a page
+// ndmTransferPage: FTL driver function - transfer a page
 //
 //      Inputs: old_vpn = old virtual page number
 //              new_vpn = new virtual page number
 //              buf = temporary buffer for swapping main page data
 //              spare = buffer holding new page's spare data
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: 0 on success, -2 on fatal error, 1 on ECC decode error
 //
-static int ftl_xfr_page(ui32 old_vpn, ui32 new_vpn, ui8* buf, ui8* spare, void* ndm_ptr) {
-    ui32 old_vbn, new_vbn, old_bn, new_bn, old_pn, new_pn;
+int ndmTransferPage(uint32_t old_vpn, uint32_t new_vpn, ui8* buf, ui8* spare, NDM ndm) {
+    uint32_t old_vbn, new_vbn, old_bn, new_bn, old_pn, new_pn;
     int status, action;
-    NDM ndm = ndm_ptr;
 
     // Grab exclusive access to TargetNDM internals.
     semPend(ndm->sem, WAIT_FOREVER);
@@ -1004,23 +999,19 @@ static int ftl_xfr_page(ui32 old_vpn, ui32 new_vpn, ui8* buf, ui8* spare, void*
 } //lint !e818
 
 #if INC_FTL_NDM_MLC
-// pair_offset: FTL driver function (MLC NAND) - pair offset
+// ndmPairOffset: FTL driver function (MLC NAND) - pair offset
 //
 //      Inputs: page_offset = page offset within block
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: Pair page offset within block if any, page offset
 //              otherwise
 //
-static ui32 pair_offset(ui32 page_offset, void* ndm_ptr) {
-    NDM ndm = ndm_ptr;
-
+uint32_t ndmPairOffset(uint32_t page_offset, CNDM ndm) {
     return ndm->pair_offset(page_offset, ndm->dev);
 }
 #endif // INC_FTL_NDM_MLC
 
-// Global Function Definitions
-
 // ndmMarkBadBlock: Mark virtual block bad and do bad block recovery
 //
 //      Inputs: ndm = pointer to NDM control block
@@ -1296,12 +1287,12 @@ ui32 ndmGetNumVBlocks(CNDM ndm) {
 //
 //      Inputs: ndm = pointer to NDM control block
 //              part_num = NDM partition number
-//              ftl = pointer to FTL config structure
+//              ftl_cfg = pointer to FTL config structure
 //              xfs = XFS volume information
 //
 //     Returns: 0 on success, -1 on error
 //
-int ndmAddVolFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, XfsVol* xfs) {
+int ndmAddVolFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl_cfg, XfsVol* xfs) {
     NDMPartition* part;
 
     // Check partition number.
@@ -1313,32 +1304,17 @@ int ndmAddVolFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, XfsVol* xfs) {
     if (part->first_block + part->num_blocks > ndm->num_vblks)
         return FsError2(NDM_CFG_ERR, ENOSPC);
 
-    // Set the fields dependent on flash geometry.
-    ftl->page_size = ndm->page_size;
-    ftl->eb_size = ndm->eb_size;
-    ftl->block_size = ndm->block_size;
-
-    // Set up the non-customizable part of the FTL/XFS drivers.
-    ftl->ndm = ndm;
-    ftl->start_page = part->first_block * ndm->pgs_per_blk;
-    ftl->num_blocks = part->num_blocks;
-    ftl->type = ndm->dev_type;
+    // Assign the NDM-supplied configuration.
+    ftl_cfg->page_size = ndm->page_size;
+    ftl_cfg->eb_size = ndm->eb_size;
+    ftl_cfg->block_size = ndm->block_size;
+    ftl_cfg->ndm = ndm;
+    ftl_cfg->start_page = part->first_block * ndm->pgs_per_blk;
+    ftl_cfg->num_blocks = part->num_blocks;
     xfs->name = part->name;
 
-    // Provide the TargetNDM lower-level interface to TargetFTL-NDM.
-    ftl->erase_block = ndmEraseBlock;
-    ftl->write_data_and_spare = ftl_wr_pg;
-    ftl->write_pages = ndmWritePages;
-    ftl->read_spare = ftl_rd_spare;
-    ftl->read_pages = ndmReadPages;
-    ftl->page_check = ftl_check_pg;
-    ftl->transfer_page = ftl_xfr_page;
-#if INC_FTL_NDM_MLC
-    ftl->pair_offset = pair_offset;
-#endif
-
     // Add an FTL to this partition. Return status.
-    return FtlnAddVol(ftl, xfs);
+    return FtlnAddVol(ftl_cfg, xfs);
 }
 
 // ndmReadPages: FTL driver function - read multiple consecutive
@@ -1346,20 +1322,19 @@ int ndmAddVolFTL(NDM ndm, ui32 part_num, FtlNdmVol* ftl, XfsVol* xfs) {
 //
 //      Inputs: vpn = starting virtual page number
 //              count = number of consecutive virtual pages to read
-//              data = pointer to buffer to copy main page data to
+//              buf = pointer to buffer to copy main page data to
 //              spare = points to array of page spare data sets
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: -2 on fatal error, -1 on error, 0 on success, 1 if
 //              block needs to be recycled
 //
-int ndmReadPages(ui32 vpn, ui32 count, void* data, void* spare, void* ndm_ptr) {
-    NDM ndm = ndm_ptr;
+int ndmReadPages(uint32_t vpn, uint32_t count, ui8* buf, ui8* spare, NDM ndm) {
     int status;
 
     // If NDM driver supplies read_pages(), use it.
     if (ndm->read_pages) {
-        ui32 vbn, bn, pn;
+        uint32_t vbn, bn, pn;
 
         // Compute the virtual block number based on virtual page number.
         vbn = vpn / ndm->pgs_per_blk;
@@ -1382,7 +1357,7 @@ int ndmReadPages(ui32 vpn, ui32 count, void* data, void* spare, void* ndm_ptr) {
         pn = bn * ndm->pgs_per_blk + vpn % ndm->pgs_per_blk;
 
         // Read pages.
-        status = ndm->read_pages(pn, count, data, spare, ndm->dev);
+        status = ndm->read_pages(pn, count, buf, spare, ndm->dev);
 
         // Release exclusive access to NDM and return status.
         semPostBin(ndm->sem);
@@ -1391,14 +1366,13 @@ int ndmReadPages(ui32 vpn, ui32 count, void* data, void* spare, void* ndm_ptr) {
 
     // Else loop over all pages, reading one at a time.
     else {
-        ui32 i;
+        uint32_t i;
         int rd_status;
-        ui8* buf = data;
 
         // Loop over virtual pages.
         for (status = 0, i = 0; i < count; ++i, ++vpn, buf += ndm->page_size) {
             // Issue current page read request.
-            rd_status = read_page(vpn, buf, ndm_ptr);
+            rd_status = read_page(vpn, buf, ndm);
 
             // If error, return.
             if (rd_status < 0)
@@ -1421,13 +1395,12 @@ int ndmReadPages(ui32 vpn, ui32 count, void* data, void* spare, void* ndm_ptr) {
 //              count = number of consecutive virtual pages to write
 //              data = points to array of page main data sets
 //              spare = points to array of page spare data sets
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: -1 on error, 0 on success
 //
-int ndmWritePages(ui32 vpn, ui32 count, const void* data, void* spare, void* ndm_ptr) {
+int ndmWritePages(uint32_t vpn, uint32_t count, const ui8* data, ui8* spare, NDM ndm) {
     int action, rc = 0;
-    NDM ndm = ndm_ptr;
 
     // Ensure all writes are to the same virtual block.
     PfAssert(count);
@@ -1444,7 +1417,7 @@ int ndmWritePages(ui32 vpn, ui32 count, const void* data, void* spare, void* ndm
 
     // If NDM driver supplies write_pages(), use it.
     if (ndm->write_pages) {
-        ui32 vbn;
+        uint32_t vbn;
 
         // Compute the virtual block number based on virtual page number.
         vbn = vpn / ndm->pgs_per_blk;
@@ -1455,11 +1428,11 @@ int ndmWritePages(ui32 vpn, ui32 count, const void* data, void* spare, void* ndm
 
         // Writing to flash until success or failure other than bad block.
         for (;;) {
-            ui32 bn, pn;
+            uint32_t bn, pn;
 
             // Get the physical block number from virtual one.
             bn = get_pbn(ndm, vbn, WR_MAPPING);
-            if (bn == (ui32)-1) {
+            if (bn == (uint32_t)-1) {
                 rc = -1;
                 break;
             }
@@ -1489,7 +1462,7 @@ int ndmWritePages(ui32 vpn, ui32 count, const void* data, void* spare, void* ndm
 
     // Else loop over all pages, writing one at a time.
     else {
-        ui32 past = vpn + count;
+        uint32_t past = vpn + count;
         const ui8* curr_data = data;
         ui8* curr_spare = spare;
 
@@ -1634,12 +1607,11 @@ int ndmWritePartition(NDM ndm, const NDMPartition* part, ui32 part_num, const ch
 // ndmEraseBlock: Erase a block
 //
 //      Inputs: vpn = base virtual page for block to be erased
-//              ndm_ptr = NDM control block handle
+//              ndm = NDM control block handle
 //
 //     Returns: 0 on success, -1 on fatal error
 //
-int ndmEraseBlock(ui32 vpn, void* ndm_ptr) {
-    NDM ndm = ndm_ptr;
+int ndmEraseBlock(ui32 vpn, NDM ndm) {
     ui32 vbn, bn, pn;
     int status;
 
diff --git a/zircon/system/ulib/ftl/ndm/ndmp.h b/zircon/system/ulib/ftl/ndm/ndmp.h
index 5e0a408fc66b613f6368ee96eedc4b7f8ad0fe74..d3600473a0590edf819cf127c6afb8a7990832d2 100644
--- a/zircon/system/ulib/ftl/ndm/ndmp.h
+++ b/zircon/system/ulib/ftl/ndm/ndmp.h
@@ -11,6 +11,7 @@
 #include <stdio.h>
 #include <errno.h>
 
+#include <ftl_private.h>
 #include <sys.h>
 #include <kprivate/ndm.h>
 
@@ -131,7 +132,6 @@ struct ndm {
     ui32 pgs_per_blk;  // number of pages in a block
     ui32 page_size;    // page size in bytes
     ui8 eb_size;       // spare area size in bytes
-    ui8 dev_type;      // NAND device type
 };
 
 //
diff --git a/zircon/system/ulib/ftl/utils/aalloc.c b/zircon/system/ulib/ftl/utils/aalloc.c
index 3441ec93470df9dfb42172a4d65e3381c230dbc7..aa4e1008f945dd76b8c388b0cbe58f81f5267da5 100644
--- a/zircon/system/ulib/ftl/utils/aalloc.c
+++ b/zircon/system/ulib/ftl/utils/aalloc.c
@@ -6,9 +6,10 @@
 #include <stdlib.h>
 #include <sys.h>
 #include <bsp.h>
+#include <ftl_private.h>
 
 #ifndef CACHE_LINE_SIZE
-#error bsp.h must define CACHE_LINE_SIZE
+#error CACHE_LINE_SIZE is undefined
 #endif
 
 // Free allocated memory and clear pointer to it.
diff --git a/zircon/system/ulib/ftl/utils/crc32_tbl.c b/zircon/system/ulib/ftl/utils/crc32_tbl.c
index 313d9ce77d6cb399e23295e8e8fdb14c46a3d22e..712e05007d5d08adff8272b095fa67162b3987c9 100644
--- a/zircon/system/ulib/ftl/utils/crc32_tbl.c
+++ b/zircon/system/ulib/ftl/utils/crc32_tbl.c
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <sys.h>
+#include <ftl_private.h>
 
 // CRC Lookup Table.
 const ui32 Crc32Tbl[256] = {
diff --git a/zircon/system/ulib/ftl/utils/fsmem.c b/zircon/system/ulib/ftl/utils/fsmem.c
index a137cf7133856a9c95c08a37194ea60c711ad6ae..fccdd9fdfde81bab9e1c0c35be8c651fcb10041b 100644
--- a/zircon/system/ulib/ftl/utils/fsmem.c
+++ b/zircon/system/ulib/ftl/utils/fsmem.c
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <fsprivate.h>
 #include <stdlib.h>
+
+#include <ftl_private.h>
+#include <fsprivate.h>
 #include <sys.h>
 
 // Wrapper for malloc()
 void* FsMalloc(size_t size) {
     void* mem = malloc(size);
-    if (mem == NULL)
-        FsError(ENOMEM);
 
     return mem;
 }
@@ -18,8 +18,6 @@ void* FsMalloc(size_t size) {
 // Wrapper for calloc()
 void* FsCalloc(size_t nitems, size_t size) {
     void* mem = calloc(nitems, size);
-    if (mem == NULL)
-        FsError(ENOMEM);
 
     return mem;
 }
@@ -27,8 +25,6 @@ void* FsCalloc(size_t nitems, size_t size) {
 // Wrapper for aalloc()
 void* FsAalloc(size_t size) {
     void* mem = aalloc(size);
-    if (mem == NULL)
-        FsError(ENOMEM);
 
     return mem;
 }
diff --git a/zircon/system/ulib/ftl/utils/fsys.c b/zircon/system/ulib/ftl/utils/fsys.c
index 657beefbb2b8f5c407c587c48fc5991f05df74b2..496bd312ba8131744c4d546fa2d94cb2330bf383 100644
--- a/zircon/system/ulib/ftl/utils/fsys.c
+++ b/zircon/system/ulib/ftl/utils/fsys.c
@@ -2,11 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <sys.h>
+#include <ftl_private.h>
 #include <kprivate/fsprivate.h>
 
-#include <string.h>
-
 // Lookup for number of bits in half byte.
 const ui8 NumberOnes[] = {
     //0 1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
diff --git a/zircon/system/ulib/ftl/utils/fsysinit.c b/zircon/system/ulib/ftl/utils/fsysinit.c
index eb54ea1dccd5ac7284d04e73f98b61bebe4aae64..c4c75f175fe34d98347a75689787b027474f435f 100644
--- a/zircon/system/ulib/ftl/utils/fsysinit.c
+++ b/zircon/system/ulib/ftl/utils/fsysinit.c
@@ -6,6 +6,7 @@
 #include <stdio.h>
 
 #include "kernel.h"
+#include <ftl_private.h>
 #include "fsprivate.h"
 
 #include <lib/backtrace-request/backtrace-request.h>
@@ -38,5 +39,6 @@ int FtlInit(void) {
     FileSysSem = semCreate("fsys sem", 1, OS_FIFO);
     if (FileSysSem == NULL)
         return -1;
+
     return 0;
 }
diff --git a/zircon/system/ulib/ftl/utils/ftl_mc.c b/zircon/system/ulib/ftl/utils/ftl_mc.c
index 4653b220c346ce921a9ea1bba97ac7c25af90108..46b3a105f4bfdf0aee95add5a7e72b1569aba44e 100644
--- a/zircon/system/ulib/ftl/utils/ftl_mc.c
+++ b/zircon/system/ulib/ftl/utils/ftl_mc.c
@@ -3,7 +3,9 @@
 // found in the LICENSE file.
 
 #include <sys.h>
-#include "ftl_mc.h"
+
+#include <ftl_private.h>
+#include <ftl_mc.h>
 #include <fsprivate.h>
 
 // Configuration
diff --git a/zircon/system/ulib/ftl/inc/kprivate/ftl_mc.h b/zircon/system/ulib/ftl/utils/ftl_mc.h
similarity index 100%
rename from zircon/system/ulib/ftl/inc/kprivate/ftl_mc.h
rename to zircon/system/ulib/ftl/utils/ftl_mc.h
diff --git a/zircon/system/ulib/ftl/utils/kernel.h b/zircon/system/ulib/ftl/utils/kernel.h
index e2fa97750f6f1c0b458f1e24cfb9663e2b6f9db1..005ac3201b5c8f9aa5b18c44bb27f2949ed1a9cd 100644
--- a/zircon/system/ulib/ftl/utils/kernel.h
+++ b/zircon/system/ulib/ftl/utils/kernel.h
@@ -25,8 +25,6 @@ void semDelete(SEM* semp);
 void semPostBin(SEM sem);
 int semPend(SEM sem, int wait_opt);
 
-#define ENOMEM 12 // out of memory
-
 #ifdef __cplusplus
 }
 #endif