changeset 1476:f2453fae08d4

Added support for FAT filesystem. Several packages, see the individual ChangeLogs for details.
author nickg
date Mon, 19 Jan 2004 14:35:01 +0000
parents c9e09b1c18cb
children b300f8978722
files packages/ChangeLog packages/NEWS packages/devs/disk/synth/current/ChangeLog packages/devs/disk/synth/current/cdl/synthdisk.cdl packages/devs/disk/synth/current/src/synthdisk.c packages/devs/disk/v85x/edb_v850/current/ChangeLog packages/devs/disk/v85x/edb_v850/current/cdl/v85x_edb_v850_disk.cdl packages/devs/disk/v85x/edb_v850/current/src/cf_ata.h packages/devs/disk/v85x/edb_v850/current/src/v85x_edb_v850_disk.c packages/ecos.db packages/fs/fat/current/ChangeLog packages/fs/fat/current/cdl/fatfs.cdl packages/fs/fat/current/doc/README.txt packages/fs/fat/current/doc/fat16.ecm packages/fs/fat/current/doc/fatfd.ecm packages/fs/fat/current/doc/mkdisk0 packages/fs/fat/current/src/fatfs.c packages/fs/fat/current/src/fatfs.h packages/fs/fat/current/src/fatfs_ncache.c packages/fs/fat/current/src/fatfs_supp.c packages/fs/fat/current/src/fatfs_tcache.c packages/fs/fat/current/tests/fileio1.c packages/io/common/current/ChangeLog packages/io/common/current/include/config_keys.h packages/io/disk/current/ChangeLog packages/io/disk/current/cdl/io_disk.cdl packages/io/disk/current/include/disk.h packages/io/disk/current/include/diskio.h packages/io/disk/current/src/disk.c packages/services/blib/current/ChangeLog packages/services/blib/current/cdl/blib.cdl packages/services/blib/current/include/blib.h packages/services/blib/current/src/blib.c
diffstat 33 files changed, 9792 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/packages/ChangeLog
+++ b/packages/ChangeLog
@@ -1,3 +1,9 @@
+2004-01-19  Nick Garnett  <nickg@calivar.com>
+
+	* ecos.db: Added FAT12/16 filesystem packages.
+
+	* NEWS: Added line about FAT filesystem support.
+	
 2003-11-22  Andrew Lunn  <andrew.lunn@ascom.ch>
 
 	* ecos.db: Added libipsec package.
--- a/packages/NEWS
+++ b/packages/NEWS
@@ -1,3 +1,5 @@
+* Added support for FAT12/16 filesystems. Contributed by
+  Savin Zlobec of Elatec Engineering.
 * Support for Spanning Tree Protocol in the old OpenBSD stack
 * Add support for GPS-4020 (embedded ARM7 GPS receiver/controller)
 * Basic IPSEC and libipsec support. 
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/synth/current/ChangeLog
@@ -0,0 +1,56 @@
+2004-01-15  Nick Garnett  <nickg@calivar.com>
+
+	* cdl/synthdisk.cdl:
+	* src/synthdisk.c: 	
+	Added _FILENAME option for disk instances to map device to an
+	arbitrary cinfigured file.
+	
+	* src/synthdisk.c: Removed block_pos arguments from read
+	and write calls: it is not necessary.
+
+2003-09-01 Savin Zlobec <savin@elatec.si>
+
+        * cdl/synthdisk.cdl: 
+        * src/synthdisk.c: 
+        Updated to work with the current disk interface.
+
+2003-08-19 Savin Zlobec <savin@elatec.si>
+
+        * cdl/synthdisk.cdl:
+        * src/synthdisk.c:
+        A synthetic block device driver.
+
+
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/synth/current/cdl/synthdisk.cdl
@@ -0,0 +1,166 @@
+# ====================================================================
+#
+#      synthdisk.cdl
+#
+#      Synthetic target disk package.
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003 Savin Zlobec 
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      savin
+# Contributors:
+# Date:           2003-06-18
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_DISK_ECOSYNTH {
+    display     "Synthetic target disk driver"
+    
+    parent      CYGPKG_IO_DISK_DEVICES
+    active_if   CYGPKG_IO_DISK
+    active_if   CYGPKG_HAL_SYNTH
+
+    cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 {
+    	display         "Provide disk 0 device"
+        flavor          bool
+        default_value   0
+        description     "Synthetic disk 0 driver."
+        
+        cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME {
+            display       "Device name for ecosynth disk 0 device"
+            flavor        data
+            default_value {"\"/dev/disk0/\""}
+            description   "This is the device name used to access this
+                           device in eCos. Note that the trailing slash
+                           character must be present."
+        }
+
+        cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME {
+            display       "Linux file name for ecosynth disk 0 device"
+            flavor        data
+            default_value {"\"disk0.img\""}
+            description   "This is the name of the file in the Linux filesystem that
+                           the driver will use to simulate a real disk. It should have
+                           been formatted with a DOS filesystem image before being used.
+                           The driver may also be pointed at real devices. For example
+                           setting this option to \"/dev/fd\" will cause the driver to access
+                           any real floppy disk inserted in the host's floppy drive."
+        }
+
+        cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE {
+            display       "Size in bytes for ecosynth disk 0"
+            flavor        data
+            default_value 10240000
+            legal_values  512 to 99999999
+            requires      { (CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE % 512) == 0 }
+            description   "
+                This option specifies the size in bytes used
+                for the ecosynth disk 0 device."
+        }
+
+        cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR {
+            display       "Expect MBR in synth disk 0 image"
+            flavor        bool
+            default_value 0
+            description   "
+                This option controls the scanning of MBR in synthetic
+                disk 0 image."
+        }
+        
+        cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS {
+            display       "Number of cylinders on synth disk 0"
+            flavor        data
+            default_value 0
+            active_if     CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR
+            description   "
+                This option controls the number of cylinders on
+                synthetic disk 0. It is only needed if
+                CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set."
+        }
+        
+        cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS {
+            display       "Number of heads on synth disk 0"
+            flavor        data
+            default_value 0
+            active_if     CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR
+            description   "
+                This option controls the number of heads on
+                synthetic disk 0. It is only needed if
+                CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set."
+        }
+       
+        cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS {
+            display       "Number of sectors per track on synth disk 0"
+            flavor        data
+            default_value 0
+            active_if     CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR
+            description   "
+                This option controls the number of sectors per track on
+                synthetic disk 0.It is only needed if
+                CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set."
+        }
+    }
+
+    cdl_component CYGPKG_DEVS_DISK_ECOSYNTH_OPTIONS {
+    	display     "Build options"
+	    active_if   { CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 }
+	    flavor      none
+	    compile     -library=libextras.a synthdisk.c
+	    description "
+	        Package-specific build options including control over compiler
+	        flags used only in building this package."
+
+	    cdl_option CYGPKG_DEVS_DISK_ECOSYNTH_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are used in addition
+                to the set of global flags."
+	    }
+        cdl_option CYGPKG_DEVS_DISK_ECOSYNTH_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building this package. These flags are removed from
+                the set of global flags if present."
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/synth/current/src/synthdisk.c
@@ -0,0 +1,299 @@
+//==========================================================================
+//
+//      synthdisk.c
+//
+//      Disk device driver for the synthetic target 
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    savin
+// Contributors: 
+// Date:         2003-06-18
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/devs_disk_ecosynth.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/disk.h>
+
+#include <stdio.h> // sprintf
+
+// ----------------------------------------------------------------------------
+
+//#define DEBUG 1
+
+// ----------------------------------------------------------------------------
+
+typedef struct {
+    int        num;
+    cyg_uint32 cylinders_num;
+    cyg_uint32 heads_num;
+    cyg_uint32 sectors_num;
+    cyg_uint32 size;
+    int        filefd;
+    char       *filename;
+} synth_disk_info_t;
+
+// ----------------------------------------------------------------------------
+
+static cyg_bool synth_disk_init(struct cyg_devtab_entry *tab);
+
+static Cyg_ErrNo synth_disk_read(disk_channel *chan, 
+                                 void         *buf,
+                                 cyg_uint32    len,
+                                 cyg_uint32    block_num);
+
+static Cyg_ErrNo synth_disk_write(disk_channel *chan, 
+                                  const void   *buf,
+                                  cyg_uint32    len,
+                                  cyg_uint32    block_num);
+
+static Cyg_ErrNo synth_disk_get_config(disk_channel *chan, 
+                                       cyg_uint32    key,
+                                       const void   *xbuf, 
+                                       cyg_uint32   *len);
+
+static Cyg_ErrNo synth_disk_set_config(disk_channel *chan, 
+                                       cyg_uint32    key,
+                                       const void   *xbuf, 
+                                       cyg_uint32   *len);
+
+static Cyg_ErrNo synth_disk_lookup(struct cyg_devtab_entry  **tab,
+                                   struct cyg_devtab_entry   *sub_tab,
+                                   const char                *name);
+
+static DISK_FUNS(synth_disk_funs, 
+                 synth_disk_read, 
+                 synth_disk_write, 
+                 synth_disk_get_config,
+                 synth_disk_set_config
+);
+
+// ----------------------------------------------------------------------------
+
+#define SYNTH_DISK_INSTANCE(_number_,_mbr_supp_, _cyl_,_hpt_,_spt_)        \
+static synth_disk_info_t synth_disk_info##_number_ = {                     \
+    num:           _number_,                                               \
+    cylinders_num: _cyl_,                                                  \
+    heads_num:     _hpt_,                                                  \
+    sectors_num:   _spt_,                                                  \
+    size:          CYGNUM_IO_DISK_ECOSYNTH_DISK##_number_##_SIZE,          \
+    filefd:        -1,                                                     \
+    filename:      CYGDAT_IO_DISK_ECOSYNTH_DISK##_number_##_FILENAME       \
+};                                                                         \
+static DISK_CHANNEL(synth_disk_channel##_number_,                          \
+                    synth_disk_funs,                                       \
+                    synth_disk_info##_number_,                             \
+                    _mbr_supp_                                             \
+);                                                                         \
+BLOCK_DEVTAB_ENTRY(synth_disk_io##_number_,                                \
+             CYGDAT_IO_DISK_ECOSYNTH_DISK##_number_##_NAME,                \
+             0,                                                            \
+             &cyg_io_disk_devio,                                           \
+             synth_disk_init,                                              \
+             synth_disk_lookup,                                            \
+             &synth_disk_channel##_number_                                 \
+);
+
+// ----------------------------------------------------------------------------
+
+#ifdef CYGVAR_DEVS_DISK_ECOSYNTH_DISK0
+# ifndef CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR
+SYNTH_DISK_INSTANCE(0, false, 0, 0, 0);
+# else
+SYNTH_DISK_INSTANCE(0, true, CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS,
+                             CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS,
+                             CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS);
+# endif
+#endif
+
+// ----------------------------------------------------------------------------
+
+static cyg_bool 
+synth_disk_init(struct cyg_devtab_entry *tab)
+{
+    disk_channel      *chan       = (disk_channel *) tab->priv;
+    synth_disk_info_t *synth_info = (synth_disk_info_t *) chan->dev_priv;
+    bool result = true;
+
+    if (chan->init) 
+        return true;
+
+#ifdef DEBUG
+    diag_printf("synth disk %d init size=%d\n", 
+                synth_info->num, synth_info->size);
+#endif
+   
+    synth_info->filefd = cyg_hal_sys_open(synth_info->filename, 
+            CYG_HAL_SYS_O_RDWR,
+            CYG_HAL_SYS_S_IRWXU|CYG_HAL_SYS_S_IRWXG|CYG_HAL_SYS_S_IRWXO);
+
+    if (-ENOENT == synth_info->filefd)
+    {
+        synth_info->filefd = cyg_hal_sys_open(synth_info->filename, 
+            CYG_HAL_SYS_O_RDWR|CYG_HAL_SYS_O_CREAT, 0644);
+        
+        if (synth_info->filefd >= 0)
+        {
+            unsigned char b = 0x00;
+            int i;
+        
+            for (i = 0; i < synth_info->size; i++)
+                cyg_hal_sys_write(synth_info->filefd, &b, 1);    
+        }
+    }
+
+    if (synth_info->filefd < 0)
+    {
+        CYG_ASSERT(false, "Can't open/create disk image file");
+        return false;
+    }
+   
+    if (result)
+    {
+        cyg_disk_identify_t ident;
+       
+        ident.serial[0]       = '\0';
+        ident.firmware_rev[0] = '\0';
+        ident.model_num[0]    = '\0';
+        ident.lba_sectors_num = synth_info->size / 512;
+        ident.cylinders_num   = synth_info->cylinders_num;
+        ident.heads_num       = synth_info->heads_num;
+        ident.sectors_num     = synth_info->sectors_num;
+ 
+        if (!(chan->callbacks->disk_init)(tab))
+            return false;
+        if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident))
+            return false;
+    }
+    return result;
+}
+
+static Cyg_ErrNo 
+synth_disk_lookup(struct cyg_devtab_entry **tab, 
+                  struct cyg_devtab_entry  *sub_tab,
+                  const char               *name)
+{
+    disk_channel *chan = (disk_channel *) (*tab)->priv;
+    return (chan->callbacks->disk_lookup(tab, sub_tab, name));
+}
+
+static Cyg_ErrNo 
+synth_disk_read(disk_channel *chan, 
+                void         *buf,
+                cyg_uint32    len,
+                cyg_uint32    block_num)
+{
+    synth_disk_info_t *synth_info = (synth_disk_info_t *)chan->dev_priv;
+
+#ifdef DEBUG
+    diag_printf("synth disk read block %d\n", block_num);
+#endif
+    
+    if (synth_info->filefd >= 0)
+    {
+        cyg_hal_sys_lseek(synth_info->filefd, 
+                          block_num * chan->info->block_size,
+                          CYG_HAL_SYS_SEEK_SET);
+        cyg_hal_sys_read(synth_info->filefd, buf, len);
+        return ENOERR;
+    }
+    return -EIO; 
+}
+
+static Cyg_ErrNo 
+synth_disk_write(disk_channel *chan,
+                 const void   *buf,
+                 cyg_uint32    len,
+                 cyg_uint32    block_num)
+{
+    synth_disk_info_t *synth_info = (synth_disk_info_t *)chan->dev_priv;
+
+#ifdef DEBUG
+    diag_printf("synth disk write block %d\n", block_num);
+#endif
+ 
+    if (synth_info->filefd >= 0)
+    {
+        cyg_hal_sys_lseek(synth_info->filefd, 
+                          block_num * chan->info->block_size,
+                          CYG_HAL_SYS_SEEK_SET);
+        cyg_hal_sys_write(synth_info->filefd, buf, len);
+//        cyg_hal_sys_fdatasync(synth_info->filefd);
+        return ENOERR;
+    }
+    return -EIO; 
+}
+
+static Cyg_ErrNo
+synth_disk_get_config(disk_channel *chan, 
+                      cyg_uint32    key,
+                      const void   *xbuf, 
+                      cyg_uint32   *len)
+{
+
+#ifdef DEBUG
+    diag_printf("synth disk get config\n");
+#endif
+    
+    return -EINVAL;
+}
+
+static Cyg_ErrNo
+synth_disk_set_config(disk_channel *chan, 
+                      cyg_uint32    key,
+                      const void   *xbuf, 
+                      cyg_uint32   *len)
+{
+
+#ifdef DEBUG
+    diag_printf("synth disk set config\n");
+#endif
+ 
+    return -EINVAL;
+}
+
+// ----------------------------------------------------------------------------
+// EOF synthdisk.c
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/v85x/edb_v850/current/ChangeLog
@@ -0,0 +1,46 @@
+2004-01-15  Nick Garnett  <nickg@calivar.com>
+
+	* src/v85x_edb_v850_disk.c: Removed block_pos arguments from read
+	and write calls: it is not necessary.
+
+2003-09-01 Savin Zlobec <savin@elatec.si>
+
+        * cdl/v85x_edb_v850_disk.cdl:
+        * src/v85x_edb_v850_disk.c:
+        * src/cf_ata.h:
+        A Elatec development board CF disk device driver
+
+
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/v85x/edb_v850/current/cdl/v85x_edb_v850_disk.cdl
@@ -0,0 +1,71 @@
+# ====================================================================
+#
+#      v85x_edb_v850_disk.cdl
+#
+#      Elatec v850 development board disk package.
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003 Savin Zlobec 
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      savin
+# Contributors:
+# Date:           2003-08-21
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_DISK_V85X_EDB_V850 {
+    display     "Disk driver for Elatec v850 development board"
+    
+    parent      CYGPKG_IO_DISK_DEVICES
+    active_if   CYGPKG_IO_DISK
+    active_if   CYGPKG_HAL_V85X_EDB_V850
+
+    compile     -library=libextras.a   v85x_edb_v850_disk.c
+    
+    cdl_component CYGVAR_DEVS_DISK_V85X_EDB_V850_DISK0 {
+    	display         "Provide disk 0 device"
+        flavor          bool
+        default_value   0
+        description     "Elatec v850 development board disk 0 driver."
+        
+        cdl_option CYGDAT_IO_DISK_V85X_EDB_V850_DISK0_NAME {
+            display       "Device name for disk 0 device"
+            flavor        data
+            default_value {"\"/dev/disk0/\""}
+        }
+    }
+}
+
+# EOF v85x_edb_v850_disk.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/v85x/edb_v850/current/src/cf_ata.h
@@ -0,0 +1,183 @@
+#ifndef CYGONCE_CF_ATA_H
+#define CYGONCE_CF_ATA_H
+//==========================================================================
+//
+//      cf_ata.h
+//
+//      CompactFlash ATA interface definitions
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    savin 
+// Contributors: 
+// Date:         2003-08-21
+// Purpose:      
+// Description:  
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+// -------------------------------------------------------------------------
+// CF Error Register bits 
+//
+#define CF_EREG_BBK   (1<<7) // bad block detected
+#define CF_EREG_UNC   (1<<6) // uncorrectable error
+#define CF_EREG_IDNF  (1<<4) // req sector ID is in error or cannot be found
+#define CF_EREG_ABORT (1<<2) // command aborted
+#define CF_EREG_AMNF  (1<<0) // general error
+
+// -------------------------------------------------------------------------
+// CF Status Register bits 
+//
+#define CF_SREG_BUSY (1<<7) // busy (no other bits in this register are valid)
+#define CF_SREG_RDY  (1<<6) // ready to accept a command
+#define CF_SREG_DWF  (1<<5) // write fault has occurred
+#define CF_SREG_DSC  (1<<4) // card is ready
+#define CF_SREG_DRQ  (1<<3) // information req transfer to/from the host 
+                            // through the Data register
+#define CF_SREG_CORR (1<<2) // correctable data error (data corrected) 
+#define CF_SREG_IDX  (1<<1) // always 0
+#define CF_SREG_ERR  (1<<0) // command has ended in error (see error reg)
+
+// -------------------------------------------------------------------------
+// CF Device Control Register bits 
+//
+#define CF_DREG_SWRST (1<<2) // soft reset
+#define CF_DREG_IEN   (1<<1) // interrupt enable (0 - enabled, 1 - disabled) 
+
+// -------------------------------------------------------------------------
+// CF ATA Command Set
+//
+// FR  - Features register
+// SC  - Sector count register
+// SN  - Sector number register
+// CY  - Cylinder registers
+// DH  - Card/Drive/Head register
+// LBA - Logical Block Address Mode Supported
+// 
+// Y - The register contains a valid parameter for this command
+//     For the Drive/Head Register Y means both the CF Card and head
+//     parameters are used
+// D - Only the CompactFlash Card parameter is valid and not the head parameter
+//
+//                                                 Note  FR  SC  SN  CY  DH  LBA
+#define CF_ATA_CHECK_POWER_MODE_CMD        0xE5 //   -   -   -   -   -   D   -
+#define CF_ATA_EXE_DRIVE_DIAG_CMD          0x90 //   -   -   -   -   -   D   -
+#define CF_ATA_ERASE_SECTORS_CMD           0xC0 //   2   -   Y   Y   Y   Y   Y
+#define CF_ATA_FORMAT_TRACK_CMD            0x50 //   -   -   Y   -   Y   Y   Y 
+#define CF_ATA_IDENTIFY_DRIVE_CMD          0xEC //   -   -   -   -   -   D   -
+#define CF_ATA_IDLE_CMD                    0xE3 //   -   -   Y   -   -   D   -
+#define CF_ATA_IDLE_IMMEDIATE_CMD          0xE1 //   -   -   -   -   -   D   -
+#define CF_ATA_INIT_DRIVE_PARAMS_CMD       0x91 //   -   -   Y   -   -   Y   -
+#define CF_ATA_READ_BUFFER_CMD             0xE4 //   -   -   -   -   -   D   -
+#define CF_ATA_READ_MULTIPLE_CMD           0xC4 //   -   -   Y   Y   Y   Y   Y
+#define CF_ATA_READ_LONG_SECTOR_CMD        0x22 //   -   -   -   Y   Y   Y   Y
+#define CF_ATA_READ_SECTORS_CMD            0x20 //   -   -   Y   Y   Y   Y   Y
+#define CF_ATA_READ_VERIFY_SECTORS_CMD     0x40 //   -   -   Y   Y   Y   Y   Y
+#define CF_ATA_RECALIBRATE_CMD             0x10 //   -   -   -   -   -   D   -
+#define CF_ATA_REQUEST_SENSE_CMD           0x03 //   1   -   -   -   -   D   -
+#define CF_ATA_SEEK_CMD                    0x70 //   -   -   -   Y   Y   Y   Y
+#define CF_ATA_SET_FEATURES_CMD            0xEF //   -   Y   -   -   -   D   -
+#define CF_ATA_SET_MULTIPLE_MODE_CMD       0xC6 //   -   -   Y   -   -   D   -
+#define CF_ATA_SET_SLEEP_MODE_CMD          0xE6 //   -   -   -   -   -   D   -
+#define CF_ATA_STAND_BY_CMD                0xE2 //   -   -   -   -   -   D   -
+#define CF_ATA_STAND_BY_IMMEDIATE_CMD      0xE0 //   -   -   -   -   -   D   -  
+#define CF_ATA_TRANSLATE_SECTOR_CMD        0x87 //   1   -   Y   Y   Y   Y   Y
+#define CF_ATA_WEAR_LEVEL_CMD              0xF5 //   1   -   -   -   -   Y   -
+#define CF_ATA_WRITE_BUFFER_CMD            0xE8 //   -   -   -   -   -   D   -
+#define CF_ATA_WRITE_LONG_SECTOR_CMD       0x32 //   -   -   -   Y   Y   Y   Y
+#define CF_ATA_WRITE_MULTIPLE_CMD          0xC5 //   -   -   Y   Y   Y   Y   Y
+#define CF_ATA_WRITE_MULTIPLE_WO_ERASE_CMD 0xCD //   2   -   Y   Y   Y   Y   Y
+#define CF_ATA_WRITE_SECTORS_CMD           0x30 //   -   -   Y   Y   Y   Y   Y
+#define CF_ATA_WRITE_SECTORS_WO_ERASE_CMD  0x38 //   2   -   Y   Y   Y   Y   Y
+#define CF_ATA_WRITE_VERIFY_SECTORS_CMD    0x3C //   -   -   Y   Y   Y   Y   Y
+//
+// Note 1 : These commands are not standard PC Card ATA commands but 
+//          provide additional functionality
+// Note 2 : These commands are not standard PC Card ATA commands and
+//          these features are no longer supported with the
+//          introduction of 256 Mbit flash technology. If one
+//          of these commands is issued, the sectors will be 
+//          erased but there will be no net gain in write
+//          performance when using the Write Without Erase command.          
+
+// -------------------------------------------------------------------------
+// CF ATA Identify drive command response
+//
+typedef struct cf_ata_identify_data_t 
+{        
+    cyg_uint16 general_conf;         // 00    : general configuration   
+    cyg_uint16 num_cylinders;        // 01    : number of cylinders (default CHS trans) 
+    cyg_uint16 reserved0;            // 02    : reserved 
+    cyg_uint16 num_heads;            // 03    : number of heads (default CHS trans) 
+    cyg_uint16 num_ub_per_track;     // 04    : number of unformatted bytes per track 
+    cyg_uint16 num_ub_per_sector;    // 05    : number of unformatted bytes per sector 
+    cyg_uint16 num_sectors;          // 06    : number of sectors per track (default CHS trans) 
+    cyg_uint16 num_card_sectors[2];  // 07-08 : number of sectors per card 
+    cyg_uint16 reserved1;            // 09    : reserved 
+    cyg_uint16 serial[10];           // 10-19 : serial number (string) 
+    cyg_uint16 buffer_type;          // 20    : buffer type (dual ported) 
+    cyg_uint16 buffer_size;          // 21    : buffer size in 512 increments 
+    cyg_uint16 num_ECC_bytes;        // 22    : number of ECC bytes passed on R/W Long cmds 
+    cyg_uint16 firmware_rev[4];      // 23-26 : firmware revision (string)
+    cyg_uint16 model_num[20];        // 27-46 : model number (string)
+    cyg_uint16 rw_mult_support;      // 47    : max number of sectors on R/W multiple cmds
+    cyg_uint16 reserved2;            // 48    : reserved 
+    cyg_uint16 capabilities;         // 49    : LBA, DMA, IORDY support indicator 
+    cyg_uint16 reserved3;            // 50    : reserved 
+    cyg_uint16 pio_xferc_timing;     // 51    : PIO data transfer cycle timing mode 
+    cyg_uint16 dma_xferc_timing;     // 52    : single word DMA data transfer cycle timing mode 
+    cyg_uint16 cur_field_validity;   // 53    : words 54-58 validity (0 == not valid) 
+    cyg_uint16 cur_cylinders;        // 54    : number of current cylinders 
+    cyg_uint16 cur_heads;            // 55    : number of current heads 
+    cyg_uint16 cur_spt;              // 56    : number of current sectors per track 
+    cyg_uint16 cur_capacity[2];      // 57-58 : current capacity in sectors 
+    cyg_uint16 mult_sectors;         // 59    : multiple sector setting 
+    cyg_uint16 lba_total_sectors[2]; // 60-61 : total sectors in LBA mode 
+    cyg_uint16 sw_dma;               // 62    : single word DMA support 
+    cyg_uint16 mw_dma;               // 63    : multi word DMA support 
+    cyg_uint16 apio_modes;           // 64    : advanced PIO transfer mode supported 
+    cyg_uint16 min_dma_timing;       // 65    : minimum multiword DMA transfer cycle 
+    cyg_uint16 rec_dma_timing;       // 66    : recommended multiword DMA cycle 
+    cyg_uint16 min_pio_timing;       // 67    : min PIO transfer time without flow control 
+    cyg_uint16 min_pio_iordy_timing; // 68    : min PIO transfer time with IORDY flow control 
+//  cyg_uint16 reserved4[187];       // 69-255: reserved 
+} cf_ata_identify_data_t;
+
+#endif // CYGONCE_CF_ATA_H
+
+// -------------------------------------------------------------------------
+// EOF cf_ata.h
new file mode 100644
--- /dev/null
+++ b/packages/devs/disk/v85x/edb_v850/current/src/v85x_edb_v850_disk.c
@@ -0,0 +1,406 @@
+//==========================================================================
+//
+//      v85x_edb_v850_disk.c
+//
+//      Elatec v850 development board disk driver 
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    savin
+// Contributors: 
+// Date:         2003-08-21
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/devs_disk_v85x_edb_v850.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/disk.h>
+
+#include <cf_ata.h>
+
+// ----------------------------------------------------------------------------
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define D(_args_) diag_printf _args_
+#else
+# define D(_args_)
+#endif
+
+// ----------------------------------------------------------------------------
+
+#include <cyg/hal/v850_common.h>
+
+static volatile unsigned char* P10  = (volatile unsigned char*)V850_REG_P10;
+static volatile unsigned char* PM10 = (volatile unsigned char*)V850_REG_PM10;
+static volatile unsigned char* PU10 = (volatile unsigned char*)V850_REG_PU10;
+
+#define CF_BASE 0x00380000
+
+#define CF_HW_INIT()                  \
+    do {                              \
+        *PU10 |= (1<<4);              \
+        *PM10 |= (1<<4);              \
+        *PM10 &= ~2;                  \
+    } while (0)
+
+#define CF_HW_RESET()                   \
+    do {                                \
+        int i;                          \
+        *P10 |=  2;                     \
+        for (i = 0; i < 500000; i++);   \
+        *P10 &= ~2;                     \
+        for (i = 0; i < 500000; i++);   \
+    } while (0)
+
+#define CF_HW_BUSY_WAIT()               \
+    do {                                \
+        while (0 == (*P10 & (1<<4)));   \
+    } while (0)
+
+// ----------------------------------------------------------------------------
+
+typedef struct {
+    volatile cyg_uint16 *base;
+} cf_disk_info_t;
+
+// ----------------------------------------------------------------------------
+
+static cyg_bool cf_disk_init(struct cyg_devtab_entry *tab);
+
+static Cyg_ErrNo cf_disk_read(disk_channel *chan, 
+                              void         *buf,
+                              cyg_uint32    len, 
+                              cyg_uint32    block_num); 
+        
+
+static Cyg_ErrNo cf_disk_write(disk_channel *chan, 
+                               const void   *buf,
+                               cyg_uint32    len, 
+                               cyg_uint32    block_num); 
+ 
+static Cyg_ErrNo cf_disk_get_config(disk_channel *chan, 
+                                    cyg_uint32    key,
+                                    const void   *xbuf, 
+                                    cyg_uint32   *len);
+
+static Cyg_ErrNo cf_disk_set_config(disk_channel *chan, 
+                                    cyg_uint32    key,
+                                    const void   *xbuf, 
+                                    cyg_uint32   *len);
+
+static Cyg_ErrNo cf_disk_lookup(struct cyg_devtab_entry **tab,
+                                struct cyg_devtab_entry  *sub_tab,
+                                const char               *name);
+
+static DISK_FUNS(cf_disk_funs, 
+                 cf_disk_read, 
+                 cf_disk_write, 
+                 cf_disk_get_config,
+                 cf_disk_set_config
+);
+
+// ----------------------------------------------------------------------------
+
+#define CF_DISK_INSTANCE(_number_,_base_,_mbr_supp_,_name_)           \
+static cf_disk_info_t cf_disk_info##_number_ = {                      \
+    base: (volatile cyg_uint16 *)_base_,                              \
+};                                                                    \
+static DISK_CHANNEL(cf_disk_channel##_number_,                        \
+                    cf_disk_funs,                                     \
+                    cf_disk_info##_number_,                           \
+                    _mbr_supp_                                        \
+);                                                                    \
+BLOCK_DEVTAB_ENTRY(cf_disk_io##_number_,                              \
+                   _name_,                                            \
+                   0,                                                 \
+                   &cyg_io_disk_devio,                                \
+                   cf_disk_init,                                      \
+                   cf_disk_lookup,                                    \
+                   &cf_disk_channel##_number_                         \
+);
+
+// ----------------------------------------------------------------------------
+
+#ifdef CYGVAR_DEVS_DISK_V85X_EDB_V850_DISK0
+CF_DISK_INSTANCE(0, CF_BASE, true, CYGDAT_IO_DISK_V85X_EDB_V850_DISK0_NAME);
+#endif
+
+// ----------------------------------------------------------------------------
+
+static __inline__ cyg_uint8
+get_status(volatile cyg_uint16 *base)
+{
+    cyg_uint16 val;
+    HAL_READ_UINT16(base + 7, val);
+    return (val & 0x00FF);
+}
+
+static __inline__ cyg_uint8
+get_error(volatile cyg_uint16 *base)
+{
+    cyg_uint16 val;
+    HAL_READ_UINT16(base + 6, val);
+    return ((val >> 8) & 0x00FF);
+}
+
+static __inline__ void
+set_dctrl(volatile cyg_uint16 *base, cyg_uint8 dbits)
+{
+    cyg_uint16 val = dbits;
+    HAL_WRITE_UINT16(base + 7, val);
+}
+
+static cyg_bool 
+exe_cmd(volatile cyg_uint16 *base,
+        cyg_uint8            cmd,
+        cyg_uint8            drive,
+        cyg_uint32           lba_addr,
+        cyg_uint8            sec_cnt)
+{
+    cyg_uint8  lba0_7;
+    cyg_uint16 lba8_23;
+    cyg_uint8  lba24_27;
+    cyg_uint8  drv;
+
+    lba0_7   = lba_addr & 0xFF;
+    lba8_23  = (lba_addr >> 8)  & 0xFFFF;
+    lba24_27 = (lba_addr >> 24) & 0x0F;
+
+    // drive and LBA addressing mode flag
+    if (0 == drive) drv = 0x40;
+    else            drv = 0x50;
+     
+    CF_HW_BUSY_WAIT();
+
+    HAL_WRITE_UINT16(base + 1, sec_cnt | (lba0_7 << 8));
+    HAL_WRITE_UINT16(base + 2, lba8_23);
+    HAL_WRITE_UINT16(base + 3, lba24_27 | drv | (cmd << 8));
+
+    CF_HW_BUSY_WAIT();
+    
+    return (0 == (get_status(base) & CF_SREG_ERR));
+}
+
+static void
+read_data(volatile cyg_uint16 *base,
+          void                *buf,
+          cyg_uint16           len)
+{
+    cyg_uint16 *bufp = (cyg_uint16 *)buf;
+    int i;
+
+    CF_HW_BUSY_WAIT();
+
+    for (i = 0; i < 512; i += 2)
+    {
+        cyg_uint16 data;
+        HAL_READ_UINT16(base + 4, data);
+        *bufp++ = data;
+    }
+
+    CF_HW_BUSY_WAIT();
+}
+
+static void
+write_data(volatile cyg_uint16 *base,
+           const void          *buf,
+           cyg_uint16           len)
+{
+    cyg_uint16 *bufp = (cyg_uint16 * const)buf;
+    int i;
+
+    CF_HW_BUSY_WAIT();
+
+    for (i = 0; i < 512; i += 2)
+    {
+        cyg_uint16 data;
+
+        data = *bufp++;
+
+        HAL_WRITE_UINT16(base + 4, data);
+    }
+    
+    CF_HW_BUSY_WAIT();
+}
+
+static void
+id_strcpy(char *dest, cyg_uint16 *src, cyg_uint16 size)
+{
+    int i;
+
+    for (i = 0; i < size; i+=2)
+    {
+        *dest++ = (char)(*src >> 8);
+        *dest++ = (char)(*src & 0x00FF);
+        src++;
+    }
+    *dest = '\0';
+}
+
+// ----------------------------------------------------------------------------
+
+static cyg_bool 
+cf_disk_init(struct cyg_devtab_entry *tab)
+{
+    disk_channel           *chan = (disk_channel *) tab->priv;
+    cf_disk_info_t         *info = (cf_disk_info_t *) chan->dev_priv;
+    cf_ata_identify_data_t *ata_id;
+    cyg_uint8               id_buf[sizeof(cf_ata_identify_data_t)];
+    cyg_disk_identify_t     ident;
+    
+    if (chan->init) 
+        return true;
+
+    D(("CF(%p) hw init\n", info->base));
+
+    CF_HW_INIT();
+    CF_HW_RESET();
+    
+    D(("CF(%p) identify drive\n", info->base));
+
+    if (!exe_cmd(info->base, CF_ATA_IDENTIFY_DRIVE_CMD, 0, 0, 0))
+    {
+        D(("CF(%p) error (%x)\n", info->base, get_error(info->base)));
+        return false; 
+    }
+    read_data(info->base, id_buf, sizeof(cf_ata_identify_data_t), 0);
+   
+    ata_id = (cf_ata_identify_data_t *)id_buf;
+
+    D(("CF(%p) general conf = %x\n", info->base, ata_id->general_conf));
+            
+    if (0x848A != ata_id->general_conf)
+        return false;
+    
+    id_strcpy(ident.serial,       ata_id->serial,       20);
+    id_strcpy(ident.firmware_rev, ata_id->firmware_rev, 8);
+    id_strcpy(ident.model_num,    ata_id->model_num,    40);
+    
+    ident.cylinders_num   = ata_id->num_cylinders;
+    ident.heads_num       = ata_id->num_heads;
+    ident.sectors_num     = ata_id->num_sectors;
+    ident.lba_sectors_num = ata_id->lba_total_sectors[1] << 16 | 
+                            ata_id->lba_total_sectors[0];
+    
+    if (!(chan->callbacks->disk_init)(tab))
+        return false;
+
+    if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident))
+        return false;
+
+    return true;
+}
+
+static Cyg_ErrNo 
+cf_disk_lookup(struct cyg_devtab_entry **tab, 
+               struct cyg_devtab_entry  *sub_tab,
+               const char *name)
+{
+    disk_channel *chan = (disk_channel *) (*tab)->priv;
+    return (chan->callbacks->disk_lookup)(tab, sub_tab, name);
+}
+
+static Cyg_ErrNo 
+cf_disk_read(disk_channel *chan, 
+             void         *buf,
+             cyg_uint32    len, 
+             cyg_uint32    block_num)
+{
+    cf_disk_info_t *info = (cf_disk_info_t *)chan->dev_priv;
+
+    D(("CF(%p) read block %d\n", info->base, block_num));
+
+    if (!exe_cmd(info->base, CF_ATA_READ_SECTORS_CMD, 0, block_num, 1))
+    {
+        D(("CF(%p) error (%x)\n", info->base, get_error(info->base)));
+        return -EIO; 
+    }
+    read_data(info->base, buf, len);
+        
+    return ENOERR;
+}
+
+static Cyg_ErrNo 
+cf_disk_write(disk_channel *chan, 
+              const void   *buf,
+              cyg_uint32    len, 
+              cyg_uint32    block_num)
+{
+    cf_disk_info_t *info = (cf_disk_info_t *)chan->dev_priv;
+
+    D(("CF(%p) write block %d\n", info->base, block_num));
+ 
+    if (!exe_cmd(info->base, CF_ATA_WRITE_SECTORS_CMD, 0, block_num, 1))
+    {
+        D(("CF(%p) error (%x)\n", info->base, get_error(info->base)));
+        return -EIO; 
+    }
+    write_data(info->base, buf, len);
+        
+    return ENOERR;
+}
+
+static Cyg_ErrNo
+cf_disk_get_config(disk_channel *chan, 
+                   cyg_uint32    key,
+                   const void   *xbuf, 
+                   cyg_uint32   *len)
+{
+    return -EINVAL;
+}
+
+static Cyg_ErrNo
+cf_disk_set_config(disk_channel *chan, 
+                   cyg_uint32    key,
+                   const void   *xbuf, 
+                   cyg_uint32   *len)
+{
+    return -EINVAL;
+}
+
+// ----------------------------------------------------------------------------
+// EOF v85x_edb_v850_disk.c
--- a/packages/ecos.db
+++ b/packages/ecos.db
@@ -5433,3 +5433,47 @@ package CYGPKG_VNC_SERVER {
 }
 
 # --------------------------------------------------------------------------
+
+package CYGPKG_DEVS_DISK_V85X_EDB_V850 {
+    alias           { "Elatec v850 development board disk driver" edb_v850_disk }
+    directory       devs/disk/v85x/edb_v850
+    script          v85x_edb_v850_disk.cdl
+    hardware
+    description     "Disk driver for the Elatec v850 development board." 
+}
+
+package CYGPKG_DEVS_DISK_ECOSYNTH {
+    alias           { "Synthetic target disk driver" ecosynthdisk ecosynth_disk_driver }
+    directory       devs/disk/synth
+    script          synthdisk.cdl
+    hardware
+    description     "Disk driver for the synthetic target."
+}
+
+package CYGPKG_IO_DISK {
+    alias           { "Disk device drivers" disk io_disk }
+    directory       io/disk
+    script          io_disk.cdl
+    description "
+        This package contains drivers for basic I/O services on
+        disk devices."
+}
+
+package CYGPKG_BLOCK_LIB {
+    alias       { "Block cache and access library" blib block_lib }
+    directory   services/blib
+    script      blib.cdl
+    description "
+        This package contains the block cache and access library."
+}
+
+package CYGPKG_FS_FAT {
+    alias       { "FAT Filesystem" fatfs fat_fs fs_fat }
+    directory   fs/fat
+    script      fatfs.cdl
+    description "
+        This package contains a FAT filesystem implementation."
+}
+
+
+# --------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/ChangeLog
@@ -0,0 +1,95 @@
+2004-01-19  Nick Garnett  <nickg@calivar.com>
+
+
+	* doc/README.txt: 
+	* doc/fatfs.ecm: 
+	* doc/fat16.ecm: 
+	* doc/mkdisk0:
+	Added some initial basic documentation, sample configurations and
+	shell script to manufacture test disk image.
+	
+2003-10-18 Savin Zlobec <savin@elatec.si>
+
+        * cdl/fatfs.cdl:
+        * src/fatfs.h:
+        * src/fatfs_ncache.c:
+        Fixed node allocation tresholds (there should be no
+        more unnecessary node memory allocation/deallocation).
+        Added more sanity checks and a cdl option to enable them.
+
+2003-10-17 Savin Zlobec <savin@elatec.si>
+    
+        * src/fatfs_supp.c: Fixed dos to unix date conversion.
+
+2003-10-16 Savin Zlobec <savin@elatec.si>
+
+        * src/fatfs.c: Fixed dir entry searching.
+        * src/fatfs_ncache.c: Added more sanity checks.
+
+2003-10-13 Savin Zlobec <savin@elatec.si>
+
+        * src/fatfs.h:
+        * src/fatfs.c:
+        * src/fatfs_supp.c:
+        Added FAT12 support and FAT type detection.
+        Fixed file deletion.
+        Fixed reusing of deleted directory entries.
+        Fixed free clusters search.
+        Fixed directory renaming.
+        Fixed out of space situation while writting.
+
+2003-09-01 Savin Zlobec <savin@elatec.si>
+
+        * cdl/fatfs.cdl:
+        * src/fatfs.h:
+        * src/fatfs.c:
+        * src/fatfs_supp.c:
+        * src/fatfs_tcache.c:
+        * src/fatfs_ncache.c:
+        Implemented block IO with BLib, fixed FAT data access
+        problems and added FAT table cache memory constraints. 
+        
+2003-07-07 Savin Zlobec <savin@elatec.si>
+
+        * cdl/fatfs.cdl:
+        * src/fatfs.h:
+        * src/fatfs.c:
+        * src/fatfs_supp.c:
+        * src/fatfs_ncache.c:
+        * src/fatfs_tcache.c:
+        A FAT16 filesystem implementation
+
+
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/cdl/fatfs.cdl
@@ -0,0 +1,138 @@
+# ====================================================================
+#
+#      fatfs.cdl
+#
+#      FAT Filesystem configuration data
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003 Savin Zlobec 
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      savin 
+# Contributors:   
+# Date:           2003-06-25
+#
+#####DESCRIPTIONEND####
+#
+# ====================================================================
+
+cdl_package CYGPKG_FS_FAT {
+    display         "FAT filesystem"
+    include_dir     cyg/fatfs
+
+    requires        CYGPKG_IO_FILEIO
+
+    requires        CYGPKG_ISOINFRA
+    requires        CYGINT_ISO_ERRNO
+    requires        CYGINT_ISO_ERRNO_CODES
+    requires        CYGPKG_MEMALLOC
+    requires        CYGPKG_BLOCK_LIB
+#    requires        CYGFUN_LIBC_STRING_BSD_FUNCS
+
+    implements      CYGINT_IO_FILEIO_FS
+    
+    compile         -library=libextras.a fatfs.c        \
+                                         fatfs_supp.c   \
+                                         fatfs_ncache.c \
+                                         fatfs_tcache.c
+
+    cdl_option      CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE {
+        display         "Node hash table size"
+        flavor          data
+        default_value   32
+        legal_values    1 to 9999999999
+        description     "This option controls the number of slots in the 
+                         hash table used to store file nodes using filenames 
+                         as keys."
+    }
+
+    cdl_option      CYGNUM_FS_FAT_NODE_ALLOC_THRESHOLD {
+        display         "Node allocation treshold"
+        flavor          data
+        default_value   16
+        legal_values    1 to 9999999999
+        description     "This option controls at which point the filesystem 
+                         starts reusing dead file nodes rather then allocating
+                         memory for new nodes."
+    }
+   
+    cdl_option      CYGNUM_FS_FAT_FAT_TABLE_CACHE_MEMSIZE {
+        display         "FAT table cache memory size"
+        flavor          data
+        default_value   10240
+        legal_values    1 to 9999999999
+        description     "This option controls the amount of memory used
+                         for the FAT table cache."
+    }
+
+    cdl_option      CYGNUM_FS_FAT_FAT_TABLE_CACHE_INCREMENT {
+        display         "FAT table cache size increment"
+        flavor          data
+        default_value   10
+        legal_values    1 to 9999999999
+        description     "This option controls the amount of memory by which 
+                         the per-file FAT table cache will grow."
+    }
+   
+    cdl_option      CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE {
+        display         "FAT block cache memory size"
+        flavor          data
+        default_value   10240 
+        legal_values    1 to 9999999999
+        description     "This option controls the amount of memory used for 
+                         the block cache."
+    }
+
+    cdl_option      CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS {
+        display         "Node cache extra checks"
+        flavor          bool
+        default_value   1
+        active_if       CYGPKG_INFRA_DEBUG && CYGDBG_USE_ASSERTS
+        description     "This option controls the inclusion of extra
+                         sanity checks in node cache code."
+    }
+    
+    # --------------------------------------------------------------------
+    
+    cdl_option      CYGPKG_FS_FAT_TESTS {
+        display         "FAT FS tests"
+        flavor          data
+        no_define
+        calculated      { "tests/fileio1.c" }
+        description     "This option specifies the set of tests for the 
+                         FAT FS package."
+    }
+}
+
+# ====================================================================
+# End of fatfs.cdl
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/doc/README.txt
@@ -0,0 +1,128 @@
+
+FAT Filesystem README
++++++++++++++++++++++        
+
+This file serves as documentation for the FAT filesystem until full
+documentation can be generated.
+
+Configuring FAT
+===============
+
+The FAT filesystem support consists of several packages:
+
+CYGPKG_FS_FAT
+        The FAT filesystem itself.
+
+CYGPKG_BLOCK_LIB
+        Block library. This actually implements the block cache. It
+        depends on the Linux compatibility library
+        (CYGPKG_LINUX_COMPAT) for support.
+
+CYGPKG_IO_DISK
+        Disk device IO support. This provides the top level disk
+        driver functions. It also interprets partition tables and
+        provides a separate access channel for each partition.
+        
+CYGPKG_DEVS_DISK_ECOSYNTH
+        This is the only low level device driver currently known to
+        work. This driver treats a file in the Linux filesystem as a
+        disk image.
+
+CYGPKG_DEVS_DISK_V85X_EDB_V850
+        This is a device driver for an IDE compatible Compact Flash
+        device on a platform that is otherwise unsupported. It has not
+        been tested.        
+
+In addition the above new packages, FAT support also relies on the
+following existing packages:
+
+CYGPKG_IO_FILEIO
+        The File IO package. This provides the API by which the FAT
+        filesystem is accessed.
+        
+CYGPKG_IO
+        Device IO package. This provides all the infrastructure for
+        the disk devices.
+        
+CYGPKG_LIBC_STRING
+        Strings library. This provides the string and memory move and
+        compare routines used by the filesystem.
+        
+CYGPKG_MEMALLOC
+        The FAT filesystem currently uses malloc() to allocate
+        its memory resources, so this package is required.
+
+The doc directory that contains this file contains a number of .ecm
+files that will add the FAT filesystem to most configurations. These
+should be used as a basis for creating new configurations.
+
+Device Drivers
+==============
+
+The disk device interface is divided into a generic layer in the
+CYGPKG_IO_DISK package and drivers for each device.
+
+Apart from providing the main API, the other thing that the DISK
+package does is to provide support for disk partitions. It interprets
+the on-disk data structures and provides an independent channel to
+access each partition independently. Partitions are referenced using
+an additional element to the device name. If the device is named
+"/dev/disk0" then "/dev/disk0/1" refers to partition 1, "/dev/disk0/2"
+to partition 2 and so on. If the disk is not partitioned, for example
+a floppy disk, then "/dev/disk0/0" refers to the whole disk.
+
+There are two device drivers currently supported:
+
+Linux Synthetic Driver
+----------------------
+
+This driver treats a disk file on Linux as a disk image and read or
+writes blocks from/to it to simulate disk IO. At present it supports
+just a single disk device image. There are a number of configuration
+options for this device, see the CDL file for details, and the .ecm
+files for examples.
+
+EDB V850 Compact Flash Device
+-----------------------------
+
+This uses an IDE interface to a Compact Flash device. This is actually
+for a platform that is not supported in the current source tree, so
+cannot be tested. However, it should be possible to adapt the code for
+other devices.
+
+Testing
+=======
+
+There is currently just one test available for the FAT filesystem,
+fileio1. This test will currently only run on the Linux synthetic
+target and needs a special disk image.
+
+To run the test you first need to configure the synthetic disk driver
+to access the disk image. This is most easily done by importing the
+fat16.ecm file. A complete build would look like this:
+
+$ mkdir linux.fat16
+$ cd linux.fat16
+$ ecosconfig new linux
+$ ecosconfig import $ECOS_REPOSITORY/fs/fat/current/doc/fat16.ecm
+$ ecosconfig tree
+$ make tests
+
+To make the disk image, first ensure that there is at least 10MB of
+space available in your /tmp directory. Add the following two lines to
+your ~/.mtoolsrc file (you may need to create it):
+
+drive z: file="/tmp/disk0.img" partition=1
+drive y: file="/tmp/disk0.img" partition=2
+
+Now run the mkdisk0 shell script from the fs/fat/doc directory:
+
+$ source $ECOS_REPOSITORY/fs/fat/current/doc/mkdisk0
+20000+0 records in
+20000+0 records out
+Warning: no active (bootable) partition present
+$
+
+Ignore the warning, we are not interested in booting this disk.
+
+It should now be possible to run the fileio1 test program.
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/doc/fat16.ecm
@@ -0,0 +1,57 @@
+cdl_savefile_version 1;
+cdl_savefile_command cdl_savefile_version {};
+cdl_savefile_command cdl_savefile_command {};
+cdl_savefile_command cdl_configuration { description hardware template package };
+cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value };
+
+cdl_configuration eCos {
+
+    package CYGPKG_IO_FILEIO current ;
+    package CYGPKG_IO current ;
+    package CYGPKG_LIBC_STRING current ;
+
+    package CYGPKG_DEVS_DISK_ECOSYNTH current ;
+    package CYGPKG_IO_DISK current ;
+    package CYGPKG_BLOCK_LIB current ;
+    package CYGPKG_LINUX_COMPAT current ;
+    package CYGPKG_FS_FAT current ;
+
+    package CYGPKG_MEMALLOC current ;
+
+};
+
+
+cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 {
+    user_value   1
+};
+
+cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME {
+    user_value {"/dev/disk0/"}
+};
+
+cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME {
+    user_value {"/tmp/disk0.img"}
+};
+
+cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE {
+    user_value 10240000
+};
+
+cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR {
+    user_value 1
+};
+        
+cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS {
+    user_value 100
+};
+        
+cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS {
+    user_value 5
+};
+       
+cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS {
+    user_value 40
+};
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/doc/fatfd.ecm
@@ -0,0 +1,45 @@
+cdl_savefile_version 1;
+cdl_savefile_command cdl_savefile_version {};
+cdl_savefile_command cdl_savefile_command {};
+cdl_savefile_command cdl_configuration { description hardware template package };
+cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value };
+cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value };
+
+cdl_configuration eCos {
+
+    package CYGPKG_IO_FILEIO current ;
+    package CYGPKG_IO current ;
+    package CYGPKG_LIBC_STRING current ;
+
+    package CYGPKG_DEVS_DISK_ECOSYNTH current ;
+    package CYGPKG_IO_DISK current ;
+    package CYGPKG_BLOCK_LIB current ;
+    package CYGPKG_LINUX_COMPAT current ;
+    package CYGPKG_FS_FAT current ;
+
+    package CYGPKG_MEMALLOC current ;
+
+};
+
+
+cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 {
+    user_value   1
+};
+
+cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME {
+    user_value {"/dev/disk0/"}
+};
+
+cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME {
+    user_value {"/dev/fd0"}
+};
+
+cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE {
+    user_value 1474560
+};
+
+cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR {
+    user_value 0
+};
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/doc/mkdisk0
@@ -0,0 +1,18 @@
+#! /bin/bash
+#
+# Add the following lines to your ~/.mtoolsrc file before running this script
+#
+# drive z: file="/tmp/disk0.img" partition=1
+# drive y: file="/tmp/disk0.img" partition=2
+#
+#
+#
+
+dd if=/dev/zero of=/tmp/disk0.img bs=512 count=20000
+mpartition -I   -t 100 -h 5 -s 40          Z:
+mpartition -cfa -t  49 -h 5 -s 40 -b 200   Z:
+mpartition -cf  -t  50 -h 5 -s 40 -b 10000 Y:
+mformat z:
+mformat y:
+
+# end
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/src/fatfs.c
@@ -0,0 +1,1278 @@
+//==========================================================================
+//
+//      fatfs.c
+//
+//      FAT file system
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2003 Savin Zlobec
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           savin (based on ramfs.c)
+// Original data:       nickg
+// Date:                2003-06-29
+// Purpose:             FAT file system
+// Description:         This is a FAT filesystem for eCos. 
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <pkgconf/io_fileio.h>
+#include <pkgconf/fs_fat.h>
+
+#include <cyg/kernel/ktypes.h>         // base kernel types
+#include <cyg/infra/cyg_trac.h>        // tracing macros
+#include <cyg/infra/cyg_ass.h>         // assertion macros
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cyg/kernel/kapi.h>
+#include <cyg/infra/diag.h>
+#include <cyg/fileio/fileio.h>
+#include <cyg/io/io.h>
+#include <blib/blib.h>
+
+#include "fatfs.h"
+
+//==========================================================================
+// Tracing support defines 
+
+#ifdef FATFS_TRACE_FS_OP
+# define TFS 1
+#else
+# define TFS 0
+#endif
+
+#ifdef FATFS_TRACE_FILE_OP
+# define TFO 1
+#else
+# define TFO 0
+#endif
+
+//==========================================================================
+// Forward definitions
+
+// Filesystem operations
+static int fatfs_mount  (cyg_fstab_entry *fste, cyg_mtab_entry *mte);
+static int fatfs_umount (cyg_mtab_entry *mte);
+static int fatfs_open   (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         int mode, cyg_file *fte);
+static int fatfs_unlink (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
+static int fatfs_mkdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
+static int fatfs_rmdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name);
+static int fatfs_rename (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                         cyg_dir dir2, const char *name2 );
+static int fatfs_link   (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1,
+                         cyg_dir dir2, const char *name2, int type );
+static int fatfs_opendir(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         cyg_file *fte );
+static int fatfs_chdir  (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         cyg_dir *dir_out );
+static int fatfs_stat   (cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         struct stat *buf);
+static int fatfs_getinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         int key, void *buf, int len );
+static int fatfs_setinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name,
+                         int key, void *buf, int len );
+
+// File operations
+static int fatfs_fo_read   (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int fatfs_fo_write  (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int fatfs_fo_lseek  (struct CYG_FILE_TAG *fp, off_t *pos, int whence );
+static int fatfs_fo_ioctl  (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com,
+                            CYG_ADDRWORD data);
+static int fatfs_fo_fsync  (struct CYG_FILE_TAG *fp, int mode );        
+static int fatfs_fo_close  (struct CYG_FILE_TAG *fp);
+static int fatfs_fo_fstat  (struct CYG_FILE_TAG *fp, struct stat *buf );
+static int fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, 
+                            void *buf, int len );
+static int fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, 
+                            void *buf, int len );
+
+// Directory operations
+static int fatfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio);
+static int fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence);
+
+//==========================================================================
+// Filesystem table entries
+
+// -------------------------------------------------------------------------
+// Fstab entry.
+
+FSTAB_ENTRY(fatfs_fste, "fatfs", 0,
+            CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM,
+            fatfs_mount,
+            fatfs_umount,
+            fatfs_open,
+            fatfs_unlink,
+            fatfs_mkdir,
+            fatfs_rmdir,
+            fatfs_rename,
+            fatfs_link,
+            fatfs_opendir,
+            fatfs_chdir,
+            fatfs_stat,
+            fatfs_getinfo,
+            fatfs_setinfo);
+
+// -------------------------------------------------------------------------
+// File operations.
+
+static cyg_fileops fatfs_fileops =
+{
+    fatfs_fo_read,
+    fatfs_fo_write,
+    fatfs_fo_lseek,
+    fatfs_fo_ioctl,
+    cyg_fileio_seltrue,
+    fatfs_fo_fsync,
+    fatfs_fo_close,
+    fatfs_fo_fstat,
+    fatfs_fo_getinfo,
+    fatfs_fo_setinfo
+};
+
+// -------------------------------------------------------------------------
+// Directory file operations.
+
+static cyg_fileops fatfs_dirops =
+{
+    fatfs_fo_dirread,
+    (cyg_fileop_write *)cyg_fileio_enosys,
+    fatfs_fo_dirlseek,
+    (cyg_fileop_ioctl *)cyg_fileio_enosys,
+    cyg_fileio_seltrue,
+    (cyg_fileop_fsync *)cyg_fileio_enosys,
+    fatfs_fo_close,
+    (cyg_fileop_fstat *)cyg_fileio_enosys,
+    (cyg_fileop_getinfo *)cyg_fileio_enosys,
+    (cyg_fileop_setinfo *)cyg_fileio_enosys
+};
+
+// -------------------------------------------------------------------------
+// Directory search data
+// Parameters for a directory search. The fields of this structure are
+// updated as we follow a pathname through the directory tree.
+
+struct fatfs_dirsearch_t
+{
+    fatfs_disk_t        *disk;     // Disk info 
+    fatfs_node_t        *dir;      // Directory to search
+    const char          *path;     // Path to follow
+    fatfs_node_t        *node;     // Node found
+    const char          *name;     // Last name fragment used
+    int                  namelen;  // Name fragment length
+    cyg_bool             last;     // Last name in path?
+};
+
+typedef struct fatfs_dirsearch_t fatfs_dirsearch_t;
+
+//==========================================================================
+
+#if TFS
+static void 
+print_disk_info(fatfs_disk_t *disk)
+{
+    diag_printf("FAT: sector size:        %u\n", disk->sector_size);
+    diag_printf("FAT: cluster size:       %u\n", disk->cluster_size);
+    diag_printf("FAT: FAT table position: %u\n", disk->fat_tbl_pos);
+    diag_printf("FAT: FAT table num ent:  %u\n", disk->fat_tbl_nents);
+    diag_printf("FAT: FAT table size:     %u\n", disk->fat_tbl_size);
+    diag_printf("FAT: FAT tables num:     %u\n", disk->fat_tbls_num);
+    diag_printf("FAT: FAT root dir pos:   %u\n", disk->fat_root_dir_pos);
+    diag_printf("FAT: FAT root dir nents: %u\n", disk->fat_root_dir_nents);
+    diag_printf("FAT: FAT root dir size:  %u\n", disk->fat_root_dir_size);
+    diag_printf("FAT: FAT data position:  %u\n", disk->fat_data_pos);
+}
+#endif
+
+static void
+init_dirsearch(fatfs_dirsearch_t *ds,
+               fatfs_disk_t      *disk, 
+               fatfs_node_t      *dir,
+               const char        *name)
+{
+    ds->disk    = disk;
+    if (NULL == dir)
+        ds->dir = disk->root;
+    else
+        ds->dir = dir;
+    ds->path    = name;
+    ds->node    = ds->dir;
+    ds->namelen = 0;
+    ds->last    = false;
+}
+
+static int
+find_direntry(fatfs_dirsearch_t *ds)
+{
+    int err;
+    cyg_uint32 pos = 0;
+    fatfs_node_t node_data;
+
+    CYG_TRACE1(TFS, "Finding dir entry '%s'", ds->name);
+
+    ds->node = fatfs_node_find(ds->disk, ds->name, 
+                               ds->namelen, ds->dir->cluster);
+
+    if (ds->node != NULL)
+    {
+        CYG_TRACE1(TFS, "Found dir entry '%s' in cache", ds->name);
+        fatfs_node_touch(ds->disk, ds->node);
+        return ENOERR;
+    }
+    
+    while (true)
+    {   
+        err = fatfs_get_dir_entry_node(ds->disk, ds->dir, &pos, &node_data);
+        if (err != ENOERR)
+            return (err == EEOF ? ENOERR : err);
+
+        if ('\0' == node_data.filename[ds->namelen] &&
+            0 == strncasecmp(node_data.filename, ds->name, ds->namelen))
+        {
+            CYG_TRACE2(TFS, "Read dir entry '%s' at %d", 
+                            node_data.filename, pos);
+
+            ds->node = fatfs_node_alloc(ds->disk, &node_data);
+            if (NULL == ds->node)
+                return ENOMEM;
+            return ENOERR;
+        }
+        pos++;
+    }
+}
+
+static int 
+find_entry(fatfs_dirsearch_t *ds)
+{
+    int err;
+    const char *name = ds->path;
+    const char *n = name;
+    char namelen = 0;
+    
+    // check that we really have a directory
+    if( !S_ISDIR(ds->dir->mode) )
+    {
+        CYG_TRACE1(TFS, "Entry '%s' not dir", ds->dir->filename);
+        return ENOTDIR;
+    }
+    
+    // Isolate the next element of the path name.
+    while (*n != '\0' && *n != '/')
+        n++, namelen++;
+
+    // If we terminated on a NUL, set last flag.
+    if (*n == '\0')
+        ds->last = true;
+
+    // update name in dirsearch object
+    ds->name    = name;
+    ds->namelen = namelen;
+
+    err = find_direntry(ds);
+    if (err != ENOERR)
+        return err;
+
+    CYG_TRACE2(TFS, "Entry '%s' %s", name, (ds->node ? "found" : "not found"));
+    
+    if (ds->node != NULL)
+       return ENOERR;
+    else
+       return ENOENT; 
+}
+
+static int
+fatfs_find(fatfs_dirsearch_t *ds)
+{
+    int err;
+
+    CYG_TRACE1(TFS, "Find path='%s'", ds->path);
+    
+    // Short circuit empty paths
+    if (*(ds->path) == '\0')
+        return ENOERR;
+    
+    // Iterate down directory tree until we find the object
+    // we want.
+    for(;;)
+    {
+        err = find_entry(ds);
+        
+        if (err != ENOERR)
+            return err;
+        
+        if (ds->last)
+        {
+            CYG_TRACE0(TFS, "Entry found");
+            return ENOERR;
+        }
+
+        // Update dirsearch object to search next directory.
+        ds->dir = ds->node;
+        ds->path += ds->namelen;
+        
+        // Skip dirname separators
+        if (*(ds->path) == '/') ds->path++;
+        
+        CYG_TRACE1(TFS, "Find path to go='%s'", ds->path);
+    }
+}
+
+//==========================================================================
+// Filesystem operations
+
+// -------------------------------------------------------------------------
+// fatfs_mount()
+// Process a mount request. This mainly creates a root for the
+// filesystem.
+
+static int 
+fatfs_mount(cyg_fstab_entry *fste, cyg_mtab_entry *mte)
+{
+    cyg_io_handle_t dev_h;
+    Cyg_ErrNo err;
+    fatfs_disk_t *disk;
+    fatfs_node_t root_data;
+
+    CYG_TRACE2(TFS, "Mount fste=%p mte=%p", fste, mte);
+    
+    CYG_TRACE1(TFS, "Looking up disk device '%s'", mte->devname);
+    
+    err = cyg_io_lookup(mte->devname, &dev_h);
+    if (err != ENOERR)
+        return err;
+
+    disk = (fatfs_disk_t *)malloc(sizeof(fatfs_disk_t));
+    if (NULL == disk)
+        return ENOMEM;
+        
+    CYG_TRACE0(TFS, "Initializing FAT table cache"); 
+   
+    if (ENOERR != fatfs_tcache_create(disk, 
+                      CYGNUM_FS_FAT_FAT_TABLE_CACHE_MEMSIZE))
+    {
+        free(disk);
+        return ENOMEM;
+    }
+    
+    CYG_TRACE0(TFS, "Initializing block cache"); 
+
+    disk->bcache_mem = (cyg_uint8 *)malloc(CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE);
+    if (NULL == disk->bcache_mem)
+    {
+        fatfs_tcache_delete(disk);
+        free(disk);
+        return ENOMEM;
+    }
+    // FIXME: get block size from disk device
+    err = cyg_blib_io_create(dev_h, disk->bcache_mem, 
+            CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE, 512, &disk->blib);
+    if (err != ENOERR)
+    {
+        fatfs_tcache_delete(disk);
+        free(disk->bcache_mem);
+        free(disk);
+        return err;
+    }
+    
+    disk->dev_h = dev_h;
+
+    CYG_TRACE0(TFS, "Initializing disk");
+    
+    err = fatfs_get_disk_info(disk);
+    if (err != ENOERR)
+    {
+        fatfs_tcache_delete(disk);
+        cyg_blib_delete(&disk->blib);
+        free(disk->bcache_mem);
+        free(disk);
+        return err;
+    }
+   
+#if TFS    
+    print_disk_info(disk);
+#endif
+
+    CYG_TRACE0(TFS, "Initializing node cache");
+
+    fatfs_node_cache_init(disk);
+    
+    CYG_TRACE0(TFS, "Initializing root node");
+    
+    fatfs_get_root_node(disk, &root_data);
+    disk->root = fatfs_node_alloc(disk, &root_data);
+    fatfs_node_ref(disk, disk->root);
+    
+    mte->root = (cyg_dir)disk->root;
+    mte->data = (CYG_ADDRWORD)disk;
+
+    CYG_TRACE0(TFS, "Disk mounted");
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_umount()
+// Unmount the filesystem. This will currently only succeed if the
+// filesystem is empty.
+
+static int
+fatfs_umount(cyg_mtab_entry *mte)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_node_t *root = (fatfs_node_t *)mte->root;
+
+    CYG_TRACE3(TFS, "Umount mte=%p %d live nodes %d dead nodes", 
+                    mte, fatfs_get_live_node_count(disk), 
+                    fatfs_get_dead_node_count(disk));
+
+    if (root->refcnt > 1)
+        return EBUSY;
+    
+    if (fatfs_get_live_node_count(disk) != 1)
+        return EBUSY;
+
+    fatfs_node_unref(disk, root);
+    fatfs_node_cache_flush(disk);
+    fatfs_tcache_delete(disk);
+    // FIXME: cache delete can fail if cache can't be synced
+    cyg_blib_delete(&disk->blib); 
+    free(disk->bcache_mem);
+    free(disk);
+    
+    mte->root = CYG_DIR_NULL;
+    mte->data = (CYG_ADDRWORD)NULL;
+    
+    CYG_TRACE0(TFS, "Disk umounted");
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_open()
+// Open a file for reading or writing.
+
+static int 
+fatfs_open(cyg_mtab_entry *mte,
+           cyg_dir         dir, 
+           const char     *name,
+           int             mode,  
+           cyg_file       *file)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_node_t *node = NULL;
+    fatfs_dirsearch_t ds;
+    int err;
+
+    CYG_TRACE5(TFS, "Open mte=%p dir=%p name='%s' mode=%d file=%p", 
+                    mte, dir, name, mode, file);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err == ENOENT)
+    {
+        if (ds.last && (mode & O_CREAT))
+        {
+            fatfs_node_t node_data;
+            
+            // No node there, if the O_CREAT bit is set then we must
+            // create a new one. The dir and name fields of the dirsearch
+            // object will have been updated so we know where to put it.
+
+            CYG_TRACE1(TFS, "Creating new file '%s'", name); 
+
+            err = fatfs_create_file(disk, ds.dir, ds.name, 
+                                    ds.namelen, &node_data);
+            if (err != ENOERR)
+                return err;
+
+            node = fatfs_node_alloc(disk, &node_data);
+            if (NULL == node)
+                return ENOMEM;
+           
+            // Update directory times
+            ds.dir->atime =
+            ds.dir->mtime = cyg_timestamp();
+            
+            err = ENOERR;
+        }
+    }
+    else if (err == ENOERR)
+    {
+        // The node exists. If the O_CREAT and O_EXCL bits are set, we
+        // must fail the open.
+
+        if ((mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+            err = EEXIST;
+        else
+            node = ds.node;
+    }
+
+    if (err == ENOERR && (mode & O_TRUNC))
+    {
+        // If the O_TRUNC bit is set we must clean out the file data.
+        CYG_TRACE0(TFS, "Truncating file"); 
+        fatfs_trunc_file(disk, node);
+    }
+
+    if (err != ENOERR)
+        return err;
+    
+    // Check that we actually have a file here
+    if (S_ISDIR(node->mode))
+        return EISDIR;
+
+    // Make a reference to this file node     
+    fatfs_node_ref(disk, node);
+
+    // Initialize the file object
+
+    file->f_flag   |= mode & CYG_FILE_MODE_MASK;
+    file->f_type    = CYG_FILE_TYPE_FILE;
+    file->f_ops     = &fatfs_fileops;
+    file->f_offset  = (mode & O_APPEND) ? node->size : 0;
+    file->f_data    = (CYG_ADDRWORD)node;
+    file->f_xops    = 0;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_unlink()
+// Remove a file link from its directory.
+
+static int 
+fatfs_unlink(cyg_mtab_entry *mte, 
+             cyg_dir         dir, 
+             const char     *name)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds;
+    int err;
+
+    CYG_TRACE3(TFS, "Unlink mte=%p dir=%p name='%s'", mte, dir, name);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err != ENOERR)
+        return err;
+  
+    if (ds.node->refcnt > 0)
+        return EBUSY;
+    
+    // Delete node
+    err = fatfs_delete_file(disk, ds.node);
+    if (err == ENOERR)
+        fatfs_node_free(disk, ds.node);
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_mkdir()
+// Create a new directory.
+
+static int
+fatfs_mkdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds;
+    int err;
+    
+    CYG_TRACE3(TFS, "Mkdir mte=%p dir=%p name='%s'", mte, dir, name);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err == ENOENT)
+    {
+        if (ds.last)
+        {
+            fatfs_node_t node_data;
+            
+            // The entry does not exist, and it is the last element in
+            // the pathname, so we can create it here.
+
+            err = fatfs_create_dir(disk, ds.dir, ds.name, 
+                                   ds.namelen, &node_data);
+            if (err != ENOERR)
+                return err;
+            
+            fatfs_node_alloc(disk, &node_data);
+
+            return ENOERR;
+        }
+    } 
+    else if (err == ENOERR)
+    {
+        return EEXIST;
+    }
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_rmdir()
+// Remove a directory.
+
+static int 
+fatfs_rmdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds;
+    int err;
+
+    CYG_TRACE3(TFS, "Rmdir mte=%p dir=%p name='%s'", mte, dir, name);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err != ENOERR)
+        return err;
+  
+    // Check that this is actually a directory.
+    if (!S_ISDIR(ds.node->mode))
+        return EPERM;
+ 
+    if (ds.node->refcnt > 0)
+        return EBUSY;
+    
+    err = fatfs_delete_file(disk, ds.node);
+    if (err == ENOERR)
+        fatfs_node_free(disk, ds.node);
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_rename()
+// Rename a file/dir.
+
+static int 
+fatfs_rename(cyg_mtab_entry *mte, 
+             cyg_dir         dir1, 
+             const char     *name1,
+             cyg_dir         dir2, 
+             const char     *name2)
+{
+    fatfs_disk_t *disk  = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds1, ds2;
+    int err;
+ 
+    CYG_TRACE5(TFS, "Rename mte=%p dir1=%p name1='%s' dir2=%p name2='%s'", 
+                    mte, dir1, name1, dir2, name2);
+
+    init_dirsearch(&ds1, disk, (fatfs_node_t *)dir1, name1);
+
+    err = fatfs_find(&ds1);
+    if (err != ENOERR)
+        return err;
+
+    // Protect the found nodes from being reused by the
+    // following search
+    fatfs_node_ref(disk, ds1.dir);
+    fatfs_node_ref(disk, ds1.node);
+    
+    init_dirsearch(&ds2, disk, (fatfs_node_t *)dir2, name2);
+   
+    err = fatfs_find(&ds2);
+
+    if (err == ENOERR && ds2.last)
+    {
+        err = EEXIST;  
+        goto out;  
+    }
+    
+    if (err == ENOENT && !ds2.last)
+        goto out;
+    
+    if (ds1.node == ds2.node)
+    {
+        err = ENOERR;
+        goto out;
+    }
+    
+    err = fatfs_rename_file(disk, ds1.dir, ds1.node, ds2.dir, 
+                            ds2.name, ds2.namelen);  
+    fatfs_node_rehash(disk, ds1.node);
+
+out:
+    fatfs_node_unref(disk, ds1.dir);
+    fatfs_node_unref(disk, ds1.node);
+   
+    if (err == ENOERR)
+    {
+        ds1.dir->atime =
+        ds1.dir->mtime = 
+        ds2.dir->atime = 
+        ds2.dir->mtime = cyg_timestamp();    
+    } 
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_link()
+// Make a new directory entry for a file.
+
+static int 
+fatfs_link(cyg_mtab_entry *mte, 
+           cyg_dir         dir1, 
+           const char     *name1,
+           cyg_dir         dir2, 
+           const char     *name2, 
+           int             type)
+{
+    CYG_TRACE6(TFS, "Link mte=%p dir1=%p name1='%s' dir2=%p name2='%s' type=%d",
+                    mte, dir1, name1, dir2, name2, type);
+
+    // Linking not supported
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_opendir()
+// Open a directory for reading.
+
+static int
+fatfs_opendir(cyg_mtab_entry *mte,
+              cyg_dir         dir,
+              const char     *name,
+              cyg_file       *file)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds;
+    int err;
+
+    CYG_TRACE4(TFS, "Opendir mte=%p dir=%p name='%s' file=%p", 
+                    mte, dir, name, file);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err != ENOERR)
+        return err;
+
+    // Check it is really a directory.
+    if (!S_ISDIR(ds.node->mode)) 
+        return ENOTDIR;
+  
+    // Make a reference to this dir node
+    fatfs_node_ref(disk, ds.node);
+    
+    // Initialize the file object
+    file->f_type        = CYG_FILE_TYPE_FILE;
+    file->f_ops         = &fatfs_dirops;
+    file->f_data        = (CYG_ADDRWORD)ds.node;
+    file->f_xops        = 0;
+    file->f_offset      = 0;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_chdir()
+// Change directory support.
+
+static int
+fatfs_chdir(cyg_mtab_entry *mte,
+            cyg_dir         dir,
+            const char     *name,
+            cyg_dir        *dir_out)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+
+    CYG_TRACE4(TFS, "Chdir mte=%p dir=%p dir_out=%p name=%d", 
+                    mte, dir, dir_out, name);
+
+    if (dir_out != NULL)
+    {
+        // This is a request to get a new directory pointer in
+        // *dir_out.
+        
+        fatfs_dirsearch_t ds;
+        int err;
+
+        init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+        err = fatfs_find(&ds);
+
+        if (err != ENOERR)
+            return err;
+
+        // Check it is a directory
+        if (!S_ISDIR(ds.node->mode))
+            return ENOTDIR;
+
+        if (ds.node != disk->root)
+            fatfs_node_ref(disk, ds.node); 
+        *dir_out = (cyg_dir)ds.node;
+    }
+    else
+    {
+        // If no output dir is required, this means that the mte and
+        // dir arguments are the current cdir setting and we should
+        // forget this fact.
+
+        fatfs_node_t *node = (fatfs_node_t *)dir;
+
+        if (node != disk->root)
+            fatfs_node_unref(disk, node);
+    }
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_stat()
+// Get struct stat info for named object.
+
+static int
+fatfs_stat(cyg_mtab_entry *mte,
+           cyg_dir         dir,
+           const char     *name,
+           struct stat    *buf)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)mte->data;
+    fatfs_dirsearch_t ds;
+    int err;
+
+    CYG_TRACE4(TFS, "Stat mte=%p dir=%p name='%s' buf=%p", 
+                    mte, dir, name, buf);
+
+    init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name);
+
+    err = fatfs_find(&ds);
+
+    if (err != ENOERR)
+        return err;
+
+    // Fill in the status
+    buf->st_mode        = ds.node->mode;
+    buf->st_ino         = (ino_t)ds.node->cluster;
+    buf->st_dev         = 0;
+    buf->st_nlink       = 1;
+    buf->st_uid         = 0;
+    buf->st_gid         = 0;
+    buf->st_size        = ds.node->size;
+    buf->st_atime       = ds.node->atime;
+    buf->st_mtime       = ds.node->mtime;
+    buf->st_ctime       = ds.node->ctime;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_getinfo()
+// Getinfo. Nothing to support here at present.
+
+static int 
+fatfs_getinfo(cyg_mtab_entry *mte, 
+              cyg_dir         dir, 
+              const char     *name,
+              int             key, 
+              void           *buf, 
+              int             len)
+{
+    CYG_TRACE6(TFS, "Getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d",
+                    mte, dir, name, key, buf, len);
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_setinfo()
+// Setinfo. Nothing to support here at present.
+
+static int 
+fatfs_setinfo(cyg_mtab_entry *mte, 
+              cyg_dir         dir, 
+              const char     *name,
+              int             key, 
+              void           *buf, 
+              int             len)
+{
+    CYG_TRACE6(TFS, "Getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d",
+                    mte, dir, name, key, buf, len);
+    return EINVAL;
+}
+
+//==========================================================================
+// File operations
+
+// -------------------------------------------------------------------------
+// fatfs_fo_read()
+// Read data from the file.
+
+static int 
+fatfs_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data;
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    cyg_uint32 pos = fp->f_offset;
+    ssize_t resid = uio->uio_resid;
+    int i;
+
+    CYG_TRACE3(TFO, "Read fp=%p uio=%p pos=%d", fp, uio, pos);
+
+    // Loop over the io vectors until there are none left
+    for (i = 0; i < uio->uio_iovcnt; i++)
+    {
+        cyg_iovec *iov = &uio->uio_iov[i];
+        char *buf = (char *)iov->iov_base;
+        off_t len = iov->iov_len;
+
+        // Loop over each vector filling it with data from the file.
+        while (len > 0 && pos < node->size)
+        {
+            cyg_uint32 l = len;
+            int err;
+
+            // Adjust size to end of file if necessary
+            if (l > node->size-pos)
+                l = node->size-pos;
+
+            // Read data
+            err = fatfs_read_data(disk, node, buf, &l, pos);
+
+            if (err != ENOERR)
+                return err;
+
+            // Update working vars
+            len   -= l;
+            buf   += l;
+            pos   += l;
+            resid -= l;
+        }
+    }
+
+    // We successfully read some data,
+    // update the access time, file offset and transfer residue.
+    node->atime    = cyg_timestamp(); 
+    uio->uio_resid = resid;
+    fp->f_offset   = (off_t)pos;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_write()
+// Write data to file.
+
+static int 
+fatfs_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data;
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    cyg_uint32 pos = fp->f_offset;
+    ssize_t resid = uio->uio_resid;
+    int err = ENOERR;
+    int i;
+
+    CYG_TRACE3(TFO, "Write fp=%p uio=%p pos=%d", fp, uio, pos);
+    
+    // If the APPEND mode bit was supplied, force all writes to
+    // the end of the file.
+    if (fp->f_flag & CYG_FAPPEND)
+        pos = fp->f_offset = node->size;
+
+    // Check that pos is within current file size, or at the very end.
+    if (pos < 0 || pos > node->size)
+        return EINVAL;
+   
+    // Now loop over the iovecs until they are all done, or
+    // we get an error.
+    for (i = 0; i < uio->uio_iovcnt; i++)
+    {
+        cyg_iovec *iov = &uio->uio_iov[i];
+        char *buf = (char *)iov->iov_base;
+        off_t len = iov->iov_len;
+ 
+        // Loop over the vector writing it to the file until it has
+        // all been done.
+        while (len > 0)
+        {
+            cyg_uint32 l = len;
+
+            // Write data
+            err = fatfs_write_data(disk, node, buf, &l, pos);
+
+            // Update working vars
+            len   -= l;
+            buf   += l;
+            pos   += l;
+            resid -= l;
+
+            // Stop writing if there is no more space in the file and
+            // indicate end of data.
+            if (err == ENOSPC)
+                break;
+            
+            if (err != ENOERR)
+                return err;
+        }
+    }        
+
+    // We wrote some data successfully, update the modified and access
+    // times of the node, increase its size appropriately, and update
+    // the file offset and transfer residue.
+    node->mtime = 
+    node->atime = cyg_timestamp();
+    if (pos > node->size)
+        node->size = pos;
+
+    uio->uio_resid = resid;
+    fp->f_offset = (off_t)pos;
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_lseek()
+// Seek to a new file position.
+
+static int 
+fatfs_fo_lseek(struct CYG_FILE_TAG *fp, off_t *apos, int whence)
+{
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    off_t pos = *apos;
+
+    CYG_TRACE3(TFO, "Lseek fp=%p pos=%d whence=%d", fp, fp->f_offset, whence);
+
+    switch (whence)
+    {
+        case SEEK_SET:
+            // Pos is already where we want to be.
+            break;
+         case SEEK_CUR:
+            // Add pos to current offset.
+            pos += fp->f_offset;
+            break;
+         case SEEK_END:
+            // Add pos to file size.
+            pos += node->size;
+            break;
+         default:
+            return EINVAL;
+    }
+
+    // Check that pos is still within current file size, or at the
+    // very end.
+    if (pos < 0 || pos > node->size)
+        return EINVAL;
+  
+    // All OK, set fp offset and return new position.
+    *apos = fp->f_offset = pos;
+
+    CYG_TRACE2(TFO, "Lseek fp=%p new pos=%d", fp, *apos);
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_ioctl()
+// Handle ioctls. Currently none are defined.
+
+static int 
+fatfs_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, CYG_ADDRWORD data)
+{
+    CYG_TRACE3(TFO, "Ioctl fp=%p com=%x data=%x", fp, com, data);
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_fsync().
+// Force the file out to data storage.
+
+static int 
+fatfs_fo_fsync(struct CYG_FILE_TAG *fp, int mode)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data;
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    int err;
+    
+    CYG_TRACE2(TFO, "Fsync fp=%p mode=%d", fp, mode);
+
+    err = fatfs_write_file_attr(disk, node);
+    if (ENOERR == err)
+        err = cyg_blib_sync(&disk->blib);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_close()
+// Close a file.
+
+static int
+fatfs_fo_close(struct CYG_FILE_TAG *fp)
+{    
+    fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data;
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    int err = ENOERR;
+
+    CYG_TRACE1(TFO, "Close fp=%p", fp);
+
+    if (node != disk->root)
+        err = fatfs_write_file_attr(disk, node);
+    fatfs_node_unref(disk, node);    
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_fstat()
+// Get file status.
+
+static int
+fatfs_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf)
+{
+    fatfs_node_t *node = (fatfs_node_t *)fp->f_data;
+    
+    CYG_TRACE2(TFO, "Fstat fp=%p buf=%p", fp, buf);
+
+    // Fill in the status
+    buf->st_mode   = node->mode;
+    buf->st_ino    = (ino_t)node->cluster;
+    buf->st_dev    = 0;
+    buf->st_nlink  = 1;
+    buf->st_uid    = 0;
+    buf->st_gid    = 0;
+    buf->st_size   = node->size;
+    buf->st_atime  = node->atime;
+    buf->st_mtime  = node->mtime;
+    buf->st_ctime  = node->ctime;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_getinfo()
+// Get info.
+
+static int
+fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len)
+{
+    CYG_TRACE4(TFO, "Getinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len);
+    return EINVAL;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_setinfo()
+// Set info.
+
+static int
+fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len)
+{
+    CYG_TRACE4(TFO, "Setinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len);
+    return EINVAL;
+}
+
+//==========================================================================
+// Directory operations
+
+// -------------------------------------------------------------------------
+// fatfs_fo_dirread()
+// Read a single directory entry from a file.
+
+static int 
+fatfs_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)
+{
+    fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data;
+    fatfs_node_t *dir  = (fatfs_node_t *)fp->f_data;
+    struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base;
+    cyg_uint32 pos     = fp->f_offset;
+    char *nbuf         = ent->d_name;
+    off_t len          = uio->uio_iov[0].iov_len;
+    fatfs_node_t node;
+    int err;
+    
+    CYG_TRACE3(TFO, "Dirread fp=%p uio=%p pos=%d", fp, uio, pos);
+
+    if (len < sizeof(struct dirent))
+        return EINVAL;
+
+    err = fatfs_get_dir_entry_node(disk, dir, &pos, &node);
+
+    if (err != ENOERR)
+        return (err == EEOF ? ENOERR : err);
+
+    strcpy(nbuf, node.filename);
+
+    dir->atime      = cyg_timestamp();
+    uio->uio_resid -= sizeof(struct dirent);
+    fp->f_offset    = (off_t)(pos + 1);
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_fo_dirlseek()
+// Seek directory to start.
+
+static int 
+fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence)
+{
+    CYG_TRACE2(TFO, "Dirlseek fp=%p whence=%d", fp, whence);
+
+    // Only allow SEEK_SET to zero
+    
+    if (whence != SEEK_SET || *pos != 0)
+        return EINVAL;
+    
+    *pos = fp->f_offset = 0;
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// EOF fatfs.c
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/src/fatfs.h
@@ -0,0 +1,324 @@
+#ifndef CYGONCE_FATFS_FATFS_H
+#define CYGONCE_FATFS_FATFS_H
+//==========================================================================
+//
+//      fatfs.h
+//
+//      FAT file system header 
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           savin 
+// Date:                2003-06-29
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/fs_fat.h>
+#include <cyg/kernel/kapi.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/io/io.h>
+#include <blib/blib.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cyg/fileio/fileio.h>
+
+// --------------------------------------------------------------------------
+
+#define FATFS_HASH_TABLE_SIZE CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE 
+
+#define FATFS_NODE_ALLOC_THRESHOLD CYGNUM_FS_FAT_NODE_ALLOC_THRESHOLD 
+
+#define FATFS_FAT_TABLE_CACHE_INCREMENT CYGNUM_FS_FAT_FAT_TABLE_CACHE_INCREMENT 
+
+#ifdef CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS
+# define FATFS_NODE_CACHE_EXTRA_CHECKS 1
+#endif
+
+// --------------------------------------------------------------------------
+
+// Node cache tracing support
+//#define FATFS_TRACE_NODE_CACHE 1
+
+// FAT table cache tracing support
+//#define FATFS_TRACE_FAT_TABLE_CACHE 1
+
+// FAT table operations tracing support 
+//#define FATFS_TRACE_FAT_TABLE 1
+
+// FAT dir entry operations tracing support 
+//#define FATFS_TRACE_DIR_ENTRY 1
+
+// FAT clusters operations tracing support 
+//#define FATFS_TRACE_CLUSTER 1
+
+// FAT data tracing support 
+//#define FATFS_TRACE_DATA 1
+
+// FAT file operations tracing support 
+//#define FATFS_TRACE_FILE_OP 1
+
+// FAT filesystem operations tracing support 
+//#define FATFS_TRACE_FS_OP 1
+
+// --------------------------------------------------------------------------
+
+typedef enum fatfs_type_e
+{
+    FATFS_FAT12 = 0,    
+    FATFS_FAT16         
+} fatfs_type_t;
+
+typedef struct fatfs_data_pos_s
+{
+    cyg_uint32 cluster;      // Cluster number
+    cyg_uint32 cluster_snum; // Cluster file seq number 
+                             // (0 - first cluster of file,
+                             //  1 - second cluster of file, ...)  
+    cyg_uint32 cluster_pos;  // Position inside cluster
+} fatfs_data_pos_t;
+
+typedef struct fatfs_tcache_s
+{
+    cyg_uint32 *clusters; // Cached clusters array
+    cyg_uint32  size;     // Number of cached clusters in array
+    cyg_uint32  max_size; // Size of array (allocated space)
+} fatfs_tcache_t;
+
+typedef struct fatfs_node_s
+{
+    char                 filename[12+1]; // File name
+    mode_t               mode;           // Node type
+    cyg_ucount32         refcnt;         // Open file/current dir references
+    size_t               size;           // Size of file in bytes
+    time_t               ctime;          // Creation timestamp
+    time_t               atime;          // Last access timestamp
+    time_t               mtime;          // Last write timestamp
+    cyg_uint8            priv_data;      // Private data
+    cyg_uint32           cluster;        // First cluster number
+    cyg_uint32           parent_cluster; // First cluster of parent dentry
+    fatfs_data_pos_t     dentry_pos;     // Position of node's dir entry on disk
+
+    struct fatfs_node_s *list_prev;      // Next node in list
+    struct fatfs_node_s *list_next;      // Prev node in list
+    struct fatfs_node_s *hash_next;      // Next node in hash
+
+    fatfs_tcache_t       tcache;         // Node FAT table clusters cache 
+} fatfs_node_t;
+
+typedef struct fatfs_hash_table_s 
+{
+    cyg_uint32     size;                         // Number of slots
+    cyg_uint32     n;                            // Number of nodes 
+    fatfs_node_t  *nodes[FATFS_HASH_TABLE_SIZE]; // Nodes slots
+} fatfs_hash_table_t;
+
+typedef struct fatfs_node_list_s
+{
+    cyg_uint32    size;          // Number of nodes in list
+    fatfs_node_t *first;         // First node in list
+    fatfs_node_t *last;          // Last node in list
+} fatfs_node_list_t;
+
+typedef struct fatfs_disk_s
+{
+    cyg_uint32   sector_size;        // Sector size in bytes
+    cyg_uint32   sector_size_log2;   // Sector size log2
+    cyg_uint32   cluster_size;       // Cluster size in bytes
+    cyg_uint32   cluster_size_log2;  // Cluster size log2 
+    cyg_uint32   fat_tbl_pos;        // Position of the first FAT table
+    cyg_uint32   fat_tbl_size;       // FAT table size in bytes
+    cyg_uint32   fat_tbl_nents;      // Number of entries in FAT table
+    cyg_uint32   fat_tbls_num;       // Number of FAT tables
+    cyg_uint32   fat_root_dir_pos;   // Position of the root dir
+    cyg_uint32   fat_root_dir_size;  // Root dir size in bytes 
+    cyg_uint32   fat_root_dir_nents; // Max number of entries in root dir
+    cyg_uint32   fat_data_pos;       // Position of data area
+    fatfs_type_t fat_type;           // Type of FAT - 12 or 16
+    
+    cyg_io_handle_t  dev_h;          // Disk device handle
+    fatfs_node_t    *root;           // Root dir node
+
+    cyg_uint8       *tcache_mem;     // FAT table cache memory base
+    cyg_handle_t     tcache_mpool_h; // FAT table cache memory pool handle 
+    cyg_mempool_var  tcache_mpool;   // FAT table cache memory pool struct 
+
+    cyg_uint8       *bcache_mem;     // Block cache memory base
+    cyg_blib_t       blib;           // Block cache and access library instance
+
+    fatfs_node_list_t  live_nlist;   // List of nodes with refcnt > 0
+    fatfs_node_list_t  dead_nlist;   // List of nodes with refcnt == 0
+    fatfs_hash_table_t node_hash;    // Hash of nodes in live and dead lists
+} fatfs_disk_t;
+
+// --------------------------------------------------------------------------
+
+int  fatfs_get_disk_info(fatfs_disk_t *disk);
+
+void fatfs_get_root_node(fatfs_disk_t *disk, fatfs_node_t *root);
+
+bool fatfs_is_node_root_node(fatfs_node_t *node);
+
+int  fatfs_get_dir_entry_node(fatfs_disk_t *disk,
+                              fatfs_node_t *dir,
+                              cyg_uint32   *pos,
+                              fatfs_node_t *node);
+
+int  fatfs_write_file_attr(fatfs_disk_t *disk, fatfs_node_t *node);
+
+int  fatfs_delete_file(fatfs_disk_t *disk, fatfs_node_t *node);
+
+int  fatfs_create_file(fatfs_disk_t *disk, 
+                       fatfs_node_t *dir, 
+                       const char   *name,
+                       int           namelen,
+                       fatfs_node_t *node);
+
+int  fatfs_create_dir(fatfs_disk_t *disk, 
+                      fatfs_node_t *dir, 
+                      const char   *name,
+                      int           namelen,
+                      fatfs_node_t *node);
+
+int  fatfs_trunc_file(fatfs_disk_t *disk, fatfs_node_t *node);
+
+int  fatfs_rename_file(fatfs_disk_t *disk, 
+                       fatfs_node_t *dir1,
+                       fatfs_node_t *node,
+                       fatfs_node_t *dir2,
+                       const char   *name,
+                       int           namelen);
+
+int  fatfs_read_data(fatfs_disk_t *disk,
+                     fatfs_node_t *node,
+                     void         *data,
+                     cyg_uint32   *len,
+                     cyg_uint32    off);
+
+int  fatfs_write_data(fatfs_disk_t *disk,
+                      fatfs_node_t *node,
+                      void         *data,
+                      cyg_uint32   *len,
+                      cyg_uint32    off);
+
+// --------------------------------------------------------------------------
+
+void fatfs_node_cache_init(fatfs_disk_t *disk);
+
+void fatfs_node_cache_flush(fatfs_disk_t *disk);
+
+fatfs_node_t *fatfs_node_alloc(fatfs_disk_t *disk, fatfs_node_t *node_data);
+
+void fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node);
+
+void fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node);
+
+void fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node);
+
+void fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node);
+
+void fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node);
+
+fatfs_node_t* fatfs_node_find(fatfs_disk_t *disk, 
+                              const char   *name, 
+                              unsigned int  namelen, 
+                              cyg_uint32    parent_cluster);
+
+int  fatfs_get_live_node_count(fatfs_disk_t *disk);
+
+int  fatfs_get_dead_node_count(fatfs_disk_t *disk);
+
+void fatfs_node_flush_dead_tcache(fatfs_disk_t *disk);
+
+// --------------------------------------------------------------------------
+
+int  fatfs_tcache_create(fatfs_disk_t *disk, cyg_uint32 mem_size);
+
+void fatfs_tcache_delete(fatfs_disk_t *disk);
+
+void fatfs_tcache_init(fatfs_disk_t *disk, fatfs_tcache_t *tcache);
+
+void fatfs_tcache_flush(fatfs_disk_t *disk, fatfs_tcache_t *tcache);
+
+bool fatfs_tcache_get(fatfs_disk_t   *disk,
+                      fatfs_tcache_t *tcache, 
+                      cyg_uint32      num, 
+                      cyg_uint32     *cluster);
+
+bool fatfs_tcache_get_last(fatfs_disk_t   *disk,
+                           fatfs_tcache_t *tcache, 
+                           cyg_uint32     *num, 
+                           cyg_uint32     *cluster);
+
+bool fatfs_tcache_set(fatfs_disk_t   *disk,
+                      fatfs_tcache_t *tcache, 
+                      cyg_uint32      num, 
+                      cyg_uint32      cluster);
+
+// --------------------------------------------------------------------------
+// Support routines
+// These enable the definition of local versions of certain routines
+// if the given packages are not present.
+
+#ifndef CYGPKG_LIBC_I18N
+
+__externC int toupper( int c );
+
+#endif
+
+#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS
+
+__externC int strcasecmp( const char *s1, const char *s2 );
+
+__externC int strncasecmp( const char *s1, const char *s2, size_t n );
+
+#endif
+
+// --------------------------------------------------------------------------
+
+#endif // CYGONCE_FATFS_FATFS_H
+
+// --------------------------------------------------------------------------
+// EOF fatfs.h
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/src/fatfs_ncache.c
@@ -0,0 +1,926 @@
+//==========================================================================
+//
+//      fatfs_ncache.c
+//
+//      FAT file system node cache functions
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           savin 
+// Date:                2003-06-26
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/fs_fat.h>
+#include <pkgconf/infra.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "fatfs.h"
+
+//==========================================================================
+// Defines & macros 
+
+#ifdef CYGDBG_USE_ASSERTS
+# define USE_ASSERTS 1
+#endif
+
+#ifdef FATFS_NODE_CACHE_EXTRA_CHECKS
+# define USE_XCHECKS 1
+#endif
+
+#ifdef FATFS_TRACE_NODE_CACHE
+# define TNC 1
+#else
+# define TNC 0
+#endif
+
+// This defines how many nodes should always be kept in
+// dead list regardless of node allocation treshold - it 
+// should always be >= 2 or the file finding code may not
+// work correctly!
+#define DLIST_KEEP_NUM 2 
+
+//==========================================================================
+// Node list functions 
+
+static void
+node_list_init(fatfs_node_list_t *list)
+{
+    list->size = 0;
+    list->first = list->last = NULL;
+}
+
+static __inline__ int 
+node_list_get_size(fatfs_node_list_t *list)
+{
+    return (list->size);
+}
+
+static __inline__ fatfs_node_t*
+node_list_get_head(fatfs_node_list_t *list)
+{
+    return (list->first);
+}
+
+static __inline__ fatfs_node_t*
+node_list_get_tail(fatfs_node_list_t *list)
+{
+    return (list->last);
+}
+
+static __inline__ fatfs_node_t*
+node_list_get_next(fatfs_node_t *node)
+{
+    return (node->list_next);
+}
+
+static __inline__ fatfs_node_t*
+node_list_get_prev(fatfs_node_t *node)
+{
+    return (node->list_prev);
+}
+
+static __inline__ bool
+node_list_is_last(fatfs_node_t *node)
+{
+    return (NULL == node->list_next);
+}
+
+static __inline__ bool
+node_list_is_first(fatfs_node_t *node)
+{
+    return (NULL == node->list_prev);
+}
+
+static void
+node_list_head_add(fatfs_node_list_t *list, fatfs_node_t *node)
+{
+    node->list_prev = NULL;
+    node->list_next = list->first;
+    
+    if (NULL == list->first)
+    {
+        CYG_ASSERTC(list->size == 0);
+        list->last = node;
+    }
+    else
+    {
+        list->first->list_prev = node;
+    }
+    
+    list->first = node;
+    list->size++;
+}
+
+#if 0
+static __inline__ fatfs_node_t*
+node_list_head_get(fatfs_node_list_t *list)
+{
+    return list->first;
+}
+
+static void
+node_list_tail_add(fatfs_node_list_t *list, fatfs_node_t *node)
+{
+    node->list_prev = list->last;
+    node->list_next = NULL;
+    
+    if (NULL == list->last)
+    {
+        CYG_ASSERTC(list->size == 0);
+        list->first = node;
+    }
+    else
+    {
+        list->last->list_next = node;
+    }
+    
+    list->last = node;
+    list->size++;
+}
+#endif
+
+static __inline__ fatfs_node_t*
+node_list_tail_get(fatfs_node_list_t *list)
+{
+    return list->last;
+}
+
+static void
+node_list_remove(fatfs_node_list_t *list, fatfs_node_t *node)
+{
+    if (list->first == list->last)
+    {
+        if (list->first == node)
+        {
+            CYG_ASSERTC(list->size == 1);
+            list->first = list->last = NULL;
+        }
+        else
+        {
+            CYG_ASSERT(false, "chain node not in list!");
+        }
+    }
+    else if (list->first == node)
+    {
+        CYG_ASSERTC(node->list_prev == NULL); 
+        list->first = node->list_next;
+        list->first->list_prev = NULL;
+    }
+    else if (list->last == node)
+    {
+        CYG_ASSERTC(node->list_next == NULL); 
+        list->last = node->list_prev;
+        list->last->list_next = NULL;
+    }
+    else
+    {
+        CYG_ASSERTC(node->list_prev != NULL && node->list_next != NULL); 
+        node->list_prev->list_next = node->list_next;
+        node->list_next->list_prev = node->list_prev;        
+    }
+    list->size--;
+}
+
+#ifdef USE_XCHECKS
+static void
+node_list_check(fatfs_node_list_t *list, int min_ref, int max_ref)
+{
+    int i;
+    fatfs_node_t *node, *pnode;
+
+    CYG_ASSERT((list->last == NULL && list->first == NULL) ||
+               (list->last != NULL && list->first != NULL), 
+               "list->first and list->last broken!");
+
+    if (list->first == NULL)
+    {
+        CYG_ASSERTC(list->size == 0);
+        return;
+    }
+    
+    CYG_ASSERTC(list->first->list_prev == NULL);
+    CYG_ASSERTC(list->last->list_next == NULL);
+
+    CYG_ASSERTC(list->first->refcnt >= min_ref && 
+                list->first->refcnt <= max_ref);
+    CYG_ASSERTC(list->last->refcnt >= min_ref && 
+                list->last->refcnt <= max_ref);
+    
+    i = 1;
+    node = list->first; 
+    pnode = NULL;
+    while (node->list_next != NULL)
+    {
+        i++;
+        CYG_ASSERTC(node->list_prev == pnode);
+        CYG_ASSERTC(node->refcnt >= min_ref && node->refcnt <= max_ref);
+        pnode = node;
+        node = node->list_next;
+    }
+    CYG_ASSERTC(node->list_prev == pnode);
+    CYG_ASSERTC(list->size == i);
+    CYG_ASSERTC(node == list->last);
+}
+
+static void
+node_lists_check(fatfs_disk_t* disk)
+{
+    node_list_check(&disk->live_nlist, 1, 99999);                        
+    node_list_check(&disk->dead_nlist, 0, 0);                           
+
+    if ((disk->live_nlist.size + disk->dead_nlist.size) > 
+        FATFS_NODE_ALLOC_THRESHOLD)
+        CYG_ASSERTC(disk->dead_nlist.size <= DLIST_KEEP_NUM);
+}
+#endif // USE_XCHECKS
+
+//==========================================================================
+// Node hash functions 
+
+static unsigned int 
+hash_fn(const char *str, unsigned int strlen)
+{
+    unsigned int i = 0, val = 0;
+
+    while (i++ < strlen)
+        val = (val ^ (int)toupper(*str++)) << 1;
+    return(val);
+}
+
+static void
+node_hash_init(fatfs_hash_table_t *tbl)
+{
+    int i;
+
+    // Set size and clear all slots
+    tbl->size = FATFS_HASH_TABLE_SIZE;
+    for (i = 0; i < tbl->size; i++)
+        tbl->nodes[i] = NULL;
+    tbl->n = 0;
+}
+
+static bool
+node_hash_add(fatfs_hash_table_t *tbl, fatfs_node_t *node)
+{
+    unsigned int hval;
+   
+    // Calculate hash of given node filename
+    hval = hash_fn(node->filename, strlen(node->filename)) % tbl->size;
+
+    CYG_TRACE2(TNC, "name='%s' hval=%d", node->filename, hval);
+
+    if (tbl->nodes[hval] == NULL)
+    {
+        // First node in this slot
+        node->hash_next  = NULL;
+        tbl->nodes[hval] = node;
+        tbl->n++; 
+        return true;
+    }
+    else
+    { 
+        // More nodes in this slot
+        fatfs_node_t *lnode, *pnode;
+       
+        pnode = NULL;
+        lnode = tbl->nodes[hval];
+
+        // Insert node into list so that it is sorted by filename        
+        while (lnode != NULL)
+        {
+            if (lnode == node)
+                return false;
+            
+            if (strcasecmp(lnode->filename, node->filename) > 0)
+            {
+                if (pnode != NULL)
+                    pnode->hash_next = node; // Insert in the middle
+                else
+                    tbl->nodes[hval] = node; // Insert at the beginning
+                node->hash_next = lnode;
+                tbl->n++; 
+                return true;
+            }    
+            
+            pnode = lnode;    
+            lnode = lnode->hash_next;
+        }
+        // Insert at the end
+        pnode->hash_next = node;
+        node->hash_next  = NULL;
+        tbl->n++; 
+        return true;
+    }
+}
+
+static fatfs_node_t*
+node_hash_find(fatfs_hash_table_t *tbl, 
+               const char         *name,
+               unsigned int        namelen,
+               unsigned int        parent_cluster)
+{   
+    unsigned int hval; 
+    fatfs_node_t *node;
+
+    // Calculate hash of name and get the first node in slot
+    hval = hash_fn(name, namelen) % tbl->size;
+    node = tbl->nodes[hval];
+
+    CYG_TRACE2(TNC, "name='%s' hval=%d\n", name, hval);
+ 
+    // Find the node in list wich matches the 
+    // given name and parent_cluster
+    while (node != NULL)
+    {
+        // First compare the parent cluster number and 
+        // check filename length since it is faster than 
+        // comparing filenames
+        if (parent_cluster == node->parent_cluster &&
+            '\0' == node->filename[namelen])
+        {
+            int i = strncasecmp(node->filename, name, namelen);
+
+            if (i == 0)
+                return node;
+            else if (i > 0)
+                return NULL; // Stop searching - we have a 
+                             // sorted list so there can't be
+                             // any matching filename further on
+                             // if i > 0 - look at node_hash_add
+        }
+        node = node->hash_next;
+    }
+    
+    // No such node found
+    return NULL; 
+}
+
+static bool
+node_hash_remove_keyless(fatfs_hash_table_t *tbl, fatfs_node_t *node)
+{
+    int i;
+    fatfs_node_t *lnode, *pnode;
+
+    // Look in all slots, since we have no key
+    for (i = 0; i < tbl->size; i++)
+    {
+        // Look in list and remove it if there
+        lnode = tbl->nodes[i];
+        pnode = NULL;
+        while (lnode != NULL)
+        {
+            if (lnode == node)
+            {
+                if (pnode == NULL)
+                    tbl->nodes[i]    = lnode->hash_next;
+                else
+                    pnode->hash_next = lnode->hash_next;
+                node->hash_next = NULL;
+                tbl->n--;
+            
+                // All done
+                return true;
+            }
+            pnode = lnode;
+            lnode = lnode->hash_next;
+        }    
+    }
+    return false;
+}
+
+static bool 
+node_hash_remove(fatfs_hash_table_t *tbl, fatfs_node_t *node)
+{
+    unsigned int hval;
+    fatfs_node_t *lnode, *pnode;
+   
+    // Calculate hash of name and get the first node in slot
+    hval = hash_fn(node->filename, strlen(node->filename)) % tbl->size;
+    lnode = tbl->nodes[hval];
+
+    // Now find the node in list and remove it
+    pnode = NULL;
+    while (lnode != NULL)
+    {
+        if (lnode == node)
+        {
+            if (pnode == NULL)
+                tbl->nodes[hval] = lnode->hash_next;
+            else
+                pnode->hash_next = lnode->hash_next;
+            node->hash_next = NULL;
+            tbl->n--;
+            
+            // All done
+            return true;
+        }
+        pnode = lnode;
+        lnode = lnode->hash_next;
+    } 
+
+    // Node not in list 
+    return false;
+}
+
+#ifdef USE_XCHECKS
+static void
+node_hash_check(fatfs_hash_table_t *tbl)
+{
+    int i, n;
+
+    n = 0;
+    for (i = 0; i < tbl->size; i++)
+    {
+        fatfs_node_t *lnode, *pnode;
+
+        pnode = NULL;
+        lnode = tbl->nodes[i];
+
+        while (lnode != NULL)
+        {
+            if (pnode != NULL)
+            {
+                int c = strcasecmp(pnode->filename, lnode->filename);
+                CYG_ASSERT(c <= 0, "hash table not sorted");
+                CYG_ASSERT(pnode->parent_cluster != lnode->parent_cluster ||
+                           0 != c, "duplicated node in hash table");
+            }
+            n++;
+            pnode = lnode;
+            lnode = lnode->hash_next;
+        }
+    }
+    CYG_ASSERTC(tbl->n == n);
+}
+
+static void
+node_hash_not_found_check(fatfs_disk_t *disk, 
+                          const char   *name,
+                          unsigned int  namelen,
+                          unsigned int  parent_cluster)
+{
+    fatfs_node_t* node;
+
+    node = node_list_get_head(&disk->live_nlist);
+    while (NULL != node)
+    {
+        if (node->parent_cluster == parent_cluster          &&
+            namelen == strlen(node->filename)               &&
+            0 == strncasecmp(name, node->filename, namelen))
+            CYG_ASSERT(false, "node not found in hash, "
+                              "but exists in live list");
+        node = node_list_get_next(node);
+    }
+
+    node = node_list_get_head(&disk->dead_nlist);
+    while (NULL != node)
+    {
+        if (node->parent_cluster == parent_cluster          &&
+            namelen == strlen(node->filename)               &&
+            0 == strncasecmp(name, node->filename, namelen))
+            CYG_ASSERT(false, "node not found in hash, "
+                              "but exists in dead list");
+        node = node_list_get_next(node);
+    }   
+}
+
+static void
+node_hash_found_check(fatfs_disk_t *disk, 
+                      const char   *name,
+                      unsigned int  namelen,
+                      unsigned int  parent_cluster,
+                      fatfs_node_t* node)
+{
+    fatfs_node_t* n;
+
+    n = node_list_get_head(&disk->live_nlist);
+    while (NULL != n)
+    {
+        if (n == node) 
+        {
+            if (node->parent_cluster != parent_cluster ||
+                namelen != strlen(node->filename)      ||
+                0 != strncasecmp(name, node->filename, namelen))
+                CYG_ASSERT(false, "node_hash_find returned wrong node");
+            return;
+        }
+        n = node_list_get_next(n);
+    }
+
+    n = node_list_get_head(&disk->dead_nlist);
+    while (NULL != n)
+    {
+        if (n == node)
+        {
+            if (node->parent_cluster != parent_cluster ||
+                namelen != strlen(node->filename)      ||
+                0 != strncasecmp(name, node->filename, namelen))
+                CYG_ASSERT(false, "node_hash_find returned wrong node");
+            return;
+        } 
+        n = node_list_get_next(n);
+    }
+
+    CYG_ASSERT(false, "node not found in dead or live lists, "
+                      "but exists in hash.");
+}
+#endif // USE_XCHECKS
+    
+//--------------------------------------------------------------------------
+
+#ifdef USE_XCHECKS
+
+# define SANITY_CHECK()                                                     \
+    CYG_MACRO_START                                                         \
+        node_lists_check(disk);                                             \
+        node_hash_check(&disk->node_hash);                                  \
+        CYG_ASSERTC((disk->live_nlist.size + disk->dead_nlist.size) ==      \
+                    disk->node_hash.n);                                     \
+    CYG_MACRO_END    
+
+# define NODE_FIND_CHECK()                                                    \
+    CYG_MACRO_START                                                           \
+        if (NULL == node)                                                     \
+            node_hash_not_found_check(disk, name, namelen, parent_cluster);   \
+        else                                                                  \
+            node_hash_found_check(disk, name, namelen, parent_cluster, node); \
+    CYG_MACRO_END
+
+#else // USE_XCHECKS
+# define SANITY_CHECK()    CYG_EMPTY_STATEMENT
+# define NODE_FIND_CHECK() CYG_EMPTY_STATEMENT
+#endif // not USE_XCHECKS
+
+//==========================================================================
+//==========================================================================
+// Exported functions 
+
+//--------------------------------------------------------------------------
+// fatfs_node_cache_init()
+// Initializes node cache of given disk.
+
+void
+fatfs_node_cache_init(fatfs_disk_t *disk)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    
+    node_list_init(&disk->live_nlist);
+    node_list_init(&disk->dead_nlist);
+    node_hash_init(&disk->node_hash);
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_cache_flush()
+// Frees all node cache of given disk.
+
+void
+fatfs_node_cache_flush(fatfs_disk_t *disk)
+{
+    fatfs_node_t *node, *next_node;
+ 
+    node = node_list_get_head(&disk->live_nlist);
+
+    while (NULL != node)
+    {
+        next_node = node_list_get_next(node);
+        node_list_remove(&disk->live_nlist, node);
+        if (!node_hash_remove(&disk->node_hash, node))
+            CYG_ASSERT(false, "Node not in hash");
+        fatfs_tcache_flush(disk, &node->tcache);
+        free(node);
+        node = next_node;
+    }
+
+    node = node_list_get_head(&disk->dead_nlist);
+
+    while (NULL != node)
+    {
+        next_node = node_list_get_next(node);
+        node_list_remove(&disk->dead_nlist, node);
+        if (!node_hash_remove(&disk->node_hash, node))
+            CYG_ASSERT(false, "Node not in hash");
+        fatfs_tcache_flush(disk, &node->tcache);
+        free(node);
+        node = next_node;
+    }
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_alloc()
+// Allocates a new node.  
+
+fatfs_node_t*
+fatfs_node_alloc(fatfs_disk_t *disk, fatfs_node_t *node_data)
+{
+    int lsize, dsize;
+    fatfs_node_t *anode;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node_data);
+ 
+    lsize = node_list_get_size(&disk->live_nlist);
+    dsize = node_list_get_size(&disk->dead_nlist);
+   
+    CYG_TRACE2(TNC, "lsize=%d dsize=%d", lsize, dsize);
+    
+    // Allocate space for a new node if we haven't reached the 
+    // allocation treshold or if we can't reuse dead nodes space
+    if (dsize > DLIST_KEEP_NUM && (lsize + dsize) >= 
+        (FATFS_NODE_ALLOC_THRESHOLD - 1))
+        anode = NULL;
+    else
+        anode = (fatfs_node_t *)malloc(sizeof(fatfs_node_t));
+        
+    if (NULL == anode)
+    {
+        CYG_TRACE0(TNC, "getting node from dead list");
+        
+        if (dsize <= DLIST_KEEP_NUM)
+            return NULL;
+        
+        anode = node_list_tail_get(&disk->dead_nlist);
+        if (NULL == anode)
+            return NULL;
+        
+        CYG_TRACE1(TNC, "recycling node='%s'", anode->filename); 
+
+        // Flush FAT table cache
+        fatfs_tcache_flush(disk, &anode->tcache);
+        
+        node_list_remove(&disk->dead_nlist, anode);
+        if (!node_hash_remove(&disk->node_hash, anode))
+            CYG_ASSERT(false, "Node not in hash");
+    }     
+
+    // Init new node    
+    *anode = *node_data;
+    anode->refcnt = 0;
+
+    // Init FAT table cache
+    fatfs_tcache_init(disk, &anode->tcache);
+
+    node_list_head_add(&disk->dead_nlist, anode);
+    if (!node_hash_add(&disk->node_hash, anode))
+        CYG_ASSERT(false, "Node already in hash");
+
+    SANITY_CHECK();
+
+    return anode;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_touch()
+// Moves given node to top of its list.
+// (When reusing dead node it is always taken from the bottom of list) 
+
+void
+fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt);
+
+    if (node->refcnt == 0)
+    {
+        node_list_remove(&disk->dead_nlist, node);
+        node_list_head_add(&disk->dead_nlist, node);
+    }
+    else
+    {
+        node_list_remove(&disk->live_nlist, node);
+        node_list_head_add(&disk->live_nlist, node);
+    } 
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_ref()
+// Increases the given node reference count.
+// If the reference goes from 0 to 1, than the node
+// is moved from dead list to live list.
+
+void
+fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt);
+    
+    // Increase node reference counter
+    node->refcnt++;    
+    if (1 == node->refcnt)
+    {
+        CYG_TRACE1(TNC, "node='%s' to live list", node->filename);
+        // First reference - move node from dead to live list
+        node_list_remove(&disk->dead_nlist, node);
+        node_list_head_add(&disk->live_nlist, node);
+    }
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_unref()
+// Decreases the given node reference count.
+// If the reference goes from 1 to 0, than the node
+// is moved from live list to top of dead list.
+// (When reusing dead node it is always taken from the bottom of list)
+// If we are over the allocation treshold the bottom node of
+// dead list if freed.
+
+void
+fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt);
+    CYG_ASSERT(node->refcnt > 0, "node->refcnt <= 0");
+
+    node->refcnt--;
+    if (node->refcnt == 0)
+    {
+        // No more references - move node from live to dead list
+        CYG_TRACE1(TNC, "node='%s' to dead list", node->filename);
+        node_list_remove(&disk->live_nlist, node);
+        node_list_head_add(&disk->dead_nlist, node);
+
+        // Check the number of allocated nodes and free 
+        // the last node in dead list if we are over 
+        // the treshold and we have enough dead nodes
+        {
+            int lsize = node_list_get_size(&disk->live_nlist);
+            int dsize = node_list_get_size(&disk->dead_nlist);
+   
+            if (dsize > DLIST_KEEP_NUM && 
+                (lsize + dsize) >= FATFS_NODE_ALLOC_THRESHOLD) 
+            {
+                fatfs_node_t *n = node_list_get_tail(&disk->dead_nlist);
+                CYG_TRACE1(TNC, "freeing node='%s' - to satisfy "
+                                "alloc treshold", n->filename);
+                fatfs_node_free(disk, n);
+            }
+        }
+    }
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_free()
+// Frees node memory and removes it from its list and hash. 
+// This function must not be called on a referenced node.
+
+void
+fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt);
+    CYG_ASSERTC(node->refcnt == 0);
+    CYG_ASSERTC(node != disk->root);
+
+    // Flush FAT table cache    
+    fatfs_tcache_flush(disk, &node->tcache);
+
+    // Remove from dead list and from hash and free ptr
+    node_list_remove(&disk->dead_nlist, node);
+    if (!node_hash_remove(&disk->node_hash, node))
+        CYG_ASSERT(false, "node not in hash");    
+    free(node);
+
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_rehash()
+// Recalculates given node hash key. 
+
+void
+fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    
+    if (!node_hash_remove_keyless(&disk->node_hash, node))
+        CYG_ASSERT(false, "Node not in hash");
+    
+    if (!node_hash_add(&disk->node_hash, node))
+        CYG_ASSERT(false, "Node already in hash");
+    
+    SANITY_CHECK();
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_find()
+// Finds node in hash by its name and parent cluster. 
+// If no node found NULL is returned.
+
+fatfs_node_t*
+fatfs_node_find(fatfs_disk_t *disk, 
+                const char   *name, 
+                unsigned int  namelen,
+                unsigned int  parent_cluster)
+{
+    fatfs_node_t *node;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(name);
+ 
+    node = node_hash_find(&disk->node_hash, name, namelen, parent_cluster);
+
+    NODE_FIND_CHECK();
+    
+    CYG_TRACE3(TNC, "node name=%s pcluster=%d %s found in cache\n",
+        name, parent_cluster, ((node != NULL) ? "" : "not"));
+    
+    return node;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_get_live_node_count()
+// Gets the number of live nodes.
+
+int
+fatfs_get_live_node_count(fatfs_disk_t *disk)
+{
+    return node_list_get_size(&disk->live_nlist);
+}
+
+//--------------------------------------------------------------------------
+// fatfs_get_dead_node_count()
+// Gets the number of dead nodes.
+
+int
+fatfs_get_dead_node_count(fatfs_disk_t *disk)
+{
+    return node_list_get_size(&disk->dead_nlist);
+}
+
+//--------------------------------------------------------------------------
+// fatfs_node_flush_dead_tcache()
+// Flushes FAT table cache of dead nodes.
+
+void
+fatfs_node_flush_dead_tcache(fatfs_disk_t *disk)
+{
+    fatfs_node_t *node;
+ 
+    CYG_CHECK_DATA_PTRC(disk);
+    
+    node = node_list_get_tail(&disk->dead_nlist);
+
+    while (NULL != node)
+    {
+        CYG_TRACE1(TNC, "node='%s'", node->filename);
+        fatfs_tcache_flush(disk, &node->tcache);
+        node = node_list_get_prev(node);
+    }
+}
+
+// -------------------------------------------------------------------------
+// EOF fatfs_ncache.c
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/src/fatfs_supp.c
@@ -0,0 +1,2481 @@
+//==========================================================================
+//
+//      fatfs_supp.c
+//
+//      FAT file system support functions
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           savin 
+// Date:                2003-06-30
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/fs_fat.h>
+#include <pkgconf/infra.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+#include <cyg/io/io.h>
+#include <blib/blib.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "fatfs.h"
+
+//==========================================================================
+// FAT defines & macros
+
+// -------------------------------------------------------------------------
+// FAT dir entry attributes
+
+#define DENTRY_ATTR_RDONLY  0x01 // Read only
+#define DENTRY_ATTR_HIDDEN  0x02 // Hidden
+#define DENTRY_ATTR_SYSTEM  0x04 // System
+#define DENTRY_ATTR_VOLUME  0x08 // Volume label
+#define DENTRY_ATTR_DIR     0x10 // Subdirectory
+#define DENTRY_ATTR_ARCHIVE 0x20 // Needs archiving
+
+// -------------------------------------------------------------------------
+// FAT dir entry attributes macros
+
+#define DENTRY_IS_RDONLY(_dentry_)  ((_dentry_)->attr & DENTRY_ATTR_RDONLY)
+#define DENTRY_IS_HIDDEN(_dentry_)  ((_dentry_)->attr & DENTRY_ATTR_HIDDEN)
+#define DENTRY_IS_SYSTEM(_dentry_)  ((_dentry_)->attr & DENTRY_ATTR_SYSTEM)
+#define DENTRY_IS_VOLUME(_dentry_)  ((_dentry_)->attr & DENTRY_ATTR_VOLUME)
+#define DENTRY_IS_DIR(_dentry_)     ((_dentry_)->attr & DENTRY_ATTR_DIR)
+#define DENTRY_IS_ARCHIVE(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_ARCHIVE)
+
+#define DENTRY_IS_DELETED(_dentry_) \
+    (0xE5 == (unsigned char)((_dentry_)->name[0]))
+
+#define DENTRY_IS_ZERO(_dentry_) \
+    (0x00 == (unsigned char)((_dentry_)->name[0]))
+
+// -------------------------------------------------------------------------
+// FAT disk data access macros
+
+// FIXME: support big endian machines!
+   
+#define GET_BYTE(_data_, _var_, _off_) \
+    (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) )
+
+#define GET_WORD(_data_, _var_, _off_)                      \
+    (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) |         \
+             *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8)
+
+#define GET_DWORD(_data_, _var_, _off_)                         \
+    (_var_ = *( ((cyg_uint8 *)_data_) + (_off_))             |  \
+             *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8   |  \
+             *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) << 16  |  \
+             *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) << 24)
+
+#define GET_BYTES(_data_, _var_, _size_, _off_) \
+    memcpy((void *)(_var_), (void*)(((cyg_uint8 *)_data_)+(_off_)),_size_)
+
+#define SET_BYTE(_data_, _val_, _off_) \
+    (*( ((cyg_uint8 *)_data_) + (_off_) ) = _val_)
+
+#define SET_WORD(_data_, _val_, _off_)                                   \
+    do {                                                                 \
+        *( ((cyg_uint8 *)_data_) + (_off_) )     = _val_         & 0xFF; \
+        *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8)  & 0xFF; \
+    } while (0)
+
+#define SET_DWORD(_data_, _val_, _off_)                                  \
+    do {                                                                 \
+        *( ((cyg_uint8 *)_data_) + (_off_) )     = _val_         & 0xFF; \
+        *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8)  & 0xFF; \
+        *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) = (_val_ >> 16) & 0xFF; \
+        *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) = (_val_ >> 24) & 0xFF; \
+    } while (0)
+
+#define SET_BYTES(_data_, _var_, _size_, _off_) \
+    memcpy((void *)(((cyg_uint8 *)_data_)+(_off_)), (void *)(_var_), _size_)
+
+// -------------------------------------------------------------------------
+// FAT table entries types 
+
+#define TENTRY_REGULAR  0 // Used when entry points to next file cluster 
+#define TENTRY_FREE     1 // Free cluster
+#define TENTRY_LAST     2 // Last cluster of file 
+#define TENTRY_RESERVED 3 // Reserved cluster
+#define TENTRY_BAD      4 // Bad cluster 
+
+// -------------------------------------------------------------------------
+// FAT table structures size 
+
+#define DENTRY_SIZE      0x20 // Dir entry size
+
+// -------------------------------------------------------------------------
+// Time & date defines 
+
+#define JD_1_JAN_1970 2440588 // 1 Jan 1970 in Julian day number
+
+// -------------------------------------------------------------------------
+// Code tracing defines 
+
+#ifdef FATFS_TRACE_FAT_TABLE
+# define TFT 1
+#else
+# define TFT 0
+#endif
+
+#ifdef FATFS_TRACE_DIR_ENTRY
+# define TDE 1
+#else
+# define TDE 0
+#endif
+
+#ifdef FATFS_TRACE_CLUSTER
+# define TCL 1
+#else
+# define TCL 0
+#endif
+
+#ifdef FATFS_TRACE_DATA
+# define TDO 1
+#else
+# define TDO 0
+#endif
+    
+// -------------------------------------------------------------------------
+// FAT table entry type strings 
+
+#if TFT
+static const char *tentry_type_name[5] = {
+    "REGULAR", "FREE", "LAST", "RESERVED", "BAD"
+};   
+#endif
+
+//==========================================================================
+// FAT structures 
+
+// -------------------------------------------------------------------------
+// FAT table boot record structure 
+   
+typedef struct fat_boot_record_s
+{
+    cyg_uint16    jump;           // 00h : Jump code
+//  cyg_uint8     jump0;          //                 + NOP
+    char          oem_name[8+1];  // 03h : OEM name
+    cyg_uint16    bytes_per_sec;  // 0Bh : cyg_bytes per sector
+    cyg_uint8     sec_per_clust;  // 0Dh : Sectors per cluster
+    cyg_uint16    res_sec_num;    // 0Eh : Number of reserved sectors
+    cyg_uint8     fat_tbls_num;   // 10h : Number of copies of fat
+    cyg_uint16    max_root_dents; // 11h : Maximum number of root dir entries
+    cyg_uint16    sec_num_32;     // 13h : Number of sectors in partition < 32MB
+    cyg_uint8     media_desc;     // 15h : Media descriptor
+    cyg_uint16    sec_per_fat;    // 16h : Sectors per FAT
+    cyg_uint16    sec_per_track;  // 18h : Sectors per track
+    cyg_uint16    heads_num;      // 1Ah : Number of heads
+    cyg_uint32    hsec_num;       // 1Ch : Number of hidden sectors
+    cyg_uint32    sec_num;        // 20h : Number of sectors in partition
+    cyg_uint16    ldrv_num;       // 24h : Logical drive number of partition
+    cyg_uint8     ext_sig;        // 26h : Extended signature
+    cyg_uint32    ser_num;        // 27h : Serial number of partition
+    char          vol_name[11+1]; // 2Bh : Volume name of partition
+    char          fat_name[8+1];  // 36h : FAT name
+//  unsigned char exe_code[448];  // 3Eh : Executable code
+    unsigned char exe_marker[2];  // 1FEh: Executable marker (55h AAh)
+} fat_boot_record_t;
+
+// -------------------------------------------------------------------------
+// FAT dir entry structure 
+ 
+typedef struct fat_dir_entry_s
+{
+    char       name[8+1];       // 00h : Name
+    char       ext[3+1];        // 08h : Extension
+    cyg_uint8  attr;            // 0Bh : Attribute
+    cyg_uint8  nt_reserved;     // 0Ch : Win NT Reserved field
+    cyg_uint8  crt_sec_100;     // 0Dh : Creation time ms stamp 0 - 199
+    cyg_uint16 crt_time;        // 0Eh : Creation time
+    cyg_uint16 crt_date;        // 10h : Creation date
+    cyg_uint16 acc_date;        // 12h : Last access date
+    cyg_uint16 cluster_HI;      // 14h : Starting cluster HI WORD (FAT32)
+    cyg_uint16 wrt_time;        // 16h : Time    
+    cyg_uint16 wrt_date;        // 18h : Date
+    cyg_uint16 cluster;         // 1Ah : Starting cluster 
+    cyg_uint32 size;            // 1Ch : Size of the file    
+
+    fatfs_data_pos_t pos;       // Positon on disk
+} fat_dir_entry_t;
+
+// -------------------------------------------------------------------------
+// FAT cluster opts 
+ 
+typedef enum cluster_opts_e
+{
+    CO_NONE       = 0x00, // NULL option
+    CO_EXTEND     = 0x01, // Extend cluster chain if one cluster too short
+    CO_ERASE_NEW  = 0x02, // Erase newly allocated cluster
+    CO_MARK_LAST  = 0x04  // Mark  newly allocated cluster as last
+} cluster_opts_t;
+
+//==========================================================================
+// Utility functions 
+
+// -------------------------------------------------------------------------
+// get_val_log2()
+// Gets the log2 of given value or returns 0 if value is 
+// not power of 2. 
+ 
+static cyg_uint32 
+get_val_log2(cyg_uint32 val)
+{
+    cyg_uint32 i, log2;
+    
+    i = val;
+    log2 = 0;
+    while (0 == (i & 1))
+    {
+        i >>= 1;
+        log2++;
+    }
+    if (i != 1)
+        return 0;
+    else
+        return log2;
+}
+
+// -------------------------------------------------------------------------
+// get_data_disk_apos()
+// Gets the absolute data position on disk from cluster number and
+// position inside given cluster. 
+ 
+static __inline__ cyg_uint32
+get_data_disk_apos(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 pos)
+{
+    return (disk->fat_data_pos + disk->cluster_size * (cluster - 2) + pos);
+}
+
+// -------------------------------------------------------------------------
+// jdays_to_gdate()
+// Converts juilan days into gregorian date.
+ 
+static void
+jdays_to_gdate(cyg_uint32 jd, int *day, int *month, int *year)
+{
+    cyg_uint32 l, n, i, j;
+
+    l = jd + 68569;
+    n = (4 * l) / 146097;
+    l = l - (146097 * n + 3) / 4;
+    i = (4000 * (l + 1)) / 1461001;
+    l = l - (1461 * i) / 4 + 31;
+    j = (80 * l) / 2447;
+    *day = l - (2447 * j) / 80;
+
+    l = j / 11;
+    *month = j + 2 - (12 * l);
+    *year = 100 * (n - 49) + i + l;
+}
+
+// -------------------------------------------------------------------------
+// gdate_to_jdays()
+// Converts gregorian date to juilan days.
+ 
+static void
+gdate_to_jdays(int day, int month, int year, cyg_uint32 *jd)
+{
+    *jd = day - 32075 + 1461*(year + 4800 + (month - 14)/12)/4 +
+          367*(month - 2 - (month - 14)/12*12)/12 - 
+          3*((year + 4900 + (month - 14)/12)/100)/4;
+}
+ 
+// -------------------------------------------------------------------------
+// date_unix2dos()
+// Converts unix timestamp to dos time and date. 
+                 
+static void
+date_unix2dos(cyg_uint32  unix_timestamp, 
+              cyg_uint16 *dos_time,
+              cyg_uint16 *dos_date)
+{
+    cyg_uint32 jd;
+    cyg_uint16 dtime, ddate;
+    int hour, min, sec;
+    int day, month, year;
+    
+    hour = (unix_timestamp / 3600) % 24;
+    min  = (unix_timestamp / 60) % 60;
+    sec  =  unix_timestamp % 60;
+
+    jd = JD_1_JAN_1970 + unix_timestamp / (3600 * 24);
+    jdays_to_gdate(jd, &day, &month, &year);
+
+    CYG_TRACE7(TDE, "ts=%d date=%d:%d:%d %d-%d-%d",
+                   unix_timestamp, hour, min, sec, year, month, day);
+
+    if (year < 1980)
+        year = 1980;
+
+    dtime = (hour << 11) | (min << 5) | (sec >> 1);
+    ddate = ((year - 1980) << 9) | (month << 5) | day;
+ 
+    CYG_TRACE2(TDE, "dos time=%d date=%d", dtime, ddate);
+    
+    if (NULL != dos_time) *dos_time = dtime;
+    if (NULL != dos_date) *dos_date = ddate;
+}
+
+// -------------------------------------------------------------------------
+// date_dos2unix()
+// Converts dos time and date to unix timestamp. 
+ 
+static void
+date_dos2unix(cyg_uint16  dos_time, 
+              cyg_uint16  dos_date, 
+              cyg_uint32 *unix_timestamp)
+{
+    int hour, min, sec;
+    int day, month, year;
+    cyg_uint32 ts; 
+    
+    sec = (dos_time & ((1<<5)-1)) * 2;
+    dos_time >>= 5;
+    min = (dos_time & ((1<<6)-1));
+    dos_time >>= 6;
+    hour = dos_time;
+    
+    day = (dos_date & ((1<<5)-1));
+    dos_date >>= 5;
+    month = (dos_date & ((1<<4)-1));
+    dos_date >>= 4;
+    year = dos_date + 1980;
+
+    gdate_to_jdays(day, month, year, &ts);
+    ts -= JD_1_JAN_1970;
+    ts = (ts * 24 * 3600) + (sec + min * 60 + hour * 3600);
+    
+    *unix_timestamp = ts;
+
+    CYG_TRACE2(TDE, "dos time=%d date=%d", dos_time, dos_date);
+    CYG_TRACE7(TDE, "timestamp=%d date=%d:%d:%d %d-%d-%d",
+                    ts, hour, min, sec, year, month, day);
+}
+
+//==========================================================================
+// FAT boot record functions 
+
+// -------------------------------------------------------------------------
+// print_boot_record()
+// Prints FAT boot record.
+
+#if TFT 
+static void
+print_boot_record(fat_boot_record_t* fbr)
+{
+    diag_printf("FAT: FBR jump code:       0x%02X\n", fbr->jump);
+    diag_printf("FAT: FBR oem name:       '%.8s'\n",  fbr->oem_name);
+    diag_printf("FAT: FBR bytes per sec:   %u\n",     fbr->bytes_per_sec);
+    diag_printf("FAT: FBR sec per cluster: %u\n",     fbr->sec_per_clust);
+    diag_printf("FAT: FBR reserved sec:    %u\n",     fbr->res_sec_num);
+    diag_printf("FAT: FBR fat tbls num:    %u\n",     fbr->fat_tbls_num);
+    diag_printf("FAT: FBR max root dents:  %u\n",     fbr->max_root_dents);
+    diag_printf("FAT: FBR sec num (32):    %u\n",     fbr->sec_num_32);
+    diag_printf("FAT: FBR media desc:      0x%02X\n", fbr->media_desc);
+    diag_printf("FAT: FBR sec per fat:     %u\n",     fbr->sec_per_fat);
+    diag_printf("FAT: FBR sec per track:   %u\n",     fbr->sec_per_track);
+    diag_printf("FAT: FBR heads num:       %u\n",     fbr->heads_num);
+    diag_printf("FAT: FBR hidden sec num:  %u\n",     fbr->hsec_num);
+    diag_printf("FAT: FBR sec num:         %u\n",     fbr->sec_num);
+    diag_printf("FAT: FBR log drv num:     %u\n",     fbr->ldrv_num);
+    diag_printf("FAT: FBR ext sig:         0x%02X\n", fbr->ext_sig);
+    diag_printf("FAT: FBR ser num:         0x%08X\n", fbr->ser_num);
+    diag_printf("FAT: FBR vol name:       '%.11s'\n", fbr->vol_name);
+    diag_printf("FAT: FBR fat name:       '%.8s'\n",  fbr->fat_name);
+    diag_printf("FAT: FBR exe mark:        0x%02X 0x%02X\n", 
+                fbr->exe_marker[0], fbr->exe_marker[1]);
+}
+#endif // TFT
+
+// -------------------------------------------------------------------------
+// read_boot_record()
+// Reads FAT boot record from disk.
+ 
+static int 
+read_boot_record(fatfs_disk_t *disk, fat_boot_record_t *fbr)
+{
+    int len, err;
+    unsigned char data[0x3E];
+    
+    len = 0x3E;
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, 0);
+    if (err != ENOERR)
+        return err;
+   
+    GET_WORD(data,  fbr->jump,           0x00);
+    GET_BYTES(data, fbr->oem_name, 8,    0x03);
+    GET_WORD(data,  fbr->bytes_per_sec,  0x0B);
+    GET_BYTE(data,  fbr->sec_per_clust,  0x0D);
+    GET_WORD(data,  fbr->res_sec_num,    0x0E);
+    GET_BYTE(data,  fbr->fat_tbls_num,   0x10);
+    GET_WORD(data,  fbr->max_root_dents, 0x11);
+    GET_WORD(data,  fbr->sec_num_32,     0x13);
+    GET_BYTE(data,  fbr->media_desc,     0x15);
+    GET_WORD(data,  fbr->sec_per_fat,    0x16);
+    GET_WORD(data,  fbr->sec_per_track,  0x18);
+    GET_WORD(data,  fbr->heads_num,      0x1A);
+    GET_DWORD(data, fbr->hsec_num,       0x1C);
+    GET_DWORD(data, fbr->sec_num,        0x20);
+    GET_WORD(data,  fbr->ldrv_num,       0x24);
+    GET_BYTE(data,  fbr->ext_sig,        0x26);
+    GET_DWORD(data, fbr->ser_num,        0x27);
+    GET_BYTES(data, fbr->vol_name, 11,   0x2B);
+    GET_BYTES(data, fbr->fat_name, 8,    0x36);
+
+    // Skip the exe code and read the end marker
+    len = 0x02;
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, 0x1FE);
+    if (err != ENOERR)
+        return err;
+
+    GET_BYTES(data, fbr->exe_marker, 2,  0);
+
+    // Zero terminate strings
+    fbr->oem_name[8]  = '\0';
+    fbr->vol_name[11] = '\0';
+    fbr->fat_name[8]  = '\0';
+  
+#if TFT 
+    print_boot_record(fbr);
+#endif
+    
+    return ENOERR;
+}
+
+//==========================================================================
+// FAT table entry functions 
+
+// -------------------------------------------------------------------------
+// read_tentry()
+// Reads FAT table entry from disk.
+
+static int
+read_tentry_fat12(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    unsigned char data[2];
+    cyg_uint32 pos, num3;
+    cyg_uint16 e;
+    int len, err;
+
+    num3 = num * 3;
+    pos  = disk->fat_tbl_pos + (num3 >> 1);
+    len  = 2;
+    
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos);
+    if (err != ENOERR)
+        return err;
+
+    GET_WORD(data, e, 0x00);
+
+    if (0 == (num3 & 1))
+        *entry = e & 0x0FFF;
+    else
+        *entry = (e >> 4) & 0x0FFF;
+        
+    CYG_TRACE3(TFT, "tentry=%x num=%d at %d", *entry, num, pos);
+    
+    return ENOERR;
+}
+
+static int
+read_tentry_fat16(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    unsigned char data[2];
+    cyg_uint32 pos;
+    cyg_uint16 e;
+    int len, err;
+
+    pos = disk->fat_tbl_pos + (num << 1);
+    len = 2;
+    
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos);
+    if (err != ENOERR)
+        return err;
+
+    GET_WORD(data, e, 0x00);
+    *entry = e;
+
+    CYG_TRACE3(TFT, "tentry=%x num=%d at %d", *entry, num, pos);
+    
+    return ENOERR;
+}
+
+static int
+read_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    switch (disk->fat_type)
+    {
+        case FATFS_FAT12: return read_tentry_fat12(disk, num, entry);
+        case FATFS_FAT16: return read_tentry_fat16(disk, num, entry);
+        default: return EINVAL;                  
+    }
+}
+
+// -------------------------------------------------------------------------
+// write_tentry()
+// Writes FAT table entry to disk (to all copies of FAT).
+ 
+static int
+write_tentry_fat12(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    unsigned char data[2];
+    cyg_uint32 pos, num3; 
+    cyg_uint16 e;
+    int i, len, err;
+
+    num3 = num * 3;
+    pos  = disk->fat_tbl_pos + (num3 >> 1);
+    len  = 2;
+   
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos);
+    if (err != ENOERR)
+        return err;
+
+    GET_WORD(data, e, 0x00);
+  
+    if (0 == (num3 & 1)) 
+        e = (e & 0xF000) | (*entry & 0x0FFF);
+    else
+        e = (e & 0x000F) | ((*entry & 0x0FFF) << 4);
+    
+    SET_WORD(data, e, 0x00);
+
+    for (i = 0; i < disk->fat_tbls_num; i++)
+    {
+        err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, pos);
+        if (err != ENOERR)
+            return err;
+
+        CYG_TRACE4(TFT, "tentry=%x num=%d at %d tbl=%d", *entry, num, pos, i);
+
+        pos += disk->fat_tbl_size;
+    }
+    
+    return ENOERR;
+}
+
+static int
+write_tentry_fat16(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    unsigned char data[2];
+    cyg_uint32 pos; 
+    cyg_uint16 e;
+    int i, len, err;
+
+    pos = disk->fat_tbl_pos + (num << 1);
+    len = 2;
+    
+    e = *entry;
+    SET_WORD(data, e, 0x00);
+
+    for (i = 0; i < disk->fat_tbls_num; i++)
+    {
+        err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, pos);
+        if (err != ENOERR)
+            return err;
+
+        CYG_TRACE4(TFT, "tentry=%x num=%d at %d tbl=%d", *entry, num, pos, i);
+
+        pos += disk->fat_tbl_size;
+    }
+    
+    return ENOERR;
+}
+
+static int
+write_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry)
+{
+    switch (disk->fat_type)
+    {
+        case FATFS_FAT12: return write_tentry_fat12(disk, num, entry);
+        case FATFS_FAT16: return write_tentry_fat16(disk, num, entry);
+        default: return EINVAL;                  
+    }
+}
+
+// -------------------------------------------------------------------------
+// get_tentry_type()
+// Gets the type of FAT table entry.
+ 
+static int
+get_tentry_type_fat12(fatfs_disk_t *disk, cyg_uint32 entry)
+{
+    int type;
+
+    if (entry < 0x0FF0)
+    {
+        if (0x0000 == entry)  type = TENTRY_FREE;
+        else                  type = TENTRY_REGULAR;
+    }
+    else if (entry >= 0x0FF8) type = TENTRY_LAST;
+    else if (0x0FF7 == entry) type = TENTRY_BAD;
+    else                      type = TENTRY_RESERVED;
+
+#if TFT
+    CYG_TRACE2(TFT, "tentry=%04X type=%s", entry, tentry_type_name[type]);
+#endif
+
+    return type;
+}
+
+static int
+get_tentry_type_fat16(fatfs_disk_t *disk, cyg_uint32 entry)
+{
+    int type;
+
+    if (entry < 0xFFF0)
+    {
+        if (0x0000 == entry)  type = TENTRY_FREE;
+        else                  type = TENTRY_REGULAR;
+    }
+    else if (entry >= 0xFFF8) type = TENTRY_LAST;
+    else if (0xFFF7 == entry) type = TENTRY_BAD;
+    else                      type = TENTRY_RESERVED;
+
+#if TFT
+    CYG_TRACE2(TFT, "tentry=%04X type=%s", entry, tentry_type_name[type]);
+#endif
+
+    return type;
+}
+
+static int
+get_tentry_type(fatfs_disk_t *disk, cyg_uint32 entry)
+{
+    switch (disk->fat_type)
+    {
+        case FATFS_FAT12: return get_tentry_type_fat12(disk, entry);
+        case FATFS_FAT16: return get_tentry_type_fat16(disk, entry);
+        default: return TENTRY_BAD;
+    }
+}
+
+// -------------------------------------------------------------------------
+// set_tentry_type()
+// Sets the type of FAT table entry.
+ 
+static void 
+set_tentry_type_fat12(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type)
+{
+    switch (type)
+    {
+        case TENTRY_FREE:     *entry = 0x0000; break;
+        case TENTRY_LAST:     *entry = 0x0FF8; break;
+        case TENTRY_RESERVED: *entry = 0x0FF0; break;
+        case TENTRY_BAD:      *entry = 0x0FF7; break;      
+        default:
+            CYG_ASSERT(false, "Unknown tentry type");
+    }
+}
+
+static void 
+set_tentry_type_fat16(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type)
+{
+    switch (type)
+    {
+        case TENTRY_FREE:     *entry = 0x0000; break;
+        case TENTRY_LAST:     *entry = 0xFFF8; break;
+        case TENTRY_RESERVED: *entry = 0xFFF0; break;
+        case TENTRY_BAD:      *entry = 0xFFF7; break;      
+        default:
+            CYG_ASSERT(false, "Unknown tentry type");
+    }
+}
+
+static void 
+set_tentry_type(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type)
+{
+    switch (disk->fat_type)
+    {
+        case FATFS_FAT12: set_tentry_type_fat12(disk, entry, type);
+        case FATFS_FAT16: set_tentry_type_fat16(disk, entry, type);
+    }
+}
+
+// -------------------------------------------------------------------------
+// get_tentry_next_cluster()
+// Gets the the next file cluster number from FAT table entry.
+ 
+static __inline__ cyg_uint32 
+get_tentry_next_cluster(fatfs_disk_t *disk, cyg_uint32 entry)
+{
+    return entry;
+}
+
+// -------------------------------------------------------------------------
+// set_tentry_next_cluster()
+// Sets the the next cluster number to FAT table entry.
+ 
+static __inline__ void 
+set_tentry_next_cluster(fatfs_disk_t *disk, 
+                        cyg_uint32   *entry, 
+                        cyg_uint32    next_cluster)
+{
+    *entry = next_cluster;
+}
+
+//==========================================================================
+// FAT cluster functions 
+
+// -------------------------------------------------------------------------
+// is_pos_inside_cluster()
+// Checks if the given position is inside cluster size.
+
+static __inline__ bool 
+is_pos_inside_cluster(fatfs_disk_t *disk, cyg_uint32 pos)
+{
+    return (pos < disk->cluster_size);
+}
+
+// -------------------------------------------------------------------------
+// erase_cluster()
+// Erases cluster (fills with 0x00). 
+
+static int
+erase_cluster(fatfs_disk_t *disk, cyg_uint32 cluster)
+{
+    unsigned char data[32];
+    cyg_uint32 apos;
+    int err, len, i;
+    
+    len = 32;
+    memset((void*)data, 0x00, len);
+    apos = get_data_disk_apos(disk, cluster, 0);
+    
+    CYG_TRACE1(TCL, "cluster=%d", cluster);
+
+    for (i = 0; i < (disk->cluster_size >> 5); i++)
+    {
+        err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, apos);
+        if (err != ENOERR)
+            return err;
+        apos += len;
+    }
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// mark_cluster()
+// Marks cluster (sets the cluster's FAT table entry to given type). 
+
+static int
+mark_cluster(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 type)
+{
+    cyg_uint32 tentry;
+    int err;
+ 
+    set_tentry_type(disk, &tentry, type);
+    err = write_tentry(disk, cluster, &tentry);
+
+    CYG_TRACE3(TCL, "cluster=%d type=%d tentry=%d", 
+                    cluster, type, tentry);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// link_cluster()
+// Links two clusters.
+
+static int
+link_cluster(fatfs_disk_t *disk, cyg_uint32 cluster1, cyg_uint32 cluster2)
+{
+    cyg_uint32 tentry;
+    int err;
+    
+    set_tentry_next_cluster(disk, &tentry, cluster2);
+    err = write_tentry(disk, cluster1, &tentry);
+ 
+    CYG_TRACE3(TCL, "cluster1=%d cluster2=%d tentry=%d", 
+                    cluster1, cluster2, tentry);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// find_next_free_cluster()
+// Finds first free cluster starting from given cluster.
+// If none is available free_cluster is set to 0.
+// If mark_as_last is set the found cluster is marked as LAST.
+
+static int
+find_next_free_cluster(fatfs_disk_t  *disk,
+                       cyg_uint32     start_cluster, 
+                       cyg_uint32    *free_cluster,
+                       cluster_opts_t opts)
+{
+    cyg_uint32 c, tentry;
+    int err;
+
+    if (start_cluster < 2)
+        c = 2;
+    else
+        c = start_cluster + 1;
+
+    *free_cluster = 0;
+
+    CYG_TRACE1(TCL, "c=%d", c);
+   
+    // Search from the starting cluster to the end of FAT and
+    // from start of FAT to the starting cluster 
+    while (c != start_cluster)
+    {
+        // End of FAT check
+        if (c >= disk->fat_tbl_nents)
+        {
+            c = 2;
+            if (c >= start_cluster)
+                break;
+        }
+
+        err = read_tentry(disk, c, &tentry);
+        if (err != ENOERR)
+            return err;
+
+        if (TENTRY_FREE == get_tentry_type(disk, tentry))
+        {
+            CYG_TRACE1(TCL, "free_cluster=%d", c);
+            *free_cluster = c;
+            if (opts & CO_MARK_LAST)
+                err = mark_cluster(disk, c, TENTRY_LAST);
+            if ((err == ENOERR) && (opts & CO_ERASE_NEW))
+                err = erase_cluster(disk, c);
+            return err;
+        }
+        c++;
+    }   
+
+    // No free clusters found
+    CYG_TRACE0(TCL, "!!! no free clusters found");
+ 
+    // Return out of space
+    return ENOSPC;
+}
+
+// -------------------------------------------------------------------------
+// find_and_append_cluster()
+// Finds a free cluster on disk and appends it to the given cluster. 
+// New cluster is marked as LAST. 
+
+static int
+find_and_append_cluster(fatfs_disk_t  *disk, 
+                        cyg_uint32     cluster, 
+                        cyg_uint32    *new_cluster,
+                        cluster_opts_t opts)
+{
+    cyg_uint32 free_cluster;
+    int err;
+
+    CYG_TRACE1(TCL, "cluster=%d", cluster);
+    
+    err = find_next_free_cluster(disk, cluster, 
+            &free_cluster, opts | CO_MARK_LAST);
+    if (err != ENOERR)
+        return err;
+
+    CYG_TRACE1(TCL, "free_cluster=%d", free_cluster);
+    
+    // Link clusters    
+    err = link_cluster(disk, cluster, free_cluster);
+    if (err != ENOERR)
+        return err;
+
+    *new_cluster = free_cluster;
+
+    CYG_TRACE1(TCL, "new_cluster=%d", free_cluster);
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// find_nth_cluster0()
+// Finds nth cluster in chain (ie nth cluster of file).
+// Searching from given position.
+ 
+static int
+find_nth_cluster0(fatfs_disk_t     *disk,
+                  fatfs_data_pos_t *dpos, 
+                  cyg_uint32        n,
+                  fatfs_tcache_t   *tcache)
+{
+    cyg_uint32 cluster, cluster_snum;
+    int err = ENOERR;
+ 
+    // Trivial case check
+    if (dpos->cluster_snum == n)
+        return ENOERR;
+
+    // First look in cache
+    if (NULL != tcache)
+    {
+        cyg_uint32 c, ln;
+        if (fatfs_tcache_get(disk, tcache, n, &c))
+        {
+            // Cluster in cache
+            dpos->cluster = c;
+            dpos->cluster_snum = n;
+            CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d in cache", c, n);
+            return ENOERR;
+        }
+        else if (fatfs_tcache_get_last(disk, tcache, &ln, &c))
+        {
+            // Cluster not in cache - get last
+            // in cache and search from there
+            dpos->cluster = c;
+            dpos->cluster_snum = ln;  
+            CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d last in cache", c, ln);
+        }
+        else
+        {
+            // Empty cache - put first cluster in
+            fatfs_tcache_set(disk, tcache, dpos->cluster_snum, dpos->cluster);
+        }
+    }
+   
+    cluster      = dpos->cluster;
+    cluster_snum = dpos->cluster_snum;
+   
+    CYG_TRACE4(TCL, "cluster=%d cluster_snum=%d n=%d n_to_search=%d",
+                    cluster, cluster_snum, n, n-cluster_snum);
+   
+    // Adjust the number of clusters that should be
+    // walked according to the given position
+    n -= cluster_snum;
+
+    // Walk the cluster chain for n clusters or
+    // until last cluster
+    while (n > 0)
+    {
+        cyg_uint32 tentry;
+
+        err = read_tentry(disk, cluster, &tentry);
+        if (err != ENOERR)
+            return err;
+
+        switch (get_tentry_type(disk, tentry))
+        {
+            case TENTRY_REGULAR:
+                break;
+            case TENTRY_LAST:
+                // Oops early last cluster
+                CYG_TRACE1(TCL, "chain end n=%d", n);
+                err = EEOF; // File has less clusters than given n
+                            // this err should be caught by the 
+                            // calling function 
+                goto out;
+            default:
+                // Inconsistant FAT table state !!!
+                CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x n=%d", 
+                                tentry, n);
+                err = EIO;                 
+                goto out;
+        }
+        cluster = get_tentry_next_cluster(disk, tentry);
+        cluster_snum++;
+        if (NULL != tcache)
+            fatfs_tcache_set(disk, tcache, cluster_snum, cluster);
+        n--;
+    }
+    
+out:
+    dpos->cluster      = cluster;
+    dpos->cluster_snum = cluster_snum;
+
+    CYG_TRACE2(TCL, "nth cluster=%d cluster_snum=%d", cluster, cluster_snum);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// find_nth_cluster()
+// Finds nth cluster in chain (ie nth cluster of file) searching 
+// from given position. If the chain ends one cluster before the 
+// given nth cluster and the CO_EXTEND is specifide, than the 
+// chain is extended by one cluster.
+ 
+static int
+find_nth_cluster(fatfs_disk_t     *disk,
+                 fatfs_data_pos_t *dpos, 
+                 cyg_uint32        n,
+                 fatfs_tcache_t   *tcache,
+                 cluster_opts_t    opts)
+{
+    int err;
+   
+    // Find nth cluster 
+    err = find_nth_cluster0(disk, dpos, n, tcache);    
+
+    // EEOF meens that the cluster chain ended early
+    if ((err != EEOF) || !(opts & CO_EXTEND))
+        return err;
+    
+    CYG_TRACE2(TCL, "cluster_snum=%d n=%d", dpos->cluster_snum, n);
+    
+    // Check if one cluster short
+    if (dpos->cluster_snum == (n - 1))
+    {
+        // Extend the chain for one cluster
+        cyg_uint32 new_cluster;
+
+        // Append new cluster to the end of chain
+        err = find_and_append_cluster(disk, dpos->cluster, 
+                                      &new_cluster, opts);
+        if (err != ENOERR)
+            return err;
+
+        // Update position
+        dpos->cluster = new_cluster;
+        dpos->cluster_snum++;
+        dpos->cluster_pos = 0;
+
+        CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", 
+                        dpos->cluster, dpos->cluster_snum);
+        
+        // Update cache
+        if (NULL != tcache)
+            fatfs_tcache_set(disk, tcache, dpos->cluster_snum, dpos->cluster);
+    }
+    
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// get_next_cluster()
+// Gets next cluster in chain (ie next cluster of file).
+// If CO_EXTEND is specified and the current cluster is last in 
+// chain then the chain is extended by one cluster.
+
+static int
+get_next_cluster(fatfs_disk_t     *disk,
+                 fatfs_data_pos_t *dpos,
+                 fatfs_tcache_t   *tcache,
+                 cluster_opts_t    opts)
+{
+    int err;
+
+    CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", 
+                    dpos->cluster, dpos->cluster_snum);
+
+    err = find_nth_cluster(disk, dpos, dpos->cluster_snum + 1, tcache, opts);
+
+    if (err != ENOERR)
+        return err;
+
+    // Update position
+    dpos->cluster_pos = 0;
+ 
+    CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", 
+                    dpos->cluster, dpos->cluster_snum);
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// get_data_position_from_off()
+// Gets data position from given file offset.
+// If CO_EXTEND is specified the file is extended if 
+// one cluster too short. 
+ 
+static int 
+get_data_position_from_off(fatfs_disk_t     *disk,
+                           cyg_uint32        first_cluster, 
+                           cyg_uint32        offset, 
+                           fatfs_data_pos_t *dpos,
+                           fatfs_tcache_t   *tcache,
+                           cluster_opts_t    opts)
+{
+    cyg_uint32 n;
+    int err;
+
+    // Position inside the cluster
+    dpos->cluster_pos = offset & (disk->cluster_size - 1);
+
+    // Cluster seq number to be searched for
+    n = offset >> disk->cluster_size_log2;
+
+    // Start searching from first cluster
+    dpos->cluster      = first_cluster;
+    dpos->cluster_snum = 0;
+
+    CYG_TRACE4(TCL, "off=%d first_cluster=%d cluster_pos=%d n=%d\n",
+                    offset, first_cluster, dpos->cluster_pos, n);
+
+    err = find_nth_cluster(disk, dpos, n, tcache, opts);
+
+    // Err could be EEOF wich means that the given 
+    // offset if out of given file (cluster chain)
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// free_cluster_chain()
+// Marks all clusters FREE from given cluster to the last cluster in chain.
+
+static int
+free_cluster_chain(fatfs_disk_t *disk, cyg_uint32 start_cluster)
+{
+    cyg_uint32 c, next_c, tentry;
+    bool last;
+    int err;
+
+    CYG_TRACE1(TCL, "start_cluster=%d", start_cluster);
+
+    c = next_c = start_cluster;
+    last = false;
+    while (!last)
+    {
+        err = read_tentry(disk, c, &tentry);
+        if (err != ENOERR)
+            return err;
+
+        switch (get_tentry_type(disk, tentry))
+        {
+            case TENTRY_LAST:
+                // Last cluster in chain
+                last = true;
+                break;
+            case TENTRY_REGULAR:
+                // Get next cluster in chain
+                next_c = get_tentry_next_cluster(disk, tentry);
+                break;
+            default:
+                CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x c=%d", 
+                                tentry, c);
+                return EIO;
+        }
+
+        // Set the current tentry to FREE 
+        set_tentry_type(disk, &tentry, TENTRY_FREE);
+        err = write_tentry(disk, c, &tentry);
+        if (err != ENOERR)
+            return err;
+
+        // Next cluster in chain
+        c = next_c; 
+    }
+
+    CYG_TRACE1(TCL, "last_cluster=%d", c);
+    
+    return ENOERR;
+}
+
+//==========================================================================
+// FAT dir entry functions 
+
+// -------------------------------------------------------------------------
+// print_dentry()
+// Prints FAT directory entry. 
+
+#if TDE
+static void
+print_dentry(fat_dir_entry_t* fde)
+{
+    if (DENTRY_IS_DELETED(fde))
+        diag_printf("FAT: FDE name:    '?%.7s'\n", &fde->name[1]);
+    else    
+        diag_printf("FAT: FDE name:    '%.8s'\n", fde->name);
+    diag_printf("FAT: FDE ext:     '%.3s'\n", fde->ext);
+    diag_printf("FAT: FDE attr:     %c%c%c%c%c%c\n", 
+                (DENTRY_IS_RDONLY(fde)  ? 'R' : '-'),
+                (DENTRY_IS_HIDDEN(fde)  ? 'H' : '-'),
+                (DENTRY_IS_SYSTEM(fde)  ? 'S' : '-'),
+                (DENTRY_IS_VOLUME(fde)  ? 'V' : '-'),
+                (DENTRY_IS_DIR(fde)     ? 'D' : '-'),
+                (DENTRY_IS_ARCHIVE(fde) ? 'A' : '-'));
+    diag_printf("FAT: FDE crt time: %u\n", fde->crt_time);
+    diag_printf("FAT: FDE crt date: %u\n", fde->crt_date);
+    diag_printf("FAT: FDE acc date: %u\n", fde->acc_date);
+    diag_printf("FAT: FDE wrt time: %u\n", fde->wrt_time);
+    diag_printf("FAT: FDE wrt date: %u\n", fde->wrt_date);
+    diag_printf("FAT: FDE cluster:  %u\n", fde->cluster);
+    diag_printf("FAT: FDE size:     %u\n", fde->size);
+}
+#endif // TDE
+
+// -------------------------------------------------------------------------
+// read_dentry()
+// Reads dir entry from disk. 
+// If cluster is 0 reads from root dir.
+ 
+static int
+read_dentry(fatfs_disk_t     *disk,
+            fatfs_data_pos_t *dpos, 
+            fat_dir_entry_t  *fde)
+{
+    unsigned char data[DENTRY_SIZE];
+    cyg_uint32 apos;
+    int len, err;
+   
+    // Check if we are reading the root directory
+    if (0 == dpos->cluster)
+        apos = disk->fat_root_dir_pos + dpos->cluster_pos;
+    else
+        apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);    
+   
+    CYG_TRACE3(TDE, "cluster=%d pos=%d apos=%d",
+                    dpos->cluster, dpos->cluster_pos, apos); 
+    
+    len = DENTRY_SIZE;
+    err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, apos);
+    if (err != ENOERR)
+        return err;
+
+    GET_BYTES(data, fde->name,     8, 0x00);
+    GET_BYTES(data, fde->ext,      3, 0x08);
+    GET_BYTE(data,  fde->attr,        0x0B);
+    GET_BYTE(data,  fde->nt_reserved, 0x0C);
+    GET_BYTE(data,  fde->crt_sec_100, 0x0D);
+    GET_WORD(data,  fde->crt_time,    0x0E);
+    GET_WORD(data,  fde->crt_date,    0x10);
+    GET_WORD(data,  fde->acc_date,    0x12);
+    GET_WORD(data,  fde->cluster_HI,  0x14);
+    GET_WORD(data,  fde->wrt_time,    0x16);
+    GET_WORD(data,  fde->wrt_date,    0x18);
+    GET_WORD(data,  fde->cluster,     0x1A);
+    GET_DWORD(data, fde->size,        0x1C);
+
+     // Zero terminate strings
+    fde->name[8] = '\0';    
+    fde->ext[3]  = '\0';    
+  
+    // Store position
+    fde->pos = *dpos;
+       
+#if TDE    
+    print_dentry(fde);
+#endif
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// write_dentry()
+// Writes dir entry to disk. 
+// If cluster is 0 writes to root dir.
+ 
+static int
+write_dentry(fatfs_disk_t     *disk,
+             fatfs_data_pos_t *dpos, 
+             fat_dir_entry_t  *fde)
+{
+    unsigned char data[DENTRY_SIZE];
+    cyg_uint32 apos;
+    int len, err;
+    
+    // Check if we are writting to the root directory
+    if (0 == dpos->cluster)
+        apos = disk->fat_root_dir_pos + dpos->cluster_pos;
+    else
+        apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);    
+ 
+    CYG_TRACE3(TDE, "cluster=%d pos=%d apos=%d",
+                    dpos->cluster, dpos->cluster_pos, apos); 
+
+    SET_BYTES(data, fde->name,     8, 0x00);
+    SET_BYTES(data, fde->ext,      3, 0x08);
+    SET_BYTE(data,  fde->attr,        0x0B);
+    SET_BYTE(data,  fde->nt_reserved, 0x0C);
+    SET_BYTE(data,  fde->crt_sec_100, 0x0D);
+    SET_WORD(data,  fde->crt_time,    0x0E);
+    SET_WORD(data,  fde->crt_date,    0x10);
+    SET_WORD(data,  fde->acc_date,    0x12);
+    SET_WORD(data,  fde->cluster_HI,  0x14);
+    SET_WORD(data,  fde->wrt_time,    0x16);
+    SET_WORD(data,  fde->wrt_date,    0x18);
+    SET_WORD(data,  fde->cluster,     0x1A);
+    SET_DWORD(data, fde->size,        0x1C);
+   
+    len = DENTRY_SIZE;
+    err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, apos);
+    if (err != ENOERR)
+        return err;
+
+#if TDE    
+    print_dentry(fde);
+#endif
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// dentry_set_deleted()
+// Sets the dentry filename first char to 0xE5 (ie deleted). 
+ 
+static __inline__ void 
+dentry_set_deleted(fatfs_disk_t *disk, fat_dir_entry_t *dentry)
+{
+    dentry->name[0] = 0xE5;
+}
+
+// -------------------------------------------------------------------------
+// get_dentry_filename()
+// Gets the filename from given dir entry. 
+ 
+static void 
+get_dentry_filename(fat_dir_entry_t *fde, char *name)
+{
+    int i = 0;
+    char *cptr  = fde->name;
+    char *cname = name;
+
+    while (*cptr != ' ' && i < 8)
+    {
+        *cname++ = *cptr++; i++;
+    }
+    cptr = fde->ext;
+
+    if (*cptr != ' ')
+    {
+        *cname++ = '.'; i = 0;
+        while (*cptr != ' ' && i < 3)
+        {
+            *cname++ = *cptr++; i++;
+        }
+    }
+    *cname = '\0';
+
+    CYG_TRACE3(TDE, "dos_name='%s' dos_ext='%s' filename='%s'",
+                    fde->name, fde->ext, name);
+}
+
+// -------------------------------------------------------------------------
+// set_dentry_filename()
+// Sets the filename to given dir entry. 
+ 
+static void 
+set_dentry_filename(fat_dir_entry_t *fde, const char *name, int namelen)
+{
+    int i, nidx;
+    const char *cname;
+    char *cptr;
+
+    // Special case check
+    if (name[0] == '.')
+    {
+        if (name[1] == '\0')
+        {
+            strcpy(fde->name, ".       ");
+            strcpy(fde->ext,  "   ");
+            return;
+        }
+        else if (name[1] == '.' && name[2] == '\0')
+        {
+            strcpy(fde->name, "..      ");
+            strcpy(fde->ext,  "   ");
+            return;
+        }
+    }
+    
+    if (0 == namelen)
+        namelen = 9999;
+    
+    nidx  = 0;
+    cname = name;
+    cptr  = fde->name;
+    for (i = 0; i < 8; i++)
+    {
+        if (*cname != '.' && *cname != '\0' && nidx++ < namelen)
+            *cptr++ = toupper(*cname++);
+        else
+            *cptr++ = ' ';
+    }
+    *cptr = '\0';
+    
+    while (*cname != '.' && *cname != '\0' && nidx++ < namelen)
+        cname++;
+   
+    if (*cname == '.' && nidx++ < namelen) 
+        cname++;
+    
+    cptr = fde->ext;
+    for (i = 0; i < 3; i++)
+    {
+        if (*cname != '.' && *cname != '\0' && nidx++ < namelen)
+            *cptr++ = toupper(*cname++);
+        else
+            *cptr++ = ' ';
+    }
+    *cptr = '\0';
+
+    CYG_TRACE4(TDE, "filename='%s' namelen=%d dos_name='%s' dos_ext='%s'", 
+                    name, namelen, fde->name, fde->ext);
+}
+
+// -------------------------------------------------------------------------
+// get_next_dentry()
+// Gets next dir entry searching from given position to the end.
+// Position is expected in DENTRY_SIZE units.
+// If EEOF is returned there are no more extries in given dir.
+ 
+static int
+get_next_dentry(fatfs_disk_t    *disk,
+                fatfs_node_t    *dir,
+                cyg_uint32      *pos,
+                fat_dir_entry_t *dentry)
+{
+    fatfs_data_pos_t dpos;
+    cyg_uint32 off;
+    int err = ENOERR;
+
+    // Calculate dentry offset
+    off = *pos * DENTRY_SIZE;
+    
+    CYG_TRACE4(TDE, "pos=%d off=%d dir=%p cluster=%d",
+                    off, *pos, dir, dir->cluster);
+
+    // Root dir check
+    if (0 == dir->cluster)
+    {
+        dpos.cluster      = 0;
+        dpos.cluster_snum = 0;
+        dpos.cluster_pos  = off;
+    }
+    else
+    {
+        err = get_data_position_from_off(disk, dir->cluster, 
+            off, &dpos, &dir->tcache, false);
+        if (err != ENOERR)
+            return err;
+    }
+
+    while (true)
+    {
+        // Root dir check
+        if (0 != dir->cluster) 
+        {
+            // Change cluster if needed
+            if (!is_pos_inside_cluster(disk, dpos.cluster_pos))
+                err = get_next_cluster(disk, &dpos, &dir->tcache, CO_NONE);
+        }
+        else
+        {
+            if (*pos >= disk->fat_root_dir_nents)
+                err = EEOF;
+        }
+
+        if (err != ENOERR)
+            break;
+
+        err = read_dentry(disk, &dpos, dentry);
+        if (err != ENOERR)
+            return err;
+
+        if (DENTRY_IS_ZERO(dentry))
+        {
+            // If we get a ZERO dir entry, we assume that
+            // there are no more entries in current dir
+            CYG_TRACE0(TDE, "ZERO dentry"); 
+            err = EEOF;
+            break;
+        }
+        else if (!DENTRY_IS_DELETED(dentry))
+        {
+            // Dir entry found
+            CYG_TRACE3(TDE, "dentry_pos=%d cluster=%d pos=%d",
+                            *pos, dpos.cluster, dpos.cluster_pos);
+            break;
+        }
+
+        // Increment offset and position
+        dpos.cluster_pos += DENTRY_SIZE;
+        (*pos)++;
+    }
+
+    if (EEOF == err) CYG_TRACE0(TDE, "end of dir");
+            
+    // EEOF could be returned if there are no more entries in this
+    // dir - this should be cought by the calling function 
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// get_free_dentry()
+// Gets free dir entry slot searching from given position.
+// If an deleated entry is found, its clusters are freed and the
+// entry is reused. 
+// The directory is extended if needed.
+
+static int
+get_free_dentry(fatfs_disk_t     *disk, 
+                fatfs_data_pos_t *dpos,
+                fatfs_tcache_t   *tcache)
+{
+    fat_dir_entry_t  dentry;
+    fatfs_data_pos_t cdpos;
+    int err = ENOERR;
+    
+    CYG_TRACE3(TDE, "cluster=%d cluster_snum=%d cluster_pos=%d", 
+                    dpos->cluster, dpos->cluster_snum, dpos->cluster_pos);
+    cdpos = *dpos;
+
+    while (true)
+    {
+        // Root dir check
+        if (0 != cdpos.cluster) 
+        {
+            // Change cluster if needed
+            if (!is_pos_inside_cluster(disk, cdpos.cluster_pos))
+                err = get_next_cluster(disk, &cdpos, tcache, 
+                                       CO_EXTEND | CO_ERASE_NEW);
+        }
+        else
+        {
+            if (cdpos.cluster_pos >= disk->fat_root_dir_size)
+                err = ENOSPC;
+        }
+
+        if (err != ENOERR)
+            return err;
+
+        err = read_dentry(disk, &cdpos, &dentry);
+        if (err != ENOERR)
+            return err;
+
+        if (DENTRY_IS_DELETED(&dentry))
+        {
+            CYG_TRACE3(TDE, "deleted dentry at cluster=%d cluster_snum=%d "
+                            "cluster_pos=%d", cdpos.cluster, cdpos.cluster_snum,
+                            cdpos.cluster_pos);
+
+            // Retrun found dentry position
+            *dpos = cdpos;
+            return ENOERR;
+        }
+        else if (DENTRY_IS_ZERO(&dentry))
+        {
+            CYG_TRACE3(TDE, "zero dentry at cluster=%d cluster_snum=%d "
+                            "cluster_pos=%d", cdpos.cluster, cdpos.cluster_snum,
+                            cdpos.cluster_pos);
+
+            // Retrun found dentry position
+            *dpos = cdpos;
+            return ENOERR;  
+        }
+       
+        // Increment current position
+        cdpos.cluster_pos += DENTRY_SIZE;
+    }
+}
+ 
+// -------------------------------------------------------------------------
+// dentry_to_node()
+// Converts FAT dir entry to node. 
+ 
+static void
+dentry_to_node(fat_dir_entry_t *dentry, 
+               fatfs_node_t    *node)
+{
+    get_dentry_filename(dentry, node->filename);
+
+    if (DENTRY_IS_DIR(dentry))
+        node->mode = __stat_mode_DIR;
+    else
+        node->mode = __stat_mode_REG;
+    
+    date_dos2unix(dentry->crt_time, dentry->crt_date, &node->ctime);
+    date_dos2unix(0,                dentry->acc_date, &node->atime);
+    date_dos2unix(dentry->wrt_time, dentry->wrt_date, &node->mtime);
+    
+    node->size       = dentry->size;
+    node->priv_data  = dentry->nt_reserved;
+    node->cluster    = dentry->cluster;
+    node->dentry_pos = dentry->pos;
+}
+
+// -------------------------------------------------------------------------
+// node_to_dentry()
+// Converts node to FAT dir entry. 
+ 
+static void
+node_to_dentry(fatfs_node_t *node, fat_dir_entry_t *dentry)
+{
+    set_dentry_filename(dentry, node->filename, 0);
+
+    if (node->mode == __stat_mode_DIR)
+        dentry->attr = DENTRY_ATTR_DIR;
+    else
+        dentry->attr = DENTRY_ATTR_ARCHIVE;
+        
+    date_unix2dos(node->ctime, &dentry->crt_time, &dentry->crt_date);
+    date_unix2dos(node->atime, NULL,              &dentry->acc_date);
+    date_unix2dos(node->mtime, &dentry->wrt_time, &dentry->wrt_date);
+    
+    dentry->crt_sec_100 = 0; //FIXME
+    dentry->size        = node->size;
+    dentry->nt_reserved = node->priv_data;
+    dentry->cluster     = node->cluster;
+    dentry->cluster_HI  = 0;
+    dentry->pos         = node->dentry_pos;
+}
+
+//==========================================================================
+// FAT data functions 
+
+// -------------------------------------------------------------------------
+// read_data()
+// Reads data from given position. 
+ 
+static int
+read_data(fatfs_disk_t     *disk,
+          void             *data,
+          cyg_uint32       *len,
+          fatfs_data_pos_t *dpos,
+          fatfs_tcache_t   *tcache)
+{
+    unsigned char *buf;
+    cyg_uint32 apos;
+    cyg_uint32 size;
+    int err;
+
+    // Initialize variables and get the absolute starting pos on disk
+    buf   = (unsigned char *)data;
+    size  = *len;
+    apos  = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);
+    err   = ENOERR;
+  
+    CYG_TRACE5(TDO, "len=%d cluster=%d cluster_pos=%d "
+                    "cluster_snum=%d apos=%d", *len, dpos->cluster, 
+                    dpos->cluster_pos, dpos->cluster_snum, apos);
+
+    while (size > 0)
+    {
+        cyg_uint32 csize;
+
+        // Check if we are still inside current cluster
+        if (!is_pos_inside_cluster(disk, dpos->cluster_pos))
+        {
+            // Get next cluster of file and adjust absolute disk position 
+            err = get_next_cluster(disk, dpos, tcache, CO_NONE);
+            if (err != ENOERR)
+                goto out;
+            apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);
+        }
+        
+        // Adjust the data chunk size to be read to the cluster boundary
+        if (size > (disk->cluster_size - dpos->cluster_pos))
+            csize = disk->cluster_size - dpos->cluster_pos;
+        else
+            csize = size;
+
+        CYG_TRACE5(TDO, "-- len=%d cluster=%d cluster_pos=%d "
+                        "cluster_snum=%d apos=%d", csize, dpos->cluster,
+                        dpos->cluster_pos, dpos->cluster_snum, apos);
+
+        // Read data chunk 
+        err = cyg_blib_read(&disk->blib, (void*)buf, &csize, 0, apos);
+
+        if (err != ENOERR)
+            goto out;
+
+        // Adjust running variables
+        buf               += csize;
+        dpos->cluster_pos += csize;
+        apos              += csize;
+        size              -= csize;    
+    }
+    
+out:
+    *len -= size;
+
+    CYG_TRACE1(TDO, "total len=%d", *len);
+
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// write_data()
+// Writes data to given position. 
+ 
+static int
+write_data(fatfs_disk_t     *disk,
+           void             *data,
+           cyg_uint32       *len,
+           fatfs_data_pos_t *dpos,
+           fatfs_tcache_t   *tcache)
+{
+    unsigned char *buf;
+    cyg_uint32 apos;
+    cyg_uint32 size;
+    int err;
+
+    // Initialize variables and get the absolute starting pos on disk
+    buf   = (unsigned char *)data;
+    size  = *len;
+    apos  = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);
+    err   = ENOERR;
+  
+    CYG_TRACE5(TDO, "len=%d cluster=%d cluster_pos=%d "
+                    "cluster_snum=%d apos=%d", *len, dpos->cluster, 
+                    dpos->cluster_pos, dpos->cluster_snum, apos);
+
+    while (size > 0)
+    {
+        cyg_uint32 csize;
+
+        // Check if we are still inside current cluster
+        if (!is_pos_inside_cluster(disk, dpos->cluster_pos))
+        {
+            // Get next cluster of file and adjust absolute disk position,
+            // if at the last cluster try to extend the cluster chain 
+            err = get_next_cluster(disk, dpos, tcache, CO_EXTEND);
+            if (err != ENOERR)
+                goto out;
+            apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos);
+        }
+        
+        // Adjust the data chunk size to be read to the cluster boundary
+        if (size > (disk->cluster_size - dpos->cluster_pos))
+            csize = disk->cluster_size - dpos->cluster_pos;
+        else
+            csize = size;
+ 
+        CYG_TRACE5(TDO, "-- len=%d cluster=%d cluster_pos=%d "
+                        "cluster_snum=%d apos=%d", csize, dpos->cluster,
+                        dpos->cluster_pos, dpos->cluster_snum, apos);
+
+        // Write data chunk 
+        err = cyg_blib_write(&disk->blib, (void*)buf, &csize, 0, apos);
+
+        if (err != ENOERR)
+            goto out;
+
+        // Adjust running variables
+        buf               += csize;
+        dpos->cluster_pos += csize;
+        apos              += csize;
+        size              -= csize;    
+    }
+    
+out:
+    *len -= size;
+
+    CYG_TRACE1(TDO, "total len=%d", *len);
+
+    return err;
+}
+
+//==========================================================================
+// Misc functions 
+
+// -------------------------------------------------------------------------
+// init_node()
+// Initializes file attributes of a new node. 
+ 
+static void
+init_node_fattr(fatfs_node_t     *node, 
+                const char       *name, 
+                int               namelen,
+                mode_t            mode,
+                cyg_uint32        parent_cluster, 
+                fatfs_data_pos_t *dentry_pos)
+{
+    if (namelen == 0)
+        namelen = 12;
+    
+    strncpy(node->filename, name, namelen);
+    node->filename[namelen] = '\0';
+    
+    node->mode  = mode;
+    node->ctime = 
+    node->atime =
+    node->mtime = cyg_timestamp();
+
+    node->priv_data      = 0;
+    node->size           = 0;
+    node->cluster        = 0;
+    node->parent_cluster = parent_cluster;
+    node->dentry_pos     = *dentry_pos;
+}
+
+// -------------------------------------------------------------------------
+// is_node_root_node()
+// Check if the given node is the root dir node 
+ 
+static __inline__ bool
+is_node_root_node(fatfs_node_t *node)
+{
+    return (node->filename[0] == '\0' && node->cluster == 0);
+}
+
+//==========================================================================
+//==========================================================================
+// Exported functions 
+
+// -------------------------------------------------------------------------
+// fatfs_get_disk_info()
+// Gets disk info. 
+ 
+int
+fatfs_get_disk_info(fatfs_disk_t *disk)
+{
+    int err;
+    cyg_uint32 sec_num, root_dir_sec_num;
+    cyg_uint32 data_sec_num, data_clu_num;
+    fat_boot_record_t boot_rec;
+
+    CYG_CHECK_DATA_PTRC(disk);
+ 
+    err = read_boot_record(disk, &boot_rec);
+    if (err != ENOERR)    
+        return err;
+
+    // Check some known boot record values
+    if (0x29 != boot_rec.ext_sig       ||
+        0x55 != boot_rec.exe_marker[0] ||
+        0xAA != boot_rec.exe_marker[1])
+        return EINVAL;
+
+    // Determine number of sectors
+    if (boot_rec.sec_num_32 != 0)
+        sec_num = boot_rec.sec_num_32;
+    else
+        sec_num = boot_rec.sec_num;
+    
+    // Number of sectors used by root directory 
+    root_dir_sec_num = ((boot_rec.max_root_dents * DENTRY_SIZE) +
+                        (boot_rec.bytes_per_sec - 1)) / boot_rec.bytes_per_sec;
+        
+    // Number of data sectors
+    data_sec_num = sec_num - (boot_rec.res_sec_num + 
+        (boot_rec.fat_tbls_num * boot_rec.sec_per_fat) + root_dir_sec_num);
+    
+    // Number of data clusters
+    data_clu_num = data_sec_num / boot_rec.sec_per_clust;
+
+    // Determine the type of FAT based on number of data clusters
+    if (data_clu_num < 4085) 
+        disk->fat_type = FATFS_FAT12;
+    else if (data_clu_num < 65525) 
+        disk->fat_type = FATFS_FAT16;
+    else // FAT32
+        return EINVAL;
+    
+    // Sector and cluster sizes 
+    disk->sector_size       = boot_rec.bytes_per_sec;
+    disk->sector_size_log2  = get_val_log2(disk->sector_size);
+    disk->cluster_size      = boot_rec.bytes_per_sec * boot_rec.sec_per_clust;
+    disk->cluster_size_log2 = get_val_log2(disk->cluster_size);
+
+    // Sector and cluster size should always be a power of 2
+    if (0 == disk->sector_size_log2 || 0 == disk->cluster_size_log2)
+        return EINVAL;
+
+    // FAT table and root dir sizes and position
+    disk->fat_tbl_pos        = boot_rec.bytes_per_sec * boot_rec.res_sec_num;
+    disk->fat_tbl_size       = boot_rec.bytes_per_sec * boot_rec.sec_per_fat;   
+    disk->fat_tbl_nents      = data_clu_num + 2;
+    disk->fat_tbls_num       = boot_rec.fat_tbls_num;
+    disk->fat_root_dir_pos   = disk->fat_tbl_pos + 
+                               disk->fat_tbls_num * disk->fat_tbl_size;
+    disk->fat_root_dir_size  = boot_rec.max_root_dents * DENTRY_SIZE;
+    disk->fat_root_dir_nents = boot_rec.max_root_dents;
+    disk->fat_data_pos       = disk->fat_root_dir_pos + disk->fat_root_dir_size;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_get_root_node()
+// Gets root dir node. 
+
+void
+fatfs_get_root_node(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    
+    node->mode           = __stat_mode_DIR;
+    node->size           = disk->fat_root_dir_size;
+    node->ctime          = 0;
+    node->atime          = 0;
+    node->mtime          = 0;
+    node->filename[0]    = '\0';
+    node->cluster        = 0;
+    node->parent_cluster = 0;
+
+    node->dentry_pos.cluster      = 0;
+    node->dentry_pos.cluster_snum = 0;
+    node->dentry_pos.cluster_pos  = 0;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_is_node_root_node()
+// Check if the given node is root dir node. 
+
+bool
+fatfs_is_node_root_node(fatfs_node_t *node)
+{
+    return(is_node_root_node(node));
+}
+
+// -------------------------------------------------------------------------
+// fatfs_get_dir_entry_node()
+// Gets dir entry node at given position.
+// If there is no dir entry at given position the next closest
+// dir entry and its position are returned. 
+// If EEOF error is returned, then there are no more dir
+// entries in given dir. 
+
+int
+fatfs_get_dir_entry_node(fatfs_disk_t *disk,
+                         fatfs_node_t *dir,
+                         cyg_uint32   *pos,
+                         fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(dir);
+    CYG_CHECK_DATA_PTRC(pos);
+    CYG_CHECK_DATA_PTRC(node);
+ 
+    err = get_next_dentry(disk, dir, pos, &dentry);
+  
+    if (err != ENOERR)
+       return err;
+
+    dentry_to_node(&dentry, node);
+    node->parent_cluster = dir->cluster;
+
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_write_node()
+// Writes node attributes to its dir entry (to disk).
+ 
+int
+fatfs_write_file_attr(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+ 
+    node_to_dentry(node, &dentry);
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_delete_file()
+// Marks file dir entry as deleted.
+ 
+int
+fatfs_delete_file(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+
+    // Can't delete root
+    if (is_node_root_node(node))
+        return EINVAL;
+    
+    CYG_TRACE1(TDE, "filename='%s'", node->filename);
+
+    node_to_dentry(node, &dentry);
+
+    // Check if we are about to delete a directory
+    if (DENTRY_IS_DIR(&dentry))
+    {
+        fat_dir_entry_t cdentry;
+        cyg_uint32 pos = 0;
+        int i = 0;
+        
+        CYG_TRACE0(TDE, "got directory");
+        // Count number of entries in this dir    
+        while (true)
+        {
+            err = get_next_dentry(disk, node, &pos, &cdentry);
+
+            if (EEOF == err)
+                break;
+            else if (err != ENOERR)
+                return err;
+            i++; pos++;
+        }
+        CYG_TRACE1(TDE, "child count=%d", i);
+        
+        // Check if the dir is empty (except '.' and '..')
+        if (i > 2)
+            return EPERM; 
+    }    
+    
+    // Free file clusters
+    free_cluster_chain(disk, dentry.cluster);
+    dentry_set_deleted(disk, &dentry);
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    return err;
+} 
+
+// -------------------------------------------------------------------------
+// fatfs_create_file()
+// Creates a new file.
+ 
+int
+fatfs_create_file(fatfs_disk_t *disk, 
+                  fatfs_node_t *dir, 
+                  const char   *name,
+                  int           namelen,
+                  fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(dir);
+    CYG_CHECK_DATA_PTRC(name);
+    CYG_CHECK_DATA_PTRC(node);
+ 
+    CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename);
+
+    dentry.pos.cluster      = dir->cluster;
+    dentry.pos.cluster_snum = 0;
+    dentry.pos.cluster_pos  = 0;
+
+    // Get free dir entry in parent dir
+    err = get_free_dentry(disk, &dentry.pos, &dir->tcache);
+    if (err != ENOERR)
+        return err;
+
+    // Set new file attributes
+    init_node_fattr(node, name, namelen, __stat_mode_REG, 
+                    dir->cluster, &dentry.pos); 
+
+    // Write new dir dentry  
+    node_to_dentry(node, &dentry);
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+       return err;
+ 
+    return ENOERR;     
+}
+
+// -------------------------------------------------------------------------
+// fatfs_create_dir()
+// Creates a new directory.
+ 
+int
+fatfs_create_dir(fatfs_disk_t *disk, 
+                 fatfs_node_t *dir, 
+                 const char   *name,
+                 int           namelen,
+                 fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    fatfs_node_t    cnode;
+    cyg_uint32 free_cluster;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(dir);
+    CYG_CHECK_DATA_PTRC(name);
+    CYG_CHECK_DATA_PTRC(node);
+ 
+    CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename);
+
+    // Get free cluster
+    err = find_next_free_cluster(disk, 0, &free_cluster, 
+                                 CO_MARK_LAST | CO_ERASE_NEW);
+    if (err != ENOERR)
+        return err;
+
+    dentry.pos.cluster      = dir->cluster;
+    dentry.pos.cluster_snum = 0;
+    dentry.pos.cluster_pos  = 0;
+
+    // Get free dir entry in parent dir
+    err = get_free_dentry(disk, &dentry.pos, &dir->tcache);
+    if (err != ENOERR)
+        return err;
+
+    // Set new dir attributes
+    init_node_fattr(node, name, namelen, __stat_mode_DIR, 
+                    dir->cluster, &dentry.pos); 
+    node->cluster = free_cluster;
+
+    // Write new dir dentry  
+    node_to_dentry(node, &dentry);
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+       return err;
+
+    // Starting position of new dir entries
+    dentry.pos.cluster      = node->cluster;
+    dentry.pos.cluster_snum = 0;
+    dentry.pos.cluster_pos  = 0;
+
+    CYG_TRACE0(TDE, "Creating '.' entry");
+    // Set '.' dir attributes
+    init_node_fattr(&cnode, ".", 0, __stat_mode_DIR, 
+                    node->cluster, &dentry.pos); 
+    cnode.cluster = node->cluster;
+
+    // Write '.' dentry
+    node_to_dentry(&cnode, &dentry); 
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+        return err;
+          
+    dentry.pos.cluster_pos += DENTRY_SIZE;
+
+    CYG_TRACE0(TDE, "Creating '..' entry");
+    // Set '..' dir attributes
+    init_node_fattr(&cnode, "..", 0, __stat_mode_DIR,
+                    node->cluster, &dentry.pos); 
+    cnode.cluster = dir->cluster;
+    
+    // Write '..' dentry
+    node_to_dentry(&cnode, &dentry); 
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+        return err;
+  
+    return ENOERR;     
+}
+
+// -------------------------------------------------------------------------
+// fatfs_trunc_file()
+// Truncates a file to zero length.
+
+int
+fatfs_trunc_file(fatfs_disk_t *disk, fatfs_node_t *node)
+{
+    fat_dir_entry_t dentry;
+    int err;
+    
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+ 
+    CYG_TRACE1(TDE, "file='%s'", node->filename);
+  
+    // Check for dir
+    if (S_ISDIR(node->mode))
+        return EINVAL; 
+
+    // Trivial case check
+    if (0 == node->size)
+        return ENOERR;
+   
+    // Free cluster chain 
+    err = free_cluster_chain(disk, node->cluster);
+    if (err != ENOERR)
+        return err;
+    
+    // Flush tcache
+    fatfs_tcache_flush(disk, &node->tcache);
+    
+    // Update node attributes
+    node->cluster = 0;
+    node->size    = 0;
+    node->mtime   =
+    node->atime   = cyg_timestamp();
+
+    // Write dentry
+    node_to_dentry(node, &dentry);
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+        return err;
+    
+    return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_rename_file()
+// Renames a file.
+ 
+int
+fatfs_rename_file(fatfs_disk_t *disk, 
+                  fatfs_node_t *dir1, 
+                  fatfs_node_t *node,
+                  fatfs_node_t *dir2, 
+                  const char   *name,
+                  int           namelen)
+{
+    fat_dir_entry_t  dentry;
+    fatfs_data_pos_t dpos;
+    int err;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(dir1);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_CHECK_DATA_PTRC(dir2);
+    CYG_CHECK_DATA_PTRC(name);
+ 
+    // Can't rename root 
+    if (is_node_root_node(node))
+        return EINVAL;
+ 
+    CYG_TRACE5(TDE, "dir1=%p '%s' to dir2=%p '%s' (%d)", 
+                    dir1, node->filename, dir2, name, namelen);
+
+    strncpy(node->filename, name, namelen);
+    node->filename[namelen] = '\0';
+   
+    // Moving around in same dir
+    if (dir1 == dir2)
+    {
+        CYG_TRACE0(TDE, "same dir"); 
+        node_to_dentry(node, &dentry);
+        err = write_dentry(disk, &dentry.pos, &dentry);
+        return err;
+    }
+    
+    CYG_TRACE0(TDE, "different dirs"); 
+    
+    // Moveing around in different dirs
+
+    dpos.cluster      = dir2->cluster;
+    dpos.cluster_snum = 0;
+    dpos.cluster_pos  = 0;
+
+    CYG_TRACE0(TDE, "writting to new dir"); 
+
+    // Get free dir entry in target dir
+    err = get_free_dentry(disk, &dpos, &dir2->tcache);
+    if (err != ENOERR)
+        return err;
+
+    // Write node dentry to new location
+    node_to_dentry(node, &dentry);
+    err = write_dentry(disk, &dpos, &dentry);
+    if (err != ENOERR)
+        return err;
+   
+    CYG_TRACE0(TDE, "deleting from old dir"); 
+    
+    // Deleate dentry at old location
+    dentry_set_deleted(disk, &dentry);
+    dentry.size    = 0;
+    dentry.cluster = 0;
+    err = write_dentry(disk, &dentry.pos, &dentry);
+    if (err != ENOERR)
+        return err;
+   
+    // Set node new position and parent cluster
+    node->dentry_pos     = dpos;
+    node->parent_cluster = dir2->cluster;
+ 
+    // If we moved a directory, we also have to correct the '..' entry  
+    if (__stat_mode_DIR == node->mode)
+    {
+        fat_dir_entry_t cdentry;
+        cyg_uint32 pos = 0;
+        
+        CYG_TRACE0(TDE, "moving directory - correcting '..' entry");
+
+        while (true)
+        {
+            err = get_next_dentry(disk, node, &pos, &cdentry);
+
+            if (EEOF == err)
+                return EIO; // This dir doesn't have the '..' entry,
+                            // that means something is very wrong
+            else if (err != ENOERR)
+                return err;
+
+            if (0 == strncmp("..", cdentry.name, 2))
+            {
+                cdentry.cluster = dir2->cluster;
+                err = write_dentry(disk, &cdentry.pos, &cdentry);
+                if (err != ENOERR)
+                    return err;
+                break;
+            }
+            pos++;
+        }
+    }
+   
+    return ENOERR;     
+}
+
+// -------------------------------------------------------------------------
+// fatfs_read_data()
+// Reads data from disk. 
+ 
+int
+fatfs_read_data(fatfs_disk_t *disk,
+                fatfs_node_t *node,
+                void         *data,
+                cyg_uint32   *len,
+                cyg_uint32    off)
+{
+    int err;
+    fatfs_data_pos_t dpos;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_CHECK_DATA_PTRC(data);
+    CYG_CHECK_DATA_PTRC(len);
+ 
+    err = get_data_position_from_off(disk, node->cluster, off, 
+                                     &dpos, &node->tcache, CO_NONE);
+    if (err != ENOERR)
+        return err;
+
+    err = read_data(disk, data, len, &dpos, &node->tcache);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_write_data()
+// Writes data to disk. 
+ 
+int
+fatfs_write_data(fatfs_disk_t *disk,
+                 fatfs_node_t *node,
+                 void         *data,
+                 cyg_uint32   *len,
+                 cyg_uint32    off)
+{
+    int err;
+    fatfs_data_pos_t dpos;
+
+    CYG_CHECK_DATA_PTRC(disk);
+    CYG_CHECK_DATA_PTRC(node);
+    CYG_CHECK_DATA_PTRC(data);
+    CYG_CHECK_DATA_PTRC(len);
+
+    // Check if this file has a zero size and no first cluster
+    if (0 == node->size && 0 == node->cluster)
+    {
+        cyg_uint32 free_cluster;
+
+        CYG_TRACE0(TDO, "new cluster for zero file");
+        // Get free cluster
+        err = find_next_free_cluster(disk, 0, &free_cluster, CO_MARK_LAST);
+        if (err != ENOERR)
+        {
+            *len = 0;
+            return err;
+        }
+        node->cluster = free_cluster;
+    }
+
+    err = get_data_position_from_off(disk, node->cluster, off, 
+                                     &dpos, &node->tcache, 
+                                     CO_MARK_LAST | CO_EXTEND);
+    if (err != ENOERR)
+    {
+        *len = 0;
+        return err;
+    }
+    
+    err = write_data(disk, data, len, &dpos, &node->tcache);
+    return err;
+}
+
+// -------------------------------------------------------------------------
+// Support routines
+// These enable the definition of local versions of certain routines
+// if the given packages are not present.
+
+#ifndef CYGPKG_LIBC_I18N
+
+__externC int
+toupper( int c )
+{
+    return (('a' <= c) && (c <= 'z')) ? c - 'a' + 'A' : c ;
+}
+
+#endif
+
+#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS
+
+__externC int
+strcasecmp( const char *s1, const char *s2 )
+{
+    int ret;
+    CYG_REPORT_FUNCNAMETYPE( "strcasecmp", "returning %d" );
+    CYG_REPORT_FUNCARG2( "s1=%08x, s2=%08x", s1, s2 );
+
+    CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" );
+    CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" );
+
+    while (*s1 != '\0' && toupper(*s1) == toupper(*s2))
+    {
+        s1++;
+        s2++;
+    }
+
+    ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2);
+    CYG_REPORT_RETVAL( ret );
+    return ret;
+}
+
+__externC int
+strncasecmp( const char *s1, const char *s2, size_t n )
+{
+    int ret;
+    CYG_REPORT_FUNCNAMETYPE( "strncasecmp", "returning %d" );
+    CYG_REPORT_FUNCARG3( "s1=%08x, s2=%08x, n=%d", s1, s2, n );
+
+    if (n == 0)
+    {
+        CYG_REPORT_RETVAL(0);
+        return 0;
+    }
+
+    CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" );
+    CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" );
+
+    while (n-- != 0 && toupper(*s1) == toupper(*s2))
+    {
+        if (n == 0 || *s1 == '\0' || *s2 == '\0')
+            break;
+        s1++;
+        s2++;
+    }
+
+    ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2);
+    CYG_REPORT_RETVAL( ret );
+    return ret;
+}
+
+#endif
+
+// -------------------------------------------------------------------------
+// EOF fatfs_supp.c
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/src/fatfs_tcache.c
@@ -0,0 +1,284 @@
+//==========================================================================
+//
+//      fatfs_tcache.c
+//
+//      FAT file system FAT table cache functions
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           savin 
+// Date:                2003-06-27
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/fs_fat.h>
+#include <pkgconf/infra.h>
+#include <cyg/kernel/kapi.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+#include <sys/types.h>
+
+#include "fatfs.h"
+
+//==========================================================================
+// Defines & macros 
+
+#define TC_INC FATFS_FAT_TABLE_CACHE_INCREMENT
+
+#ifdef CYGDBG_USE_ASSERTS
+# define USE_ASSERTS 1
+#endif
+
+#ifdef FATFS_TRACE_FAT_TABLE_CACHE
+# define TTC 1
+#else
+# define TTC 0
+#endif
+
+//==========================================================================
+// Private functions 
+
+//--------------------------------------------------------------------------
+// tcache_increment()
+// Allocates or reallocates memory for FAT table cache.
+// Returns true is allocation succeded. 
+
+static bool 
+tcache_increment(fatfs_disk_t *disk, fatfs_tcache_t *tcache)
+{
+    CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size);
+
+    if (NULL == tcache->clusters)
+    {
+        tcache->clusters = 
+            (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h, 
+                                                    TC_INC*sizeof(cyg_uint32));
+
+        if (NULL == tcache->clusters)
+            return false;
+
+        tcache->max_size = TC_INC;
+    }
+    else
+    {
+        cyg_uint32 *newc;
+
+        newc = (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h,
+            (tcache->max_size+TC_INC)*sizeof(cyg_uint32));
+
+        if (NULL == newc)
+            return false;
+
+        memcpy(newc, tcache->clusters, (tcache->max_size*sizeof(cyg_uint32)));
+        cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters);
+        
+        tcache->clusters = newc;
+        tcache->max_size += TC_INC;
+    }
+
+    CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size);
+
+    return true;
+}
+
+//==========================================================================
+// Exported functions 
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_create()
+// Creates FAT table caches memory pool.
+
+int  
+fatfs_tcache_create(fatfs_disk_t *disk, cyg_uint32 mem_size)
+{
+    disk->tcache_mem = (cyg_uint8 *)malloc(mem_size);
+    if (NULL == disk->tcache_mem)
+        return ENOMEM;
+    
+    cyg_mempool_var_create(disk->tcache_mem, mem_size, 
+                           &disk->tcache_mpool_h, &disk->tcache_mpool);
+    return ENOERR;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_delete()
+// Deletes FAT table caches memory pool.
+
+void 
+fatfs_tcache_delete(fatfs_disk_t *disk)
+{
+    cyg_mempool_var_delete(disk->tcache_mpool_h);
+    free(disk->tcache_mem);
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_init()
+// Initializes FAT table cache structure.
+
+void
+fatfs_tcache_init(fatfs_disk_t *disk, fatfs_tcache_t *tcache)
+{
+    CYG_CHECK_DATA_PTRC(tcache);
+
+    tcache->clusters = NULL;
+    tcache->size     = 0;
+    tcache->max_size = 0;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_flush()
+// Frees given tcache.
+
+void
+fatfs_tcache_flush(fatfs_disk_t *disk, fatfs_tcache_t *tcache)
+{
+    CYG_CHECK_DATA_PTRC(tcache);
+
+    if (tcache->clusters != NULL)
+    {
+        cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters);
+        tcache->clusters = NULL;    
+        tcache->size     = 0;
+        tcache->max_size = 0;
+    }
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_get()
+// Gets the cluster from cache at given position.
+// Returns true if cluster in cache. 
+
+bool
+fatfs_tcache_get(fatfs_disk_t   *disk,
+                 fatfs_tcache_t *tcache, 
+                 cyg_uint32      num, 
+                 cyg_uint32     *cluster)
+{
+    CYG_CHECK_DATA_PTRC(tcache);
+    CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size);
+    
+    // Check if requested cluster is cached
+    if (num >= tcache->size)
+    {
+        CYG_TRACE1(TTC, "cluster num=%d not in tcache", num);
+        return false;
+    }
+    
+    *cluster = tcache->clusters[num];  
+    CYG_TRACE2(TTC, "got cluster=%d num=%d from tcache", *cluster, num);
+    return true;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_get()
+// Gets last cluster in cache.
+// Returns false if cache is empty.
+
+bool
+fatfs_tcache_get_last(fatfs_disk_t   *disk,
+                      fatfs_tcache_t *tcache, 
+                      cyg_uint32     *num, 
+                      cyg_uint32     *cluster)
+{
+    CYG_CHECK_DATA_PTRC(tcache);
+    CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size);
+    
+    // Check if we have something in cache
+    if (tcache->size == 0)
+        return false;
+   
+    *num = tcache->size - 1; 
+    *cluster = tcache->clusters[*num];  
+    CYG_TRACE2(TTC, "got last cluster=%d num=%d from tcache", *cluster, *num);
+    return true;
+}
+
+//--------------------------------------------------------------------------
+// fatfs_tcache_set()
+// Sets given cluster in cache at given position.
+// There can't be any holes in cache (ie in order to set cluster
+// at position N all clusters from 0 to N-1 must be set before)
+// Returns true if set succeded.
+
+bool
+fatfs_tcache_set(fatfs_disk_t   *disk,
+                 fatfs_tcache_t *tcache, 
+                 cyg_uint32      num, 
+                 cyg_uint32      cluster)
+{
+    CYG_CHECK_DATA_PTRC(tcache);
+    CYG_TRACE4(TTC, "num=%d cluster=%d tcache size=%d max_size=%d", 
+        num, cluster, tcache->size, tcache->max_size);
+
+    if (num > tcache->size)
+    {
+        CYG_TRACE0(TTC, "won't make a hole in tcache");
+        return false;
+    }
+    
+    if (num < tcache->size)
+    {
+        CYG_TRACE2(TTC, "set cluster=%d num=%d in tcache", cluster, num);
+        tcache->clusters[num] = cluster;
+        return true;
+    }
+
+    // Here num is equal to size - check if we need to increment cache 
+    if (tcache->size == tcache->max_size)
+    {
+        // If we can't get memory to increment the cache
+        // try to flush dead nodes fat table cache
+        if (!tcache_increment(disk, tcache))
+        {
+            fatfs_node_flush_dead_tcache(disk); 
+            if (!tcache_increment(disk, tcache))
+                return false;
+        }
+    }
+    
+    tcache->size++;
+    tcache->clusters[num] = cluster;
+
+    CYG_TRACE2(TTC, "added cluster=%d num=%d to tcache", cluster, num);
+
+    return true;
+}
+
+// -------------------------------------------------------------------------
+// EOF fatfs_tcache.c
new file mode 100644
--- /dev/null
+++ b/packages/fs/fat/current/tests/fileio1.c
@@ -0,0 +1,686 @@
+//==========================================================================
+//
+//      fileio1.c
+//
+//      Test fileio system
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):           nickg
+// Contributors:        nickg
+// Date:                2000-05-25
+// Purpose:             Test fileio system
+// Description:         This test uses the testfs to check out the initialization
+//                      and basic operation of the fileio system
+//                      
+//                      
+//                      
+//                      
+//                      
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/hal.h>
+#include <pkgconf/kernel.h>
+#include <pkgconf/io_fileio.h>
+
+#include <cyg/kernel/ktypes.h>         // base kernel types
+#include <cyg/infra/cyg_trac.h>        // tracing macros
+#include <cyg/infra/cyg_ass.h>         // assertion macros
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+
+#include <cyg/fileio/fileio.h>
+
+#include <cyg/infra/testcase.h>
+#include <cyg/infra/diag.h>            // HAL polled output
+
+
+
+//==========================================================================
+
+#define SHOW_RESULT( _fn, _res ) \
+diag_printf("<FAIL>: " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):"");
+
+//==========================================================================
+
+#define IOSIZE  100
+
+#define LONGNAME1       "long_file_name_that_should_take_up_more_than_one_directory_entry_1"
+#define LONGNAME2       "long_file_name_that_should_take_up_more_than_one_directory_entry_2"
+
+
+//==========================================================================
+
+#ifndef CYGPKG_LIBC_STRING
+
+char *strcat( char *s1, const char *s2 )
+{
+    char *s = s1;
+    while( *s1 ) s1++;
+    while( (*s1++ = *s2++) != 0);
+    return s;
+}
+
+#endif
+
+//==========================================================================
+
+static void listdir( char *name, int statp, int numexpected, int *numgot )
+{
+    int err;
+    DIR *dirp;
+    int num=0;
+    
+    diag_printf("<INFO>: reading directory %s\n",name);
+    
+    dirp = opendir( name );
+    if( dirp == NULL ) SHOW_RESULT( opendir, -1 );
+
+    for(;;)
+    {
+        struct dirent *entry = readdir( dirp );
+        
+        if( entry == NULL )
+            break;
+        num++;
+        diag_printf("<INFO>: entry %14s",entry->d_name);
+        if( statp )
+        {
+            char fullname[PATH_MAX];
+            struct stat sbuf;
+
+            if( name[0] )
+            {
+                strcpy(fullname, name );
+                if( !(name[0] == '/' && name[1] == 0 ) )
+                    strcat(fullname, "/" );
+            }
+            else fullname[0] = 0;
+            
+            strcat(fullname, entry->d_name );
+            
+            err = stat( fullname, &sbuf );
+            if( err < 0 )
+            {
+                if( errno == ENOSYS )
+                    diag_printf(" <no status available>");
+                else SHOW_RESULT( stat, err );
+            }
+            else
+            {
+                diag_printf(" [mode %08x ino %08x nlink %d size %d]",
+                            sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,sbuf.st_size);
+            }
+        }
+
+        diag_printf("\n");
+    }
+
+    err = closedir( dirp );
+    if( err < 0 ) SHOW_RESULT( stat, err );
+    if (numexpected >= 0 && num != numexpected)
+        CYG_TEST_FAIL("Wrong number of dir entries\n");
+    if ( numgot != NULL )
+        *numgot = num;
+}
+
+//==========================================================================
+
+static void createfile( char *name, size_t size )
+{
+    char buf[IOSIZE];
+    int fd;
+    ssize_t wrote;
+    int i;
+    int err;
+
+    diag_printf("<INFO>: create file %s size %d\n",name,size);
+
+    err = access( name, F_OK );
+    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
+    
+    for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
+ 
+    fd = open( name, O_WRONLY|O_CREAT );
+    if( fd < 0 ) SHOW_RESULT( open, fd );
+
+    while( size > 0 )
+    {
+        ssize_t len = size;
+        if ( len > IOSIZE ) len = IOSIZE;
+        
+        wrote = write( fd, buf, len );
+        if( wrote != len ) SHOW_RESULT( write, wrote );        
+
+        size -= wrote;
+    }
+
+    err = close( fd );
+    if( err < 0 ) SHOW_RESULT( close, err );
+}
+
+//==========================================================================
+
+static void maxfile( char *name )
+{
+    char buf[IOSIZE];
+    int fd;
+    ssize_t wrote;
+    int i;
+    int err;
+    size_t size = 0;
+    size_t prevsize = 0;
+    
+    diag_printf("<INFO>: create maximal file %s\n",name);
+    diag_printf("<INFO>: This may take a few minutes\n");
+
+    err = access( name, F_OK );
+    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
+    
+    for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256;
+ 
+    fd = open( name, O_WRONLY|O_CREAT );
+    if( fd < 0 ) SHOW_RESULT( open, fd );
+
+    do
+    {
+        wrote = write( fd, buf, IOSIZE );
+        //if( wrote < 0 ) SHOW_RESULT( write, wrote );        
+
+        if( wrote >= 0 )
+            size += wrote;
+
+        if( (size-prevsize) > 100000 )
+        {
+            diag_printf("<INFO>: size = %d \n", size);
+            prevsize = size;
+        }
+        
+    } while( wrote == IOSIZE );
+
+    diag_printf("<INFO>: file size == %d\n",size);
+
+    err = close( fd );
+    if( err < 0 ) SHOW_RESULT( close, err );
+}
+
+//==========================================================================
+
+static void checkfile( char *name )
+{
+    char buf[IOSIZE];
+    int fd;
+    ssize_t done;
+    int i;
+    int err;
+    off_t pos = 0;
+
+    diag_printf("<INFO>: check file %s\n",name);
+    
+    err = access( name, F_OK );
+    if( err != 0 ) SHOW_RESULT( access, err );
+
+    fd = open( name, O_RDONLY );
+    if( fd < 0 ) SHOW_RESULT( open, fd );
+
+    for(;;)
+    {
+        done = read( fd, buf, IOSIZE );
+        if( done < 0 ) SHOW_RESULT( read, done );
+
+        if( done == 0 ) break;
+
+        for( i = 0; i < done; i++ )
+            if( buf[i] != i%256 )
+            {
+                diag_printf("buf[%d+%d](%02x) != %02x\n",pos,i,buf[i],i%256);
+                CYG_TEST_FAIL("Data read not equal to data written\n");
+            }
+        
+        pos += done;
+    }
+
+    err = close( fd );
+    if( err < 0 ) SHOW_RESULT( close, err );
+}
+
+//==========================================================================
+
+static void copyfile( char *name2, char *name1 )
+{
+
+    int err;
+    char buf[IOSIZE];
+    int fd1, fd2;
+    ssize_t done, wrote;
+
+    diag_printf("<INFO>: copy file %s -> %s\n",name2,name1);
+
+    err = access( name1, F_OK );
+    if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err );
+
+    err = access( name2, F_OK );
+    if( err != 0 ) SHOW_RESULT( access, err );
+    
+    fd1 = open( name1, O_WRONLY|O_CREAT );
+    if( fd1 < 0 ) SHOW_RESULT( open, fd1 );
+
+    fd2 = open( name2, O_RDONLY );
+    if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
+    
+    for(;;)
+    {
+        done = read( fd2, buf, IOSIZE );
+        if( done < 0 ) SHOW_RESULT( read, done );
+
+        if( done == 0 ) break;
+
+        wrote = write( fd1, buf, done );
+        if( wrote != done ) SHOW_RESULT( write, wrote );
+
+        if( wrote != done ) break;
+    }
+
+    err = close( fd1 );
+    if( err < 0 ) SHOW_RESULT( close, err );
+
+    err = close( fd2 );
+    if( err < 0 ) SHOW_RESULT( close, err );
+    
+}
+
+//==========================================================================
+
+static void comparefiles( char *name2, char *name1 )
+{
+    int err;
+    char buf1[IOSIZE];
+    char buf2[IOSIZE];
+    int fd1, fd2;
+    ssize_t done1, done2;
+    int i;
+
+    diag_printf("<INFO>: compare files %s == %s\n",name2,name1);
+
+    err = access( name1, F_OK );
+    if( err != 0 ) SHOW_RESULT( access, err );
+
+    err = access( name1, F_OK );
+    if( err != 0 ) SHOW_RESULT( access, err );
+    
+    fd1 = open( name1, O_RDONLY );
+    if( fd1 < 0 ) SHOW_RESULT( open, fd1 );
+
+    fd2 = open( name2, O_RDONLY );
+    if( fd2 < 0 ) SHOW_RESULT( open, fd2 );
+    
+    for(;;)
+    {
+        done1 = read( fd1, buf1, IOSIZE );
+        if( done1 < 0 ) SHOW_RESULT( read, done1 );
+
+        done2 = read( fd2, buf2, IOSIZE );
+        if( done2 < 0 ) SHOW_RESULT( read, done2 );
+
+        if( done1 != done2 )
+            diag_printf("Files different sizes\n");
+        
+        if( done1 == 0 ) break;
+
+        for( i = 0; i < done1; i++ )
+            if( buf1[i] != buf2[i] )
+            {
+                diag_printf("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]);
+                CYG_TEST_FAIL("Data in files not equal\n");
+            }
+    }
+
+    err = close( fd1 );
+    if( err < 0 ) SHOW_RESULT( close, err );
+
+    err = close( fd2 );
+    if( err < 0 ) SHOW_RESULT( close, err );
+    
+}
+
+//==========================================================================
+
+void checkcwd( const char *cwd )
+{
+    static char cwdbuf[PATH_MAX];
+    char *ret;
+
+    ret = getcwd( cwdbuf, sizeof(cwdbuf));
+    if( ret == NULL ) SHOW_RESULT( getcwd, ret );    
+
+    if( strcmp( cwdbuf, cwd ) != 0 )
+    {
+        diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd );
+        CYG_TEST_FAIL( "Current directory mismatch");
+    }
+}
+
+//==========================================================================
+// main
+
+void fileio1_main( CYG_ADDRESS id )
+{
+    int err;
+    int existingdirents=-1;
+
+    CYG_TEST_INIT();
+
+    // --------------------------------------------------------------
+
+    err = mount( "/dev/disk0/1", "/", "fatfs" );    
+    if( err < 0 ) SHOW_RESULT( mount, err );    
+
+    err = chdir( "/" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+
+    checkcwd( "/" );
+    
+    listdir( "/", true, -1, &existingdirents );
+
+    // --------------------------------------------------------------
+
+    createfile( "/foo", 20257 );
+    checkfile( "foo" );
+    copyfile( "foo", "fee");
+    checkfile( "fee" );
+    comparefiles( "foo", "/fee" );
+    diag_printf("<INFO>: mkdir bar\n");
+    err = mkdir( "/bar", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+
+    listdir( "/" , true, existingdirents+3, NULL );
+
+    copyfile( "fee", "/bar/fum" );
+    checkfile( "bar/fum" );
+    comparefiles( "/fee", "bar/fum" );
+
+    diag_printf("<INFO>: cd bar\n");
+    err = chdir( "bar" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+
+    checkcwd( "/bar" );
+    
+    diag_printf("<INFO>: rename /foo bundy\n");    
+    err = rename( "/foo", "bundy" );
+    if( err < 0 ) SHOW_RESULT( rename, err );
+    
+    listdir( "/", true, existingdirents+2, NULL );
+    listdir( "" , true, 4, NULL );
+
+    checkfile( "/bar/bundy" );
+    comparefiles("/fee", "bundy" );
+
+    // --------------------------------------------------------------
+
+    diag_printf("<INFO>: unlink fee\n");    
+    err = unlink( "/fee" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: unlink fum\n");        
+    err = unlink( "fum" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: unlink /bar/bundy\n");        
+    err = unlink( "/bar/bundy" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: cd /\n");        
+    err = chdir( "/" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+
+    checkcwd( "/" );
+    
+    diag_printf("<INFO>: rmdir /bar\n");        
+    err = rmdir( "/bar" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+    
+    listdir( "/", false, existingdirents, NULL );
+
+    // --------------------------------------------------------------
+
+#if 0
+    diag_printf("<INFO>: mkdir disk2\n");
+    err = mkdir( "/disk2", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+#else
+    diag_printf("<INFO>: mount /disk2\n");    
+    err = mount( "/dev/disk0/2", "/disk2", "fatfs" );    
+    if( err < 0 ) SHOW_RESULT( mount, err );    
+#endif
+    
+    listdir( "/disk2" , true, -1, &existingdirents);
+        
+    createfile( "/disk2/tinky", 4567 );
+    copyfile( "/disk2/tinky", "/disk2/laalaa" );
+    checkfile( "/disk2/tinky");
+    checkfile( "/disk2/laalaa");
+    comparefiles( "/disk2/tinky", "/disk2/laalaa" );
+
+    diag_printf("<INFO>: cd /disk2\n");    
+    err = chdir( "/disk2" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+
+    checkcwd( "/disk2" );
+        
+    diag_printf("<INFO>: mkdir noonoo\n");    
+    err = mkdir( "noonoo", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+
+    listdir( "/disk2" , true, existingdirents+3, NULL);
+
+    diag_printf("<INFO>: cd noonoo\n");
+    err = chdir( "noonoo" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+
+    checkcwd( "/disk2/noonoo" );
+    
+    createfile( "tinky", 6789 );
+    checkfile( "tinky" );
+
+    createfile( "dipsy", 34567 );
+    checkfile( "dipsy" );
+    copyfile( "dipsy", "po" );
+    checkfile( "po" );
+    comparefiles( "dipsy", "po" );
+
+    listdir( ".", true, 5, NULL );
+    listdir( "", true, 5, NULL );
+    listdir( "..", true, existingdirents+3, NULL );
+
+    // --------------------------------------------------------------
+
+    diag_printf("<INFO>: unlink tinky\n");    
+    err = unlink( "tinky" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: unlink dipsy\n");    
+    err = unlink( "dipsy" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: unlink po\n");    
+    err = unlink( "po" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: cd ..\n"); 
+    err = chdir( ".." );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2" );
+    
+    diag_printf("<INFO>: rmdir noonoo\n"); 
+    err = rmdir( "noonoo" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+
+    // --------------------------------------------------------------
+
+    err = mkdir( "x", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+    
+    err = mkdir( "x/y", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+    
+    err = mkdir( "x/y/z", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+
+    err = mkdir( "x/y/z/w", 0 );
+    if( err < 0 ) SHOW_RESULT( mkdir, err );
+    
+    diag_printf("<INFO>: cd /disk2/x/y/z/w\n");
+    err = chdir( "/disk2/x/y/z/w" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2/x/y/z/w" );
+
+    diag_printf("<INFO>: cd ..\n");
+    err = chdir( ".." );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2/x/y/z" );
+    
+    diag_printf("<INFO>: cd .\n");
+    err = chdir( "." );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2/x/y/z" );
+
+    diag_printf("<INFO>: cd ../../y\n");
+    err = chdir( "../../y" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2/x/y" );
+
+    diag_printf("<INFO>: cd ../..\n");
+    err = chdir( "../.." );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/disk2" );
+
+    diag_printf("<INFO>: rmdir x/y/z/w\n"); 
+    err = rmdir( "x/y/z/w" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+
+    diag_printf("<INFO>: rmdir x/y/z\n"); 
+    err = rmdir( "x/y/z" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+
+    diag_printf("<INFO>: rmdir x/y\n"); 
+    err = rmdir( "x/y" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+
+    diag_printf("<INFO>: rmdir x\n"); 
+    err = rmdir( "x" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+    
+    // --------------------------------------------------------------
+
+    checkcwd( "/disk2" );
+    
+    diag_printf("<INFO>: unlink tinky\n");    
+    err = unlink( "tinky" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: unlink laalaa\n");    
+    err = unlink( "laalaa" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );
+
+    diag_printf("<INFO>: cd /\n");    
+    err = chdir( "/" );
+    if( err < 0 ) SHOW_RESULT( chdir, err );
+    checkcwd( "/" );
+
+    listdir( "/disk2", true, -1, NULL );
+    
+#if 0
+    diag_printf("<INFO>: rmdir dir\n"); 
+    err = rmdir( "disk2" );
+    if( err < 0 ) SHOW_RESULT( rmdir, err );
+#else
+    diag_printf("<INFO>: umount /disk2\n");    
+    err = umount( "/disk2" );
+    if( err < 0 ) SHOW_RESULT( umount, err );    
+#endif
+    
+    maxfile("file.max");
+
+    listdir( "/", true, -1, NULL );    
+        
+    diag_printf("<INFO>: unlink file.max\n");    
+    err = unlink( "file.max" );
+    if( err < 0 ) SHOW_RESULT( unlink, err );    
+
+    diag_printf("<INFO>: umount /\n");    
+    err = umount( "/" );
+    if( err < 0 ) SHOW_RESULT( umount, err );    
+    
+    CYG_TEST_PASS_FINISH("fileio1");
+}
+
+// -------------------------------------------------------------------------
+
+#include <cyg/kernel/kapi.h>
+
+static char stack[CYGNUM_HAL_STACK_SIZE_TYPICAL];
+static cyg_handle_t thread_handle;
+static cyg_thread thread;
+
+externC void
+cyg_start( void )
+{
+    cyg_thread_create(3,                // Priority - just a number
+                      fileio1_main,     // entry
+                      0,                // index
+                      0,                // no name
+                      &stack[0],        // Stack
+                      sizeof(stack),    // Size
+                      &thread_handle,   // Handle
+                      &thread           // Thread data structure
+        );
+    cyg_thread_resume(thread_handle);
+
+    cyg_scheduler_start();
+}
+
+// -------------------------------------------------------------------------
+// EOF fileio1.c
--- a/packages/io/common/current/ChangeLog
+++ b/packages/io/common/current/ChangeLog
@@ -1,3 +1,8 @@
+2004-01-19  Nick Garnett  <nickg@calivar.com>
+
+	* include/config_keys.h (CYG_IO_GET_CONFIG_DISK_INFO): Added DISK
+	IO config key base definition.
+
 2003-11-27  David Woodhouse  <dwmw2@infradead.org>
 
 	* include/config_keys.h: Add CYG_IO_SET_CONFIG_FLASH_FIS_NAME
--- a/packages/io/common/current/include/config_keys.h
+++ b/packages/io/common/current/include/config_keys.h
@@ -123,6 +123,11 @@
 
 #define CYG_IO_SET_CONFIG_FLASH_FIS_NAME         0x680
 
+// ======== 0x700 DISK =======================================================
+// Get/Set configuration 'key' values for DISK I/O 
+
+#define CYG_IO_GET_CONFIG_DISK_INFO              0x700
+
 // ======== 0x1000 Generic ===================================================
 // Get/Set configuration 'key' values that can apply to more than one
 // class of device.
new file mode 100644
--- /dev/null
+++ b/packages/io/disk/current/ChangeLog
@@ -0,0 +1,58 @@
+2004-01-15  Nick Garnett  <nickg@calivar.com>
+
+	* src/disk.c:
+	* include/disk.h: Removed block_pos arguments from
+	hardware driver read and write calls: it is not necessary.
+
+	* src/disk.c (read_mbr): Modified to read a complete sector from
+	the disk rather than pieces. Drivers should only ever see
+	reads/writes in multiples of 512 bytes.
+
+2003-10-16 Savin Zlobec <savin@elatec.si>
+
+	* src/disk.c:
+        Check return code from MBR scan and set
+        connected flag only if there was no error.
+
+2003-09-01 Savin Zlobec <savin@elatec.si>
+
+        * cdl/io_disk.cdl:
+        * inc/disk.h:
+        * inc/diskio.h:
+        * src/disk.c:
+        A block device IO implementation
+
+
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/io/disk/current/cdl/io_disk.cdl
@@ -0,0 +1,116 @@
+# ====================================================================
+#
+#      io_disk.cdl
+#
+#      eCos IO configuration data
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+## Copyright (C) 2003 Savin Zlobec 
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+## at http://sources.redhat.com/ecos/ecos-license/
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      savin
+# Original data:  jskov, gthomas
+# Contributors:
+# Date:           2003-06-09
+#
+#####DESCRIPTIONEND####
+#
+# ====================================================================
+
+cdl_package CYGPKG_IO_DISK {
+    display       "Disk device drivers"
+    active_if     CYGPKG_IO
+    requires      CYGPKG_ERROR
+    include_dir   cyg/io
+    description   "
+        This option enables drivers for basic I/O services on
+        disk devices."
+
+    compile       -library=libextras.a disk.c
+ 
+    define_proc {
+	puts $::cdl_header "/***** proc output start *****/"
+	puts $::cdl_header "#include <pkgconf/system.h>"
+	puts $::cdl_header "#ifdef CYGDAT_IO_DISK_DEVICE_HEADER"
+	puts $::cdl_header "# include CYGDAT_IO_DISK_DEVICE_HEADER"
+	puts $::cdl_header "#endif "
+	puts $::cdl_header "/****** proc output end ******/"
+    }
+
+    cdl_component CYGPKG_IO_DISK_DEVICES {
+        display       "Hardware disk device drivers"
+        flavor        bool
+        default_value 1
+        description   "
+            This option enables the hardware disk drivers
+	        for the current platform."
+    }
+
+    cdl_component CYGPKG_IO_DISK_OPTIONS {
+        display "Disk device driver build options"
+        flavor  none
+        description   "
+	        Package specific build options including control over
+	        compiler flags used only in building this package,
+	        and details of which tests are built."
+
+
+        cdl_option CYGPKG_IO_DISK_CFLAGS_ADD {
+            display "Additional compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building the disk device drivers. These flags are used 
+                in addition to the set of global flags."
+        }
+
+        cdl_option CYGPKG_IO_DISK_CFLAGS_REMOVE {
+            display "Suppressed compiler flags"
+            flavor  data
+            no_define
+            default_value { "" }
+            description   "
+                This option modifies the set of compiler flags for
+                building the disk device drivers. These flags are removed from
+                the set of global flags if present."
+        }
+    }
+}
+
+# ====================================================================
+# EOF io_disk.cdl
new file mode 100644
--- /dev/null
+++ b/packages/io/disk/current/include/disk.h
@@ -0,0 +1,169 @@
+#ifndef CYGONCE_DISK_H
+#define CYGONCE_DISK_H
+// ====================================================================
+//
+//      disk.h
+//
+//      Device I/O 
+//
+// ====================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license/
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+// ====================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     savin 
+// Original data: gthomas
+// Date:          2003-06-09
+// Purpose:       Internal interfaces for disk I/O drivers
+// Description:
+//
+//####DESCRIPTIONEND####
+//
+// ====================================================================
+
+// Disk I/O interfaces
+
+#include <pkgconf/system.h>
+#include <pkgconf/io_disk.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/io/io.h>
+#include <cyg/io/diskio.h>
+#include <cyg/hal/drv_api.h>
+
+typedef struct disk_channel disk_channel;
+typedef struct disk_funs    disk_funs;
+
+// Pointers into upper-level driver
+typedef struct {
+    
+    // Initialize the disk 
+    cyg_bool (*disk_init)(struct cyg_devtab_entry *tab);
+
+    // Disk device has been connected
+    Cyg_ErrNo (*disk_connected)(struct cyg_devtab_entry *tab,
+                                cyg_disk_identify_t     *ident);
+
+    // Disk device has been disconnected
+    Cyg_ErrNo (*disk_disconnected)(struct cyg_devtab_entry *tab);
+
+    // Lookup disk device
+    Cyg_ErrNo (*disk_lookup)(struct cyg_devtab_entry **tab,
+                             struct cyg_devtab_entry  *sub_tab,
+                             const char               *name);
+} disk_callbacks_t;
+
+#define DISK_CALLBACKS(_l,                              \
+                       _init,                           \
+                       _connected,                      \
+                       _disconnected,                   \
+                       _lookup)                         \
+disk_callbacks_t _l = {                                 \
+    _init,                                              \
+    _connected,                                         \
+    _disconnected,                                      \
+    _lookup                                             \
+};
+
+extern disk_callbacks_t cyg_io_disk_callbacks;
+
+// Private data which describes this channel
+struct disk_channel {
+    disk_funs               *funs;
+    disk_callbacks_t        *callbacks;
+    void                    *dev_priv;    // device private data
+    cyg_disk_info_t         *info;        // disk info 
+    cyg_disk_partition_t    *partition;   // partition data 
+    cyg_bool                 mbr_support; // true if disk has MBR
+    cyg_bool                 valid;       // true if device valid 
+    cyg_bool                 init;        // true if initialized
+};
+
+// Initialization macro for disk channel
+#define DISK_CHANNEL(_l,                                              \
+                     _funs,                                           \
+                     _dev_priv,                                       \
+                     _mbr_supp)                                       \
+cyg_disk_info_t _l##_disk_info;                                       \
+disk_channel _l = {                                                   \
+    &(_funs),                                                         \
+    &cyg_io_disk_callbacks,                                           \
+    &(_dev_priv),                                                     \
+    &(_l##_disk_info),                                                \
+    NULL,                                                             \
+    _mbr_supp,                                                        \
+    false,                                                            \
+    false                                                             \
+};
+
+// Low level interface functions
+struct disk_funs {
+
+    // Read block data into buf
+    Cyg_ErrNo (*read)(disk_channel *priv, 
+                      void         *buf, 
+                      cyg_uint32    len,
+                      cyg_uint32    block_num);
+    
+    // Write block data from buf
+    Cyg_ErrNo (*write)(disk_channel *priv, 
+                       const void   *buf, 
+                       cyg_uint32    len,
+                       cyg_uint32    block_num);
+    
+    // Get hardware configuration
+    Cyg_ErrNo (*get_config)(disk_channel *priv, 
+                            cyg_uint32    key, 
+                            const void   *xbuf, 
+                            cyg_uint32   *len);
+    
+    // Set hardware configuration
+    Cyg_ErrNo (*set_config)(disk_channel *priv, 
+                            cyg_uint32    key, 
+                            const void   *xbuf, 
+                            cyg_uint32   *len);
+};
+
+#define DISK_FUNS(_l,_read,_write,_get_config,_set_config)           \
+disk_funs _l = {                                                     \
+  _read,                                                             \
+  _write,                                                            \
+  _get_config,                                                       \
+  _set_config                                                        \
+};
+
+extern cyg_devio_table_t cyg_io_disk_devio;
+
+#endif // CYGONCE_DISK_H
new file mode 100644
--- /dev/null
+++ b/packages/io/disk/current/include/diskio.h
@@ -0,0 +1,94 @@
+#ifndef CYGONCE_DISKIO_H
+#define CYGONCE_DISKIO_H
+// ====================================================================
+//
+//      diskio.h
+//
+//      Device I/O 
+//
+// ====================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+// ====================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     savin
+// Date:          2003-06-09
+// Purpose:       Special support for disk I/O devices
+// Description:
+//
+//####DESCRIPTIONEND####
+//
+// ====================================================================
+
+// This file contains the user-level visible I/O interfaces
+
+#include <pkgconf/hal.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/io/config_keys.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+typedef struct {
+    char        serial[20+1];      // serial number
+    char        firmware_rev[8+1]; // firmware revision
+    char        model_num[40+1];   // model number 
+    cyg_uint32  cylinders_num;     // number of cylinders         (CHS)
+    cyg_uint32  heads_num;         // number of heads             (CHS)
+    cyg_uint32  sectors_num;       // number of sectors per track (CHS)
+    cyg_uint32  lba_sectors_num;   // total number of sectors in LBA mode
+} cyg_disk_identify_t;
+
+typedef struct {
+    cyg_uint8  type;    // partition type
+    cyg_uint8  state;   // state 0x00 - inactive, 0x80 - active
+    cyg_uint32 start;   // first sector number
+    cyg_uint32 end;     // last sector number
+    cyg_uint32 size;    // size in sectors
+} cyg_disk_partition_t;
+
+typedef struct {
+    cyg_disk_identify_t   ident;         // identify data
+    cyg_disk_partition_t  partitions[4]; // partitions
+    cyg_addrword_t        devs[4];       // device instances
+    cyg_uint32            block_size;    // block size
+    cyg_uint32            blocks_num;    // number of blocks on disk
+    cyg_bool              connected;     // true if device connected
+} cyg_disk_info_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* CYGONCE_DISKIO_H */
+/* EOF diskio.h */
new file mode 100644
--- /dev/null
+++ b/packages/io/disk/current/src/disk.c
@@ -0,0 +1,581 @@
+//==========================================================================
+//
+//      io/disk/disk.c
+//
+//      High level disk driver
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     savin 
+// Date:          2003-06-10
+// Purpose:       Top level disk driver
+// Description: 
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <pkgconf/io.h>
+#include <pkgconf/io_disk.h>
+
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/disk.h>
+#include <cyg/infra/cyg_ass.h>      // assertion support
+#include <cyg/infra/diag.h>         // diagnostic output
+
+#include <stdlib.h>  // malloc 
+
+// ---------------------------------------------------------------------------
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define D(_args_) diag_printf _args_
+#else
+# define D(_args_)
+#endif
+
+// ---------------------------------------------------------------------------
+
+// Master Boot Record defines
+#define MBR_SIG_ADDR  0x1FE   // signature address 
+#define MBR_SIG_BYTE0 0x55    // signature first byte value
+#define MBR_SIG_BYTE1 0xAA    // signature second byte value
+#define MBR_PART_ADDR 0x1BE   // first partition address
+#define MBR_PART_SIZE 0x10    // partition size
+#define MBR_PART_NUM  4       // number of partitions
+
+// Get cylinders, heads and sectors from data (MBR partition format)
+#define READ_CHS(_data_, _c_, _h_, _s_)                     \
+    do {                                                    \
+        _h_ = (*((cyg_uint8 *)_data_));                     \
+        _s_ = (*(((cyg_uint8 *)_data_)+1) &  0x3F);         \
+        _c_ = (*(((cyg_uint8 *)_data_)+1) & ~0x3F) << 2 |   \
+              (*(((cyg_uint8 *)_data_)+2));                 \
+    } while (0)
+
+// Get double word from data (MBR partition format)
+#define READ_DWORD(_data_, _val_)                           \
+    do {                                                    \
+        _val_ = *((cyg_uint8 *)_data_)           |          \
+                *(((cyg_uint8 *)_data_)+1) << 8  |          \
+                *(((cyg_uint8 *)_data_)+2) << 16 |          \
+                *(((cyg_uint8 *)_data_)+3) << 24;           \
+    } while (0)
+
+// Convert cylinders, heads and sectors to LBA sectors 
+#define CHS_TO_LBA(_info_, _c_, _h_, _s_, _lba_) \
+    (_lba_=(((_c_)*(_info_)->heads_num+(_h_))*(_info_)->sectors_num)+(_s_)-1)
+    
+// ---------------------------------------------------------------------------
+
+static Cyg_ErrNo disk_bread(cyg_io_handle_t  handle, 
+                            void            *buf, 
+                            cyg_uint32      *len, 
+                            cyg_uint32       pos);
+
+static Cyg_ErrNo disk_bwrite(cyg_io_handle_t  handle, 
+                             const void      *buf, 
+                             cyg_uint32      *len, 
+                             cyg_uint32       pos);
+
+static Cyg_ErrNo disk_select(cyg_io_handle_t handle, 
+                             cyg_uint32      which, 
+                             CYG_ADDRWORD    info);
+
+static Cyg_ErrNo disk_get_config(cyg_io_handle_t  handle, 
+                                 cyg_uint32       key, 
+                                 void            *buf, 
+                                 cyg_uint32      *len);
+
+static Cyg_ErrNo disk_set_config(cyg_io_handle_t  handle, 
+                                 cyg_uint32       key, 
+                                 const void      *buf, 
+                                 cyg_uint32      *len);
+
+BLOCK_DEVIO_TABLE(cyg_io_disk_devio,
+                  disk_bwrite,
+                  disk_bread,
+                  disk_select,
+                  disk_get_config,
+                  disk_set_config
+);
+
+static cyg_bool disk_init(struct cyg_devtab_entry *tab);
+
+static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab,
+                                cyg_disk_identify_t     *ident);
+
+static Cyg_ErrNo disk_disconnected(struct cyg_devtab_entry *tab);
+
+static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab,
+                             struct cyg_devtab_entry  *sub_tab,
+                             const char               *name);
+
+DISK_CALLBACKS(cyg_io_disk_callbacks, 
+               disk_init,
+               disk_connected,
+               disk_disconnected,
+               disk_lookup
+); 
+
+// ---------------------------------------------------------------------------
+
+//
+// Read partition from data
+// 
+static void 
+read_partition(cyg_uint8            *data,
+               cyg_disk_info_t      *info,
+               cyg_disk_partition_t *part)
+{
+    cyg_uint16 c, h, s;
+
+    part->type  = data[4];
+    part->state = data[0];
+
+    READ_CHS(&data[1], c, h, s);
+    CHS_TO_LBA(&info->ident, c, h, s, part->start);
+
+    READ_CHS(&data[5], c, h, s);
+    CHS_TO_LBA(&info->ident, c, h, s, part->end);
+
+    READ_DWORD(&data[12], part->size);
+}
+
+//
+// Read Master Boot Record (partitions)
+//
+static Cyg_ErrNo 
+read_mbr(disk_channel *chan)
+{
+    cyg_disk_info_t *info = chan->info;
+    disk_funs       *funs = chan->funs;
+    cyg_uint8 buf[512];
+    Cyg_ErrNo res = ENOERR;
+    int i;
+ 
+    for (i = 0; i < MBR_PART_NUM; i++)
+        info->partitions[i].type = 0x00;    
+   
+    res = (funs->read)(chan, (void *)buf, 512, 0);
+    if (ENOERR != res)
+        return res;
+
+    if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
+    {
+        D(("disk MBR found\n")); 
+ 
+        for (i = 0; i < MBR_PART_NUM; i++)
+        {
+            cyg_disk_partition_t *part = &info->partitions[i];
+            
+            read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part); 
+#ifdef DEBUG
+            if (0x00 != part->type)
+            {
+                D(("\ndisk MBR partition %d:\n", i));
+                D(("      type  = %02X\n", part->type));
+                D(("      state = %02X\n", part->state));
+                D(("      start = %d\n",   part->start));
+                D(("      end   = %d\n",   part->end));
+                D(("      size  = %d\n\n", part->size));
+            }
+#endif
+        } 
+    }
+    return ENOERR;
+}
+
+static cyg_bool 
+disk_init(struct cyg_devtab_entry *tab)
+{
+    disk_channel    *chan = (disk_channel *) tab->priv;
+    cyg_disk_info_t *info = chan->info;
+    int i;
+
+    if (!chan->init)
+    {
+        info->connected = false;
+
+        // clear devices array (one per partition)
+        // and partition data
+        for (i = 0; i < MBR_PART_NUM; i++)
+        {
+            info->devs[i] = (cyg_addrword_t) 0;
+            info->partitions[i].type = 0x00;
+        }
+        
+        chan->init = true;
+    }
+    return true;
+}
+
+static Cyg_ErrNo
+disk_connected(struct cyg_devtab_entry *tab,
+               cyg_disk_identify_t     *ident)
+{
+    disk_channel    *chan = (disk_channel *) tab->priv;
+    cyg_disk_info_t *info = chan->info;
+    Cyg_ErrNo res = ENOERR;
+ 
+    if (!chan->init)
+        return -EINVAL;
+    
+    info->ident      = *ident;
+    info->block_size = 512;
+    info->blocks_num = ident->lba_sectors_num;
+ 
+    D(("disk connected\n")); 
+    D(("    serial       = '%s'\n", ident->serial)); 
+    D(("    firmware rev = '%s'\n", ident->firmware_rev)); 
+    D(("    model num    = '%s'\n", ident->model_num)); 
+    D(("    block_size   = %d\n",   info->block_size));
+    D(("    blocks_num   = %d\n",   info->blocks_num));
+
+    if (chan->mbr_support)
+    {    
+        // read disk master boot record
+        res = read_mbr(chan);
+    }
+
+    if (ENOERR == res)
+    {    
+        // now declare that we are connected
+        info->connected = true;
+        chan->valid     = true; 
+    }
+    return res;
+}
+
+static Cyg_ErrNo
+disk_disconnected(struct cyg_devtab_entry *tab)
+{
+    disk_channel    *chan = (disk_channel *) tab->priv;
+    cyg_disk_info_t *info = chan->info;
+    int i;
+
+    if (!chan->init)
+        return -EINVAL;
+    
+    info->connected = false;
+    chan->valid     = false;
+     
+    // clear partition data and invalidate 
+    // any allocated devices
+    for (i = 0; i < MBR_PART_NUM; i++)
+    {
+        info->partitions[i].type = 0x00;
+
+        if (0 != info->devs[i])
+        {
+            struct cyg_devtab_entry *dtab;
+            disk_channel            *dchan;
+
+            dtab  = (struct cyg_devtab_entry *)info->devs[i];
+            dchan = (disk_channel *) dtab->priv;
+            
+            dchan->valid = false;
+        }
+    }
+
+    
+    D(("disk disconnected\n")); 
+
+    return ENOERR;    
+}
+    
+static Cyg_ErrNo
+disk_lookup(struct cyg_devtab_entry **tab,
+            struct cyg_devtab_entry  *sub_tab,
+            const char *name)
+{
+    disk_channel    *chan = (disk_channel *) (*tab)->priv;
+    cyg_disk_info_t *info = chan->info;
+    struct cyg_devtab_entry *new_tab;
+    disk_channel            *new_chan;
+    int dev_num;
+    
+    if (!info->connected || name[0] < '0' || name[0] > '4' || '\0' != name[1])
+        return -EINVAL;
+
+    dev_num = name[0] - '0';
+
+    D(("disk lookup dev number = %d\n", dev_num)); 
+
+    if (0 == dev_num)
+    {
+        // 0 is the root device number
+        return ENOERR; 
+    }
+    if (0x00 == info->partitions[dev_num-1].type)
+    {
+        D(("disk NO partition for dev\n")); 
+        return -EINVAL;
+    }
+    
+    if (0 == info->devs[dev_num-1])
+    {
+        D(("disk creating new devtab entry\n")); 
+
+        // alloc mem for new device
+        new_tab = (struct cyg_devtab_entry *)
+            malloc(sizeof(struct cyg_devtab_entry));
+        if (NULL == new_tab)
+            return -ENOMEM;        
+
+        // alloc mem for new device private data
+        new_chan = (disk_channel *)malloc(sizeof(disk_channel));
+        if (NULL == new_chan)
+        {
+            free(new_tab);
+            return -ENOMEM;
+        }
+    }
+    else
+    {
+        new_tab  = (struct cyg_devtab_entry *) info->devs[dev_num-1]; 
+        new_chan = (disk_channel *) new_tab->priv;   
+    }
+    
+    // copy device data from parent
+    *new_tab  = **tab; 
+    *new_chan = *chan;
+
+    new_tab->priv = (void *)new_chan;
+
+    // set partition ptr and put this device into devices array    
+    new_chan->partition = &info->partitions[dev_num-1];
+    chan->info->devs[dev_num-1] = (cyg_addrword_t) new_tab;
+
+    // return device tab 
+    *tab = new_tab;
+        
+    return ENOERR;
+}
+
+// ---------------------------------------------------------------------------
+
+static Cyg_ErrNo 
+disk_bread(cyg_io_handle_t  handle, 
+           void            *buf, 
+           cyg_uint32      *len,
+           cyg_uint32       pos)
+{
+    cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
+    disk_channel       *chan = (disk_channel *) t->priv;
+    disk_funs          *funs = chan->funs;
+    cyg_disk_info_t    *info = chan->info;
+    cyg_uint32  size = *len;
+    cyg_uint8  *bbuf = (cyg_uint8  *)buf;
+    Cyg_ErrNo   res  = ENOERR;
+    cyg_uint32  last;
+
+    if (!info->connected || !chan->valid)
+        return -EINVAL;
+    
+    if (NULL != chan->partition)
+    {
+        pos += chan->partition->start;
+        last = chan->partition->end;
+    }
+    else
+    {
+        last = info->blocks_num-1;
+    }
+ 
+    D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf)); 
+ 
+    while (size > 0)
+    {
+        if (pos > last)
+        {
+            res = -EIO;
+            break;
+        }
+        
+        res = (funs->read)(chan, (void*)bbuf, info->block_size, pos);
+        if (ENOERR != res)
+            break;
+
+        if (!info->connected)
+        {
+            res = -EINVAL;
+            break;
+        }
+            
+        bbuf += info->block_size;
+        pos++;
+        size--;
+    }
+    *len -= size;
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
+static Cyg_ErrNo 
+disk_bwrite(cyg_io_handle_t  handle, 
+            const void      *buf, 
+            cyg_uint32      *len,
+            cyg_uint32       pos)
+{
+    cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
+    disk_channel       *chan = (disk_channel *) t->priv;
+    disk_funs          *funs = chan->funs;
+    cyg_disk_info_t    *info = chan->info;
+    cyg_uint32  size = *len;
+    cyg_uint8  *bbuf = (cyg_uint8 * const) buf;
+    Cyg_ErrNo   res  = ENOERR;
+    cyg_uint32  last;
+
+    if (!info->connected || !chan->valid)
+        return -EINVAL;
+ 
+    if (NULL != chan->partition)
+    {
+        pos += chan->partition->start;
+        last = chan->partition->end;
+    }
+    else
+    {
+        last = info->blocks_num-1;
+    }
+    
+    D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf)); 
+   
+    while (size > 0)
+    {
+        if (pos > last)
+        {
+            res = -EIO;
+            break;
+        }
+        
+        res = (funs->write)(chan, (void*)bbuf, info->block_size, pos);
+        if (ENOERR != res)
+            break;
+ 
+        if (!info->connected)
+        {
+            res = -EINVAL;
+            break;
+        }
+ 
+        bbuf += info->block_size;
+        pos++;
+        size--;
+    }
+    *len -= size;
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
+static cyg_bool
+disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
+{
+    cyg_devtab_entry_t *t     = (cyg_devtab_entry_t *) handle;
+    disk_channel       *chan  = (disk_channel *) t->priv;
+    cyg_disk_info_t    *cinfo = chan->info;
+ 
+    if (!cinfo->connected || !chan->valid)
+        return false;
+    else
+        return true;
+}
+
+// ---------------------------------------------------------------------------
+
+static Cyg_ErrNo 
+disk_get_config(cyg_io_handle_t  handle, 
+                cyg_uint32       key, 
+                void            *xbuf,
+                cyg_uint32      *len)
+{
+    cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
+    disk_channel       *chan = (disk_channel *) t->priv;
+    cyg_disk_info_t    *info = chan->info;
+    cyg_disk_info_t    *buf  = (cyg_disk_info_t *) xbuf;
+    disk_funs          *funs = chan->funs;
+    Cyg_ErrNo res = ENOERR;
+ 
+    if (!info->connected || !chan->valid)
+        return -EINVAL;
+
+    D(("disk get config key=%d\n", key)); 
+    
+    switch (key) {
+    case CYG_IO_GET_CONFIG_DISK_INFO:
+        if (*len < sizeof(cyg_disk_info_t)) {
+            return -EINVAL;
+        }
+        *buf = *chan->info;
+        *len = sizeof(cyg_disk_info_t);
+        break;       
+
+    default:
+        // pass down to lower layers
+        res = (funs->get_config)(chan, key, xbuf, len);
+    }
+   
+    return res;
+}
+
+// ---------------------------------------------------------------------------
+
+static Cyg_ErrNo 
+disk_set_config(cyg_io_handle_t  handle, 
+                cyg_uint32       key, 
+                const void      *xbuf, 
+                cyg_uint32      *len)
+{
+    cyg_devtab_entry_t *t    = (cyg_devtab_entry_t *) handle;
+    disk_channel       *chan = (disk_channel *) t->priv;
+    cyg_disk_info_t    *info = chan->info;
+    disk_funs          *funs = chan->funs;
+
+    if (!info->connected || !chan->valid)
+        return -EINVAL;
+
+    D(("disk set config key=%d\n", key)); 
+ 
+    // pass down to lower layers
+    return (funs->set_config)(chan, key, xbuf, len);
+}
+
+// ---------------------------------------------------------------------------
+// EOF disk.c
new file mode 100644
--- /dev/null
+++ b/packages/services/blib/current/ChangeLog
@@ -0,0 +1,41 @@
+2003-09-01 Savin Zlobec <savin@elatec.si>
+
+        * cdl/blib.cdl:
+        * inc/blib.h: 
+        * src/disk.c:  
+        A block access and cache library. 
+
+
+
+//===========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/services/blib/current/cdl/blib.cdl
@@ -0,0 +1,70 @@
+# ====================================================================
+#
+#      blib.cdl
+#
+#      Block cache and access library configuration data
+#
+# ====================================================================
+#####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003 Savin Zlobec 
+##
+## eCos is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free
+## Software Foundation; either version 2 or (at your option) any later version.
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+## WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+## for more details.
+##
+## You should have received a copy of the GNU General Public License along
+## with eCos; if not, write to the Free Software Foundation, Inc.,
+## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+##
+## As a special exception, if other files instantiate templates or use macros
+## or inline functions from this file, or you compile this file and link it
+## with other works to produce a work based on this file, this file does not
+## by itself cause the resulting work to be covered by the GNU General Public
+## License. However the source code for this file must still be made available
+## in accordance with section (3) of the GNU General Public License.
+##
+## This exception does not invalidate any other reasons why a work based on
+## this file might be covered by the GNU General Public License.
+##
+## -------------------------------------------
+#####ECOSGPLCOPYRIGHTEND####
+# ====================================================================
+######DESCRIPTIONBEGIN####
+#
+# Author(s):      savin 
+# Contributors:   
+# Date:           2003-08-29
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_BLOCK_LIB {
+    display         "Block cache and access library"
+    define_header   blib.h
+    include_dir     blib
+
+    requires        CYGPKG_ISOINFRA
+    requires        CYGPKG_MEMALLOC
+    requires        CYGPKG_LINUX_COMPAT
+    requires        CYGPKG_ERROR
+    
+    compile         -library=libextras.a blib.c    
+
+    cdl_option CYGIMP_BLOCK_LIB_STATISTICS {
+        display         "Block access statistics support"
+        flavor          bool
+        default_value   1
+        description     "This option enables statistics for
+                         block access operations."
+    }
+}
+
+# ====================================================================
+# End of blib.cdl
new file mode 100644
--- /dev/null
+++ b/packages/services/blib/current/include/blib.h
@@ -0,0 +1,285 @@
+#ifndef CYGONCE_BLIB_H
+#define CYGONCE_BLIB_H
+//==========================================================================
+//
+//      blib.h
+//
+//      Block cache and access library  
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     savin 
+// Date:          2003-08-29
+// Description: 
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <cyg/infra/cyg_type.h> 
+#include <cyg/kernel/kapi.h> 
+#include <cyg/io/io.h> 
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+
+// --------------------------------------------------------------------
+
+typedef int (*cyg_blib_bread_fn) (
+    void*,       // private data
+    void*,       // block buffer
+    cyg_uint32*, // number of blocks to read
+    cyg_uint32   // starting block number
+);
+
+typedef int (*cyg_blib_bwrite_fn) (
+    void*,       // private data
+    const void*, // block buffer
+    cyg_uint32*, // number of blocks to write 
+    cyg_uint32   // starting block number
+);
+
+typedef struct {
+    cyg_uint32  n_gets;     // number of block gets
+    cyg_uint32  n_reads;    // number of block reads
+    cyg_uint32  n_writes;   // number of block writes
+} cyg_blib_stat_t;
+
+typedef struct {
+    void                 *priv;            // private data
+    struct list_head      list_head;       // head of block list 
+    struct rb_root        rb_root;         // block red-black tree root
+    cyg_uint32            block_size;      // block size
+    cyg_uint32            block_size_log2; // block size log2
+    cyg_uint8            *mem_base;        // memory base
+    cyg_uint32            mem_size;        // memory size
+    cyg_mempool_fix       mem_pool;        // memory pool struct
+    cyg_handle_t          mem_pool_h;      // memory pool handle
+    cyg_blib_bread_fn     bread_fn;        // block read function
+    cyg_blib_bwrite_fn    bwrite_fn;       // block write function
+#ifdef CYGIMP_BLOCK_LIB_STATISTICS
+    cyg_blib_stat_t       stat;            // statistics
+#endif
+} cyg_blib_t;
+
+// --------------------------------------------------------------------
+
+// --------------------------------------------------------------------
+// Creates a block lib instance
+// 
+//   priv_data  - private data to pass to bread_fn and bwrite_fn
+//   mem_base   - block cache memory base
+//   mem_size   - block cache memory size
+//   block_size - block size
+//   bread_fn   - function which reads blocks
+//   bwrite_fn  - function which writes blocks
+//   bl         - block lib instance space holder
+//   
+//   returns ENOERR if create succeded
+//
+
+int cyg_blib_create(void               *priv_data,
+                    void               *mem_base,
+                    cyg_uint32          mem_size,
+                    cyg_uint32          block_size,
+                    cyg_blib_bread_fn   bread_fn,
+                    cyg_blib_bwrite_fn  bwrite_fn,
+                    cyg_blib_t         *bl);
+
+// --------------------------------------------------------------------
+// Creates a block lib instance on top of IO system 
+//   (cyg_io_bread and cyg_io_bwrite)
+// 
+//   handle     - cyg_io_handle_t
+//   mem_base   - block cache memory base
+//   mem_size   - block cache memory size
+//   block_size - block size
+//   bl         - block lib instance space holder
+//   
+//   returns ENOERR if create succeded
+//
+
+int cyg_blib_io_create(cyg_io_handle_t     handle,
+                       void               *mem_base,
+                       cyg_uint32          mem_size,
+                       cyg_uint32          block_size,
+                       cyg_blib_t         *bl);
+
+// --------------------------------------------------------------------
+// Deletes a block lib instance
+//   
+//   bl - block lib instance
+//
+//   The block cache is synced before
+//
+//   returns ENOERR if delete succeded
+//
+
+int cyg_blib_delete(cyg_blib_t *bl);
+
+// --------------------------------------------------------------------
+// Reads a number of blocks
+//
+//   bl  - block lib instance
+//   buf - block buffer ptr 
+//   len - number of blocks to read
+//   pos - starting block number
+//       
+//   returns ENOERR if read succeded
+//   
+        
+int cyg_blib_bread(cyg_blib_t *bl,
+                   void       *buf,
+                   cyg_uint32 *len,
+                   cyg_uint32  pos);
+
+// --------------------------------------------------------------------
+// Writes a number of blocks
+//
+//   bl  - block lib instance
+//   buf - block buffer ptr 
+//   len - number of blocks to write 
+//   pos - starting block number
+//       
+//   returns ENOERR if write succeded
+//   
+ 
+int cyg_blib_bwrite(cyg_blib_t *bl,
+                    const void *buf,
+                    cyg_uint32 *len,
+                    cyg_uint32  pos);
+
+// --------------------------------------------------------------------
+// Reads data
+//
+//   bl   - block lib instance
+//   buf  - data buffer ptr 
+//   len  - number of bytes to read
+//   bnum - starting block number 
+//   pos  - starting position inside starting block
+//       
+//   returns ENOERR if read succeded
+//   
+//   The block number is automatically adjusted if
+//   position is greater than block size
+//
+ 
+int cyg_blib_read(cyg_blib_t *bl,
+                  void       *buf,
+                  cyg_uint32 *len,
+                  cyg_uint32  bnum,
+                  cyg_uint32  pos);
+
+// --------------------------------------------------------------------
+// Writes data
+//
+//   bl   - block lib instance
+//   buf  - data buffer ptr 
+//   len  - number of bytes to write 
+//   bnum - starting block number 
+//   pos  - starting position inside starting block
+//       
+//   returns ENOERR if write succeded
+//
+//   The block number is automatically adjusted if
+//   position is greater than block size
+//    
+ 
+int cyg_blib_write(cyg_blib_t *bl,
+                   const void *buf,
+                   cyg_uint32 *len,
+                   cyg_uint32  bnum,
+                   cyg_uint32  pos);
+
+// --------------------------------------------------------------------
+// Syncs block cache - ie write modified blocks
+//
+//   bl - block lib instance
+//
+//   returns ENOERR if sync succeded
+//
+
+int cyg_blib_sync(cyg_blib_t *bl);
+
+// --------------------------------------------------------------------
+// Syncs block - ie write if modified
+//
+//   bl  - block lib instance
+//   num - block number to sync
+//
+//   returns ENOERR if sync succeded
+//
+
+int cyg_blib_sync_block(cyg_blib_t *bl, cyg_uint32 num);
+
+// --------------------------------------------------------------------
+// Flushes block cache 
+//
+//   bl  - block lib instance
+//
+//   returns ENOERR if flush succeded
+//
+//   The block cache is synced before
+//
+
+int cyg_blib_flush(cyg_blib_t *bl);
+
+// --------------------------------------------------------------------
+// Sets block size 
+//
+//   bl         - block lib instance
+//   block_size - new block size
+//
+//   returns ENOERR if set succeded
+//
+//   The block cache is synced before
+//
+
+int cyg_blib_set_block_size(cyg_blib_t *bl, cyg_uint32 block_size);
+
+// --------------------------------------------------------------------
+// Gets block cache statistics 
+//
+//   bl - block lib instance
+//
+//   returns ENOERR if get succeded
+//
+
+int cyg_blib_get_stat(cyg_blib_t *bl, cyg_blib_stat_t *stat);
+
+#endif // CYGONCE_BLIB_H
+
+// --------------------------------------------------------------------
+// EOF blib.h
new file mode 100644
--- /dev/null
+++ b/packages/services/blib/current/src/blib.c
@@ -0,0 +1,629 @@
+//==========================================================================
+//
+//      blib.c
+//
+//      Block cache and access library  
+//
+//==========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2003 Savin Zlobec 
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     savin 
+// Date:          2003-08-29
+// Description: 
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <cyg/io/io.h>
+#include <cyg/infra/cyg_ass.h>      // assertion support
+#include <cyg/infra/diag.h>         // diagnostic output
+#include <cyg/kernel/kapi.h>
+#include <blib/blib.h>
+
+#include <string.h>
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+
+// --------------------------------------------------------------------
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define D(_args_) diag_printf _args_
+#else
+# define D(_args_) CYG_EMPTY_STATEMENT
+#endif
+
+#ifdef CYGIMP_BLOCK_LIB_STATISTICS
+
+#define STAT_INIT(_bl_) do {    \
+        bl->stat.n_gets   = 0;  \
+        bl->stat.n_reads  = 0;  \
+        bl->stat.n_writes = 0;  \
+    } while (0)    
+
+#define STAT(_bl_, _group_) \
+    ((_bl_)->stat._group_++)
+
+#else // CYGIMP_BLOCK_LIB_STATISTICS
+
+#define STAT_INIT(_bl_)     CYG_EMPTY_STATEMENT
+#define STAT(_bl_, _group_) CYG_EMPTY_STATEMENT
+    
+#endif // not CYGIMP_BLOCK_LIB_STATISTICS
+ 
+// --------------------------------------------------------------------
+
+typedef struct {
+    struct list_head  list_node; // list node
+    struct rb_node    rb_node;   // red-black tree node
+    cyg_uint32        num;       // block number
+    cyg_bool          modified;  // is this block data modified (needs write)
+    cyg_uint8         data[0];   // block data
+} blib_block_t;
+
+// --------------------------------------------------------------------
+
+static blib_block_t *
+rb_find_block(cyg_blib_t *bl, cyg_uint32 num)
+{
+    struct rb_node *node  = bl->rb_root.rb_node;
+    blib_block_t   *block = NULL;
+    
+    while (NULL != node)
+    {
+        block = rb_entry(node, blib_block_t, rb_node);
+
+        if (block->num == num)
+            return block;
+
+        node = (block->num > num) ? node->rb_left : node->rb_right;
+    }
+    return NULL;
+}
+
+static void
+rb_add_block(cyg_blib_t *bl, blib_block_t *block)
+{
+    struct rb_node *node = bl->rb_root.rb_node;    
+
+    if (NULL == node)
+    {
+        rb_link_node(&block->rb_node, NULL, &bl->rb_root.rb_node);
+    }
+    else
+    {
+        struct rb_node **link;
+        blib_block_t    *b = NULL;
+        
+        while (NULL != node)
+        {
+            b = rb_entry(node, blib_block_t, rb_node);
+
+            CYG_ASSERTC(b->num != block->num);
+
+            link = (b->num > block->num) ? &node->rb_left : &node->rb_right;
+            node = *link;
+        }
+        rb_link_node(&block->rb_node, &b->rb_node, link);
+    }
+    rb_insert_color(&block->rb_node, &bl->rb_root);
+}
+
+static __inline__ void
+rb_del_block(cyg_blib_t *bl, blib_block_t *block)
+{
+    rb_erase(&block->rb_node, &bl->rb_root);
+}
+
+// --------------------------------------------------------------------
+
+static __inline__ void
+list_add_block(cyg_blib_t *bl, blib_block_t *block)
+{
+    list_add(&block->list_node, &bl->list_head);
+}
+
+static __inline__ void
+list_del_block(cyg_blib_t *bl, blib_block_t *block)
+{
+    list_del(&block->list_node);
+}
+
+static __inline__ blib_block_t *
+list_get_first_block(cyg_blib_t *bl)
+{
+    return(list_entry(bl->list_head.next, blib_block_t, list_node));
+}
+
+static __inline__ blib_block_t *
+list_get_last_block(cyg_blib_t *bl)
+{
+    return(list_entry(bl->list_head.prev, blib_block_t, list_node));
+}
+
+static void
+list_move_block_to_head(cyg_blib_t *bl, blib_block_t *block)
+{
+    list_del(&block->list_node);
+    list_add(&block->list_node, &bl->list_head);
+}
+
+// --------------------------------------------------------------------
+
+static cyg_uint32
+get_val_log2(cyg_uint32 val)
+{
+    cyg_uint32 i, log2;
+
+    i = val;
+    log2 = 0;
+    while (0 == (i & 1))
+    {
+        i >>= 1;
+        log2++;
+    }
+    if (i != 1)
+        return 0;
+    else
+        return log2;
+}
+
+static int 
+blib_sync_block(cyg_blib_t *bl, blib_block_t *block)
+{
+    int ret = ENOERR;
+
+    if (block->modified)
+    {
+        int len = 1;
+
+        D(("blib writting block=%d\n", block->num)); 
+ 
+        STAT(bl, n_writes);
+
+        ret = bl->bwrite_fn(bl->priv, (void *)block->data, &len, block->num);
+
+        if (ENOERR == ret)
+            block->modified = false;
+    }
+    return ret;
+}
+
+static int 
+blib_sync(cyg_blib_t *bl)
+{
+    struct list_head *node = bl->list_head.next;
+    blib_block_t     *block;
+    int ret = ENOERR;
+ 
+    D(("blib cache sync\n")); 
+    
+    while (node != &bl->list_head)
+    {
+        block = list_entry(node, blib_block_t, list_node);
+
+        ret = blib_sync_block(bl, block);
+        if (ENOERR != ret)
+           break;
+        
+        node = node->next;
+    }
+    return ret;
+}
+
+static int 
+blib_get_block(cyg_blib_t    *bl, 
+               cyg_uint32     num, 
+               cyg_bool       read_data,
+               blib_block_t **dblock)
+{
+    blib_block_t *block = NULL;
+    int ret = ENOERR;
+    int len;
+ 
+    D(("blib get block=%d\n", num)); 
+    
+    STAT(bl, n_gets);
+
+    // first check if the most recently used block is the requested block,
+    // this can improve performance when using byte access functions
+    if (!list_empty(&bl->list_head))
+    {
+        blib_block_t *first_block = list_get_first_block(bl);
+        if (first_block->num == num)
+            block = first_block;
+        else 
+            block = rb_find_block(bl, num);
+    }
+    
+    if (NULL != block)
+    {
+        D(("blib block=%d found in cache\n", num)); 
+
+        list_move_block_to_head(bl, block);
+        *dblock = block;
+        return ret;
+    }
+
+    D(("blib block=%d NOT found in cache\n", num)); 
+
+    block = (blib_block_t *)cyg_mempool_fix_try_alloc(bl->mem_pool_h);
+
+    if (NULL == block)
+    {
+        CYG_ASSERTC(!list_empty(&bl->list_head));
+        
+        block = list_get_last_block(bl);
+
+        D(("blib reusing block=%d space\n", block->num)); 
+
+        ret = blib_sync_block(bl, block);
+        if (ENOERR != ret)
+            return ret;
+        
+        list_del_block(bl, block);
+        rb_del_block(bl, block);
+    }
+
+    block->num      = num;
+    block->modified = false;
+
+    if (read_data)
+    {
+        D(("blib reading block=%d\n", block->num)); 
+
+        STAT(bl, n_reads);
+
+        len = 1;
+        ret = bl->bread_fn(bl->priv, (void *)block->data, &len, block->num);
+        if (ENOERR != ret)
+        {
+            cyg_mempool_fix_free(bl->mem_pool_h, block);
+            return ret;
+        }
+    }
+    rb_add_block(bl, block);
+    list_add_block(bl, block);
+    
+    *dblock = block;
+    return ret;
+}
+
+static int 
+blib_free_cache(cyg_blib_t *bl)
+{
+    int ret = ENOERR;
+    ret = blib_sync(bl);
+    if (ENOERR == ret)
+        cyg_mempool_fix_delete(bl->mem_pool_h);
+    return ret;
+}
+
+static int
+blib_init_cache(cyg_blib_t *bl, 
+                void       *mem_base,
+                cyg_uint32  mem_size,
+                cyg_uint32  block_size, 
+                cyg_bool    reinit)
+{
+    int ret = ENOERR;
+    cyg_uint32 mem_block_size;
+    
+    if (reinit)
+    {
+        ret = blib_free_cache(bl); 
+        if (ENOERR != ret)
+            return ret;
+    }
+    
+    bl->rb_root = RB_ROOT;
+    INIT_LIST_HEAD(&bl->list_head);
+
+    bl->mem_base   = mem_base;
+    bl->mem_size   = mem_size;    
+    bl->block_size = block_size;
+
+    bl->block_size_log2 = get_val_log2(block_size);
+    if (0 == bl->block_size_log2)
+        return -EINVAL;
+    
+    mem_block_size = bl->block_size + sizeof(blib_block_t);
+
+    cyg_mempool_fix_create(bl->mem_base, bl->mem_size, mem_block_size,
+                           &bl->mem_pool_h, &bl->mem_pool);
+
+    STAT_INIT(bl);
+
+    return ret;
+}
+
+// --------------------------------------------------------------------
+
+static int
+io_bread(void *priv, void *buf, cyg_uint32 *len, cyg_uint32 pos)
+{
+    return cyg_io_bread((cyg_io_handle_t)priv, buf, len, pos);
+}
+
+static int
+io_bwrite(void *priv, const void *buf, cyg_uint32 *len, cyg_uint32 pos)
+{
+    return cyg_io_bwrite((cyg_io_handle_t)priv, buf, len, pos);
+}
+
+// --------------------------------------------------------------------
+
+int
+cyg_blib_create(void               *priv_data,
+                void               *mem_base,
+                cyg_uint32          mem_size,
+                cyg_uint32          block_size,
+                cyg_blib_bread_fn   bread_fn,
+                cyg_blib_bwrite_fn  bwrite_fn,
+                cyg_blib_t         *bl)
+{
+    int ret;
+    
+    bl->priv      = priv_data;
+    bl->bread_fn  = bread_fn;
+    bl->bwrite_fn = bwrite_fn;
+
+    ret = blib_init_cache(bl, mem_base, mem_size, block_size, false); 
+    return ret; 
+}
+
+int 
+cyg_blib_io_create(cyg_io_handle_t     handle,
+                   void               *mem_base,
+                   cyg_uint32          mem_size,
+                   cyg_uint32          block_size,
+                   cyg_blib_t         *bl)
+{
+    return cyg_blib_create((void *)handle, mem_base, mem_size, block_size,
+                           io_bread, io_bwrite, bl);
+}
+
+int
+cyg_blib_delete(cyg_blib_t *bl)
+{
+    return blib_free_cache(bl);
+}
+
+int 
+cyg_blib_bread(cyg_blib_t *bl,
+               void       *buf,
+               cyg_uint32 *len,
+               cyg_uint32  pos)
+{
+    blib_block_t *block;
+    cyg_int32  size = *len;
+    cyg_int32  bnum = pos;
+    cyg_uint8 *bbuf = buf;
+    Cyg_ErrNo  ret  = ENOERR;
+
+    D(("blib bread block=%d len=%d buf=%p\n", pos, *len, buf)); 
+
+    while (size > 0)
+    {
+        ret = blib_get_block(bl, bnum, true, &block); 
+        if (ENOERR != ret)
+            break;
+        
+        memcpy((void *)bbuf, (void *)block->data, bl->block_size);
+
+        bbuf += bl->block_size;
+        bnum++;
+        size--;
+    }
+    *len -= size;
+    return ret;
+}
+
+int 
+cyg_blib_bwrite(cyg_blib_t *bl,
+                const void *buf,
+                cyg_uint32 *len,
+                cyg_uint32  pos)
+{
+    blib_block_t *block;
+    cyg_int32  size = *len;
+    cyg_int32  bnum = pos;
+    cyg_uint8 *bbuf = (cyg_uint8 * const) buf;
+    Cyg_ErrNo  ret  = ENOERR;
+
+    D(("blib bwrite block=%d len=%d buf=%p\n", pos, *len, buf)); 
+
+    while (size > 0)
+    {
+        ret = blib_get_block(bl, bnum, false, &block); 
+        if (ENOERR != ret)
+            break;
+        
+        memcpy((void *)block->data, (void *)bbuf, bl->block_size);
+        block->modified = true;
+
+        bbuf += bl->block_size;
+        bnum++;
+        size--;
+    }
+    *len -= size;
+    return ret;
+}
+
+int
+cyg_blib_read(cyg_blib_t *bl,
+              void       *buf,
+              cyg_uint32 *len,
+              cyg_uint32  bnum,
+              cyg_uint32  pos)
+{
+    blib_block_t *block;
+    cyg_uint8 *bbuf = buf;
+    cyg_int32  size = *len;
+    Cyg_ErrNo  ret  = ENOERR;
+
+    size = *len;
+
+    if (pos >= bl->block_size)
+    {
+        bnum += pos >> bl->block_size_log2;
+        pos   = pos & (bl->block_size - 1);
+    }
+        
+    D(("blib read len=%d pos=%d bnum=%d\n", *len, pos, bnum));
+
+    while (size > 0)
+    {
+        int csize;
+
+        if ((size + pos) > bl->block_size)
+            csize = bl->block_size - pos;
+        else
+            csize = size;
+
+        ret = blib_get_block(bl, bnum, true, &block);
+        if (ENOERR != ret)
+            break;
+ 
+        memcpy((void *)bbuf, (void *)(block->data+pos), csize);
+
+        bbuf += csize;
+        size -= csize;
+        pos   = 0;
+        bnum++;
+    }
+    *len -= size;
+    return ret;
+}
+
+int
+cyg_blib_write(cyg_blib_t *bl,
+               const void *buf,
+               cyg_uint32 *len,
+               cyg_uint32  bnum,
+               cyg_uint32  pos)
+{
+    blib_block_t *block;
+    cyg_uint8 *bbuf = (cyg_uint8 * const) buf;
+    cyg_int32  size = *len;
+    Cyg_ErrNo  ret  = ENOERR;
+
+    size = *len;
+
+    if (pos >= bl->block_size)
+    {
+        bnum += pos >> bl->block_size_log2;
+        pos   = pos & (bl->block_size - 1);
+    }
+        
+    D(("blib write len=%d pos=%d bnum=%d\n", *len, pos, bnum));
+
+    while (size > 0)
+    {
+        int csize;
+
+        if ((size + pos) > bl->block_size)
+            csize = bl->block_size - pos;
+        else
+            csize = size;
+
+        if (0 == pos && csize == bl->block_size)
+            ret = blib_get_block(bl, bnum, false, &block);
+        else
+            ret = blib_get_block(bl, bnum, true, &block);
+        if (ENOERR != ret)
+            break;
+
+        memcpy((void *)(block->data+pos), (void *)bbuf, csize);
+        block->modified = true;
+
+        bbuf += csize;
+        size -= csize;
+        pos   = 0;
+        bnum++;
+    }
+    *len -= size;
+    return ret;
+}
+
+int
+cyg_blib_sync(cyg_blib_t *bl)
+{
+    return blib_sync(bl);
+}
+
+int
+cyg_blib_sync_block(cyg_blib_t *bl, cyg_uint32 num)
+{
+    blib_block_t *block = rb_find_block(bl, num);
+    
+    if (NULL != block)
+        return blib_sync_block(bl, block);
+    else
+        return -EINVAL;
+}
+
+int
+cyg_blib_flush(cyg_blib_t *bl)
+{
+    return blib_init_cache(bl, bl->mem_base,  bl->mem_size,
+                           bl->block_size, true);
+}
+
+int
+cyg_blib_set_block_size(cyg_blib_t *bl, cyg_uint32 block_size)
+{
+    return blib_init_cache(bl, bl->mem_base, bl->mem_size, 
+                           block_size, true); 
+}    
+
+int
+cyg_blib_get_stat(cyg_blib_t *bl, cyg_blib_stat_t *stat)
+{
+#ifdef CYGIMP_BLOCK_LIB_STATISTICS
+    *stat = bl->stat;
+    return ENOERR;
+#else
+    stat->n_gets   = 0;
+    stat->n_reads  = 0;
+    stat->n_writes = 0;
+    return -EINVAL;
+#endif
+}
+
+// --------------------------------------------------------------------
+// EOF blib.c