]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
all new code largely related to supporting the fatfs addition and a
authorayer1 <ayer1>
Tue, 16 Mar 2010 15:17:19 +0000 (15:17 +0000)
committerayer1 <ayer1>
Tue, 16 Mar 2010 15:17:19 +0000 (15:17 +0000)
contrib app, justfatlogging.

localtime64 is to keep localtime from wrapping timestamps every 31
hours.

shimmeranalogsetup is a wrapper for old tos-1.x dma/adc stuff to
enable quick app setup and conversion.

powersupply monitor is an old attempt to make use of msp430 utility.

28 files changed:
tos/platforms/shimmer/.platform
tos/platforms/shimmer/Counter32khz64C.nc [new file with mode: 0644]
tos/platforms/shimmer/CounterMilli64C.nc [new file with mode: 0644]
tos/platforms/shimmer/CounterToLocalTime64C.nc [new file with mode: 0644]
tos/platforms/shimmer/LocalTime64.nc [new file with mode: 0644]
tos/platforms/shimmer/Time.h [new file with mode: 0644]
tos/platforms/shimmer/Time.nc [new file with mode: 0644]
tos/platforms/shimmer/TimeC.nc [new file with mode: 0644]
tos/platforms/shimmer/TimeP.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/ds2411/HplDs2411C.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/ds2411/IDChip.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/msp430/HilTimerMilli64C.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.h [new file with mode: 0644]
tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorC.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorP.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/FatFs.h [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/FatFs.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/FatFsP.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/README [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/ccsbcs.c [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/diskIOC.nc [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/diskio.c [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/diskio.h [new file with mode: 0644]
tos/platforms/shimmer/chips/sd/fatfs/integer.h [new file with mode: 0644]
tos/platforms/shimmer/shimmerAnalogSetup.nc [new file with mode: 0644]
tos/platforms/shimmer/shimmerAnalogSetupC.nc [new file with mode: 0644]
tos/platforms/shimmer/shimmerAnalogSetupP.nc [new file with mode: 0644]

index 1a3a7e8db078ae31f566c41005353be606c87274..59f3fe6d85beb62997dee38ff470df7108f2e1df 100644 (file)
@@ -13,8 +13,10 @@ push( @includes, qw(
   %T/platforms/shimmer/chips/cc2420
   %T/platforms/shimmer/chips/mma7260
   %T/platforms/shimmer/chips/sd
+  %T/platforms/shimmer/chips/sd/fatfs
   %T/platforms/shimmer/chips/bluetooth
   %T/platforms/shimmer/chips/msp430
+  %T/platforms/shimmer/chips/ds2411
   %T/chips/cc2420
   %T/chips/cc2420/alarm
   %T/chips/cc2420/control
diff --git a/tos/platforms/shimmer/Counter32khz64C.nc b/tos/platforms/shimmer/Counter32khz64C.nc
new file mode 100644 (file)
index 0000000..e66c8f6
--- /dev/null
@@ -0,0 +1,52 @@
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+/**
+ * Counter32khz32C provides at 32-bit counter at 32768 ticks per second.
+ *
+ * @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ * @see  Please refer to TEP 102 for more information about this component and its
+ *          intended use.
+ */
+
+/*
+ * arguments table for convenience:
+  typedef to_precision_tag,
+  typedef to_size_type @integer(),
+  typedef from_precision_tag,
+  typedef from_size_type @integer(),
+  uint8_t bit_shift_right,
+  typedef upper_count_type @integer()) @safe()
+*/
+configuration Counter32khz64C
+{
+  provides interface Counter<T32khz,uint64_t>;
+}
+implementation
+{
+  components Msp430Counter32khzC as CounterFrom;
+  components new TransformCounterC(T32khz,uint64_t,T32khz,uint16_t,0,uint32_t) as Transform;
+
+  Counter = Transform;
+
+  Transform.CounterFrom -> CounterFrom;
+}
+
diff --git a/tos/platforms/shimmer/CounterMilli64C.nc b/tos/platforms/shimmer/CounterMilli64C.nc
new file mode 100644 (file)
index 0000000..baf14dc
--- /dev/null
@@ -0,0 +1,43 @@
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+/**
+ * CounterMilli32C provides at 32-bit counter at 1024 ticks per second.
+ *
+ * @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ * @see  Please refer to TEP 102 for more information about this component and its
+ *          intended use.
+ */
+    
+configuration CounterMilli64C
+{
+  provides interface Counter<TMilli,uint64_t>;
+}
+implementation
+{
+  components Msp430Counter32khzC as CounterFrom;
+  components new TransformCounterC(TMilli,uint64_t,T32khz,uint16_t,5,uint32_t) as Transform;
+
+  Counter = Transform.Counter;
+
+  Transform.CounterFrom -> CounterFrom;
+}
+
diff --git a/tos/platforms/shimmer/CounterToLocalTime64C.nc b/tos/platforms/shimmer/CounterToLocalTime64C.nc
new file mode 100644 (file)
index 0000000..2774107
--- /dev/null
@@ -0,0 +1,51 @@
+//$Id$
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+#include "Timer.h"
+
+/**
+ * CounterToLocalTimeC converts a 32-bit LocalTime to a Counter.  
+ *
+ * <p>See TEP102 for more details.
+ * @param precision_tag A type indicating the precision of the LocalTime and
+ * Counter being converted.
+ *
+ * @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ */
+
+generic module CounterToLocalTime64C(typedef precision_tag) @safe()
+{
+  provides interface LocalTime64<precision_tag>;
+  uses interface Counter<precision_tag,uint64_t>;
+}
+implementation
+{
+  async command uint64_t LocalTime64.get()
+  {
+    return call Counter.get();
+  }
+
+  async event void Counter.overflow()
+  {
+  }
+}
+
diff --git a/tos/platforms/shimmer/LocalTime64.nc b/tos/platforms/shimmer/LocalTime64.nc
new file mode 100644 (file)
index 0000000..b328a3e
--- /dev/null
@@ -0,0 +1,52 @@
+//$Id$
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+#include "Timer.h"
+
+/**
+ * A LocalTime interface counts time in some units. If you need to detect
+ * time overflow, you should use a component offering the Counter
+ * interface.
+ *
+ * <p>The LocalTime interface is parameterised by its "precision"
+ * (milliseconds, microseconds, etc), identified by a type. This prevents,
+ * e.g., unintentionally mixing components expecting milliseconds with
+ * those expecting microseconds as those interfaces have a different type.
+ *
+ * <p>See TEP102 for more details.
+ *
+ * @param precision_tag A type indicating the precision of this Counter.
+ *
+ * @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ */
+
+interface LocalTime64<precision_tag>
+{
+  /** 
+   * Return current time. Time starts counting at boot - some time sources
+   * may stop counting while the processor is in low-power mode.
+   *
+   * @return Current time.
+   */
+  async command uint64_t get();
+}
+
diff --git a/tos/platforms/shimmer/Time.h b/tos/platforms/shimmer/Time.h
new file mode 100644 (file)
index 0000000..9f2b17d
--- /dev/null
@@ -0,0 +1,29 @@
+typedef uint32_t time_t;
+
+#define HAVE_WDAY
+#undef HAVE_DST
+
+struct tm
+{
+  int tm_sec;                  /* Seconds.     [0-60] (1 leap second) */
+  int tm_min;                  /* Minutes.     [0-59] */
+  int tm_hour;                 /* Hours.       [0-23] */
+  int tm_mday;                 /* Day.         [1-31] */
+  int tm_mon;                  /* Month.       [0-11] */
+  int tm_year;                 /* Year - 1900.  */
+#ifdef HAVE_WDAY
+  int tm_wday;                 /* Day of week. [0-6] */
+#endif
+  int tm_yday;                 /* Days in year.[0-365] */
+  int tm_isdst;                        /* DST.         [-1/0/1]*/
+
+#ifdef __UCLIBC_HAS_TM_EXTENSIONS__
+#ifdef __USE_BSD
+  long int tm_gmtoff;          /* Seconds east of UTC.  */
+  __const char *tm_zone;       /* Timezone abbreviation. */
+#else
+  long int __tm_gmtoff;                /* Seconds east of UTC.  */
+  __const char *__tm_zone;     /* Timezone abbreviation. */
+#endif
+#endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
+};
diff --git a/tos/platforms/shimmer/Time.nc b/tos/platforms/shimmer/Time.nc
new file mode 100644 (file)
index 0000000..76e2a01
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2005 Hewlett-Packard Company
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of the Hewlett-Packard Company nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "Time.h"
+
+interface Time {
+  command error_t gmtime(const time_t *timer, struct tm *tm);
+  command error_t localtime(const time_t *timer, struct tm *tm);
+  command error_t asctime(const struct tm *tm, char *buf, int buflen);
+  command error_t time(time_t *timer);
+  event void tick();
+}
diff --git a/tos/platforms/shimmer/TimeC.nc b/tos/platforms/shimmer/TimeC.nc
new file mode 100644 (file)
index 0000000..617752b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Steve Ayer
+ * @date March, 2010
+ * 
+ * pulling together time modules
+ */
+
+configuration TimeC {
+  provides{
+    interface Time;
+    interface Init;
+  }
+}
+
+implementation { 
+  components TimeP;
+  Init = TimeP;
+  Time = TimeP;
+
+  components new TimerMilliC() as LocalTimer;
+  TimeP.Timer -> LocalTimer;
+
+  components Counter32khz64C as Counter;
+  components new CounterToLocalTime64C(T32khz);
+  CounterToLocalTime64C.Counter -> Counter;
+  TimeP.LocalTime64 -> CounterToLocalTime64C;
+  
+  /* 
+   * and, at some point...
+   *  TimeP.NTPClient            -> NTPClientM;   not yet, awaiting tos-2.x ip port
+   */
+
+}
+
diff --git a/tos/platforms/shimmer/TimeP.nc b/tos/platforms/shimmer/TimeP.nc
new file mode 100644 (file)
index 0000000..5340939
--- /dev/null
@@ -0,0 +1,320 @@
+
+
+/*  Copyright (C) 2002     Manuel Novoa III
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/**
+ * port to tos-2.x
+ * @author Steve Ayer
+ * @date   January, 2010
+ */
+
+// includes InfoMem;  let's avoid this...
+
+module TimeP {
+  provides {
+    interface Init;
+    interface Time;
+    //    interface ParamView;    save for ip stack port to tos-2.x
+  }
+  uses {
+    interface Timer<TMilli>;
+    //    interface NTPClient;  save for ip stack port to tos-2.x
+    interface LocalTime64<T32khz>;
+  }
+}
+
+implementation {
+
+#define HAVE_DST
+extern int snprintf(char *str, size_t len, const char *format, ...) __attribute__ ((C));
+
+  //#define TZNAME_MAX 7
+  //#define LONG_MAX 0x7fffffffL
+  time_t g_tick_local_time;
+#ifdef CURRENT_TIME  
+  time_t g_current_time = CURRENT_TIME;
+#else
+  time_t g_current_time;
+#endif
+  //  time_t g_local_time; /* from LocalTime.read() */
+  uint64_t g_local_time; /* from LocalTime.read(), now 64 bits to handle */
+
+  typedef struct {
+    long gmt_offset;
+    //    long dst_offset;
+    //    short day;                                   /* for J or normal */
+    //    short week;
+    //    short month;
+    //    short rule_type;                     /* J, M, \0 */
+    //    char tzname[TZNAME_MAX+1];
+  } rule_struct;
+
+  rule_struct _time_tzinfo[1];
+
+  command error_t Init.init() {
+    _time_tzinfo[0].gmt_offset = 60L * 0L;
+
+    g_tick_local_time = call LocalTime64.get();
+    call Timer.startPeriodic(10*1024L);
+    signal Time.tick();
+    return SUCCESS;
+  }
+
+  void dotick(int force) {
+    time_t tick = call LocalTime64.get();
+    if (force || tick >= (g_tick_local_time + 32768L*10)) {
+      signal Time.tick();
+      g_tick_local_time = tick;
+    }
+  }
+  event void Timer.fired() {
+    dotick(0);
+  }
+
+  /*  
+   * save this pending tos-2.x port of ip stack
+   *
+   event void NTPClient.timestampReceived( uint32_t *seconds, uint32_t *fraction ) {
+   g_current_time = *seconds;
+   g_local_time = call LocalTime64.get();
+   dotick(1);
+   }
+  */
+
+  struct tm __time_tm;
+
+  /* Notes:
+   * If time_t is 32 bits, then no overflow is possible.
+   * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
+   */
+
+  /* Note: offset is the correction in _days_ to *timer! */
+  static const uint16_t vals[] = {
+    60, 60, 24, 7 /* special */, 36524u, 1461, 365, 0
+  };
+
+  static const int8_t days_per_month[] = {
+    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
+    29,
+  };
+
+#ifdef __UCLIBC_HAS_TM_EXTENSIONS__
+  static const char utc_string[] = "UTC";
+#endif
+
+  #define NUM_YEARS 4
+  /*
+    import time
+    for y in range(2004,2008):
+       t = time.mktime((y, 1, 1, 0, 0, 0, 0, 0, 0))-5*3600
+       print time.gmtime(t)
+    
+    (2004, 1, 1, 0, 0, 0, 3, 1, 0)
+    (2005, 1, 1, 0, 0, 0, 5, 1, 0)
+    (2006, 1, 1, 0, 0, 0, 6, 1, 0)
+    (2007, 1, 1, 0, 0, 0, 0, 1, 0)
+    from below in year_info struct for 2004-7
+    { 0x3ff36300, 3, 1 },
+    { 0x41d5e800, 5, 0 },
+    { 0x43b71b80, 6, 0 },
+    { 0x45984f00, 0, 0 }
+    
+    NTP Timestamp starts Jan 1, 1900 and is 2208988800 jan 1 1970
+    '%x' % (int(time.mktime((2004, 1, 1, 0, 0, 0, 3, 1, 0))) -18000 + 2208988800l)
+    
+    and now, 
+    
+    (2009, 1, 1, 0, 0, 0, 3, 1, 0)
+    (2010, 1, 1, 0, 0, 0, 4, 1, 0)
+    (2011, 1, 1, 0, 0, 0, 5, 1, 0)
+    (2012, 1, 1, 0, 0, 0, 6, 1, 0)
+    
+  */
+
+  const int16_t g_first_year = 2009;
+  const struct {
+    time_t year_seconds; /* time_t value for beginning of year */
+    int8_t wday_offset;   /* tm_day of first day of the year */
+    int8_t isleap;
+    int16_t dst_first_yday;
+    int16_t dst_last_yday;
+  } year_info[NUM_YEARS] = {
+    /* unix time start jan 1 1970 */
+    // from 2007, dst begins 2nd sunday in march, ends first sunday in november
+    { 0x495c4dd0, 3, 0, 67, 305},
+    { 0x4B3D8150, 4, 0, 73, 311},
+    { 0x4D1EB4D0, 5, 0, 72, 310},
+    { 0x4EFFE850, 6, 1, 71, 309}
+  };
+  time_t g_seconds;
+  time_t g_year_seconds;
+  time_t g_seconds_from_year;
+  uint16_t g_days;
+  time_t g_seconds_from_day;
+
+  struct tm *_time_t2tm(const time_t *timer, int localtime, struct tm *result)
+  {
+    uint16_t seconds16;
+    int i;
+    time_t seconds = *timer;
+    uint16_t days;
+    uint16_t hour, min;
+    int isleap = 0;
+
+    //if year_info has dst_first_yday and dst_last_yday defined, then HAVE_DST should be defined up top.
+#ifdef HAVE_DST
+    int isdst = 0;
+#endif
+
+    if (localtime) {
+      seconds -= _time_tzinfo[0].gmt_offset;
+    }
+
+    g_seconds = seconds;
+    if (seconds < year_info[0].year_seconds) {
+      memset(result, 0, sizeof(struct tm));
+      return NULL;
+    }
+    for (i = 0; i < NUM_YEARS-1; i++) {
+      if (seconds < year_info[i+1].year_seconds) {
+       seconds -= year_info[i].year_seconds;
+       result->tm_year = g_first_year + i;
+       isleap = year_info[i].isleap;
+       break;
+      }
+    }
+    g_year_seconds = year_info[i].year_seconds;
+    g_seconds_from_year = seconds;
+    days = seconds / 86400L;
+    g_days = days;
+    seconds -= days * 86400L;
+    g_seconds_from_day = seconds;
+#ifdef HAVE_DST
+    if (days >= year_info[i].dst_first_yday && 
+       days <= year_info[i].dst_last_yday) {
+      isdst = 1;
+      seconds += 3600;
+      if (seconds < 0) {
+       days--;
+       seconds += 86400L;
+      }
+      if (days < 0) {
+       result->tm_year--;
+       days += 365;
+      }
+    }
+#endif /* HAVE_DST */
+
+    result->tm_yday = days; 
+#ifdef HAVE_WDAY
+    result->tm_wday = (result->tm_yday + year_info[i+1].wday_offset) % 7;
+#endif
+
+    for (i = 0; i < 12; i++) {
+      int8_t dpm = days_per_month[i];
+      if (i == 1 && isleap) dpm++;
+      if (days < dpm)
+       break;
+      days -= dpm;
+    }
+    result->tm_mon = i;
+    result->tm_mday = 1 + days ;
+
+    hour = seconds / 3600;
+    seconds16 = seconds - hour * 3600;
+    result->tm_hour = hour;
+    min = seconds16 / 60;
+    result->tm_sec = seconds16 - min * 60;
+    result->tm_min = min;
+    return result;
+  }
+
+  command error_t Time.gmtime(const time_t *timer, struct tm *ptm)
+  {
+
+    _time_t2tm(timer, 0, ptm); /* Can return NULL... */
+
+    return SUCCESS;
+  }
+
+  static const unsigned char day_cor[] = { /* non-leap */
+    31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
+    /*          0,  0,  3,  3,  4,  4,  5,  5,  5,  6,  6,  7,  7 */
+    /*     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
+  };
+
+  /* Note: timezone locking is done by localtime_r. */
+
+  static int tm_isdst(register const struct tm *__restrict ptm) {
+    // no DST in arizona
+    return 0;
+  }
+    
+  command error_t Time.localtime(const time_t *timer, struct tm *result)
+  {
+    _time_t2tm(timer, 1, result);
+
+    return SUCCESS;
+  }
+  static char *wday_name[] = { "Sun", "Mon", "Tues", "Wed", "Thur", "Fri", "Sat" };
+  static char *mon_name[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+  command error_t Time.asctime(const struct tm *tm, char *buf, int buflen)
+  {
+    char *out = buf;
+    char *outmax = buf + buflen;
+    out += snprintf(out, outmax - out, "%02d:%02d:%02d",
+                   tm->tm_hour, tm->tm_min, tm->tm_sec);
+#ifdef HAVE_WDAY
+    out += snprintf(out, outmax - out, " %s", wday_name[tm->tm_wday]);
+#endif
+    out += snprintf(out, outmax - out, " %s %d %d",
+                   mon_name[tm->tm_mon], tm->tm_mday, tm->tm_year);
+    return SUCCESS;
+  }
+
+  command error_t Time.time(time_t *timer)
+  {
+    // 1/32768 seconds since last NTP response
+    uint64_t o = call LocalTime64.get() - g_local_time;
+
+    *timer = g_current_time + (time_t)(o >> 15);
+    
+    return SUCCESS;
+  }
+
+  default event void Time.tick() { }
+
+  /*****************************************
+   *  ParamView interface
+   * save for ip stack port to tos-2.x   
+
+  const struct Param s_Time[] = {
+    { "ntp time", PARAM_TYPE_UINT32, &g_current_time },
+    { "local time", PARAM_TYPE_UINT32, &g_local_time },
+    { NULL, 0, NULL }
+  };
+
+  struct ParamList g_TimeList = { "time", &s_Time[0] };
+
+  command error_t ParamView.init()
+  {
+    signal ParamView.add( &g_TimeList );
+    return SUCCESS;
+  }
+  *****************************************/
+
+}
diff --git a/tos/platforms/shimmer/chips/ds2411/HplDs2411C.nc b/tos/platforms/shimmer/chips/ds2411/HplDs2411C.nc
new file mode 100644 (file)
index 0000000..1bec79d
--- /dev/null
@@ -0,0 +1,206 @@
+//$Id$
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+//@author Cory Sharp <cssharp@eecs.berkeley.edu>
+
+
+/*
+  
+The 1-wire timings suggested by the DS2411 data sheet are incorrect,
+incomplete, or unclear.  The timings provided the app note 522 work:
+
+http://www.maxim-ic.com/appnotes.cfm/appnote_number/522
+    
+*/
+
+/*
+  This is a stripped version of the DS2411 driver modified for our network stack.
+  Instead of a nice abstraction layer, we rely on TOSH_ASSIGN_PIN(ONEWIRE) in the
+  hardware.h file to set up the appropriate pin for communications.
+
+  Andrew Christian <andrew.christian@hp.com>
+  June 2005
+  
+  * "ported" to tinyos-2.x
+  * @author Steve Ayer
+  * @date   March, 2010
+  */
+
+module HplDs2411C
+{
+  provides interface IDChip;
+}
+implementation
+{
+  enum
+  {
+    STD_A = 6,
+    STD_B = 64,
+    STD_C = 60,
+    STD_D = 10,
+    STD_E = 9,
+    STD_F = 55,
+    STD_G = 0,
+    STD_H = 480,
+    STD_I = 90,
+    STD_J = 220,
+  };
+
+  void init_pins()
+  {
+#ifdef ID_CHIP_POWER
+    TOSH_SET_ONEWIRE_POWER_PIN();    
+    TOSH_MAKE_ONEWIRE_POWER_OUTPUT();
+#endif
+
+    TOSH_SEL_ONEWIRE_IOFUNC();
+    TOSH_MAKE_ONEWIRE_INPUT();
+    TOSH_CLR_ONEWIRE_PIN();
+  }
+
+  void clear_pins()
+  {
+#ifdef TOSH_SET_ONEWIRE_POWER_PIN
+    TOSH_CLR_ONEWIRE_POWER_OUTPUT();
+#endif
+    // Don't need to fix ONEWIRE...it finishes as an INPUT
+  }
+
+  bool reset() // >= 960us
+  {
+    int present;
+    TOSH_MAKE_ONEWIRE_OUTPUT();
+    TOSH_uwait(STD_H); //t_RSTL
+    TOSH_MAKE_ONEWIRE_INPUT();
+    TOSH_uwait(STD_I);  //t_MSP
+    present = TOSH_READ_ONEWIRE_PIN();
+    TOSH_uwait(STD_J);  //t_REC
+    return (present == 0);
+  }
+
+  void write_bit_one() // >= 70us
+  {
+    TOSH_MAKE_ONEWIRE_OUTPUT();
+    TOSH_uwait(STD_A);  //t_W1L
+    TOSH_MAKE_ONEWIRE_INPUT();
+    TOSH_uwait(STD_B);  //t_SLOT - t_W1L
+  }
+
+  void write_bit_zero() // >= 70us
+  {
+    TOSH_MAKE_ONEWIRE_OUTPUT();
+    TOSH_uwait(STD_C);  //t_W0L
+    TOSH_MAKE_ONEWIRE_INPUT();
+    TOSH_uwait(STD_D);  //t_SLOT - t_W0L
+  }
+
+  void write_bit( int is_one ) // >= 70us
+  {
+    if(is_one)
+      write_bit_one();
+    else
+      write_bit_zero();
+  }
+
+  bool read_bit() // >= 70us
+  {
+    int bit;
+    TOSH_MAKE_ONEWIRE_OUTPUT();
+    TOSH_uwait(STD_A);  //t_RL
+    TOSH_MAKE_ONEWIRE_INPUT();
+    TOSH_uwait(STD_E); //near-max t_MSR
+    bit = TOSH_READ_ONEWIRE_PIN();
+    TOSH_uwait(STD_F);  //t_REC
+    return bit;
+  }
+
+  void write_byte( uint8_t byte ) // >= 560us
+  {
+    uint8_t bit;
+    for( bit=0x01; bit!=0; bit<<=1 )
+      write_bit( byte & bit );
+  }
+
+  uint8_t read_byte() // >= 560us
+  {
+    uint8_t byte = 0;
+    uint8_t bit;
+    for( bit=0x01; bit!=0; bit<<=1 )
+    {
+      if( read_bit() )
+       byte |= bit;
+    }
+    return byte;
+  }
+
+  uint8_t crc8_byte( uint8_t crc, uint8_t byte )
+  {
+    int i;
+    crc ^= byte;
+    for( i=0; i<8; i++ )
+    {
+      if( crc & 1 )
+        crc = (crc >> 1) ^ 0x8c;
+      else
+        crc >>= 1;
+    }
+    return crc;
+  }
+
+  /*
+   * Reset the DS2411 chip and read the 8 bytes of data out.  
+   * We verify the CRC to ensure good data, dump the family byte (it should be '1')
+   * and fill a buffer with the 6 good uniqut address bytes.
+   *
+   * It is possible for the initialization to fail.
+   */
+
+  command error_t IDChip.read( uint8_t *id_buf ) // >= 6000us
+  {
+    int retry = 5;
+    uint8_t id[8];
+
+    init_pins();
+    TOSH_uwait( 1200 );    // Delay a bit at start up (as per DS2411 data sheet)
+
+    while( retry-- > 0 ) {
+      int crc = 0;
+      if( reset() ) {
+       uint8_t* byte;
+
+       write_byte(0x33); //read rom
+       for( byte=id+7; byte!=id-1; byte-- )
+         crc = crc8_byte( crc, *byte=read_byte() );
+
+       if( crc == 0 ) {
+         memcpy( id_buf, id + 1, 6 );
+         clear_pins();
+         return SUCCESS;
+       }
+      }
+    }
+
+    clear_pins();
+    return FAIL;
+  }
+}
+
diff --git a/tos/platforms/shimmer/chips/ds2411/IDChip.nc b/tos/platforms/shimmer/chips/ds2411/IDChip.nc
new file mode 100644 (file)
index 0000000..1ed9db7
--- /dev/null
@@ -0,0 +1,35 @@
+//$Id$
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+//@author Cory Sharp <cssharp@eecs.berkeley.edu>
+
+/*
+ * This is a stripped version of the DS2411 driver that provides very 
+ * limited functionality.  Generally you should call this at start up and
+ * use the result to fill in the CC2420 Radio's long address.
+ */
+
+interface IDChip
+{
+  command error_t read( uint8_t *id_buf ); // Fills in 6 bytes if successful.
+}
+
diff --git a/tos/platforms/shimmer/chips/msp430/HilTimerMilli64C.nc b/tos/platforms/shimmer/chips/msp430/HilTimerMilli64C.nc
new file mode 100644 (file)
index 0000000..9d6d11f
--- /dev/null
@@ -0,0 +1,53 @@
+
+/* "Copyright (c) 2000-2003 The Regents of the University of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement
+ * is hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY
+ * OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ */
+
+/**
+ * HilTimerMilliC provides a parameterized interface to a virtualized
+ * millisecond timer.  TimerMilliC in tos/system/ uses this component to
+ * allocate new timers.
+ *
+ * @author Cory Sharp <cssharp@eecs.berkeley.edu>
+ * @see  Please refer to TEP 102 for more information about this component and its
+ *          intended use.
+ */
+
+configuration HilTimerMilli64C
+{
+  provides interface Init;
+  provides interface Timer<TMilli> as TimerMilli[ uint8_t num ];
+  provides interface LocalTime<TMilli>;
+}
+implementation
+{
+  components new AlarmMilli32C();
+  components new AlarmToTimerC(TMilli);
+  components new VirtualizeTimerC(TMilli,uniqueCount(UQ_TIMER_MILLI));
+  components new CounterToLocalTimeC(TMilli);
+  components CounterMilli64C;
+
+  Init = AlarmMilli32C;
+  TimerMilli = VirtualizeTimerC;
+  LocalTime = CounterToLocalTimeC;
+
+  VirtualizeTimerC.TimerFrom -> AlarmToTimerC;
+  AlarmToTimerC.Alarm -> AlarmMilli32C;
+  CounterToLocalTime64C.Counter -> CounterMilli64C;
+}
diff --git a/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.h b/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.h
new file mode 100644 (file)
index 0000000..8b063f5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007, Intel Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, 
+ * this list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution. 
+ *
+ * Neither the name of the Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software 
+ * without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author  Steve Ayer
+ * @date    March, 2007
+ *
+ * enum to set svs thresholds, presumably more meaningful than "vld_n"?
+ */
+
+// as in 1.9v, 2.1v, etc.
+enum {
+  OFF,
+  ONE_9V =  VLD_1,    
+  TWO_1V =  VLD_2,
+  TWO_2V =  VLD_3,
+  TWO_3V =  VLD_4,
+  TWO_4V =  VLD_5,
+  TWO_5V =  VLD_6,
+  TWO_65V = VLD_7,
+  TWO_8V =  VLD_8,
+  TWO_9V =  VLD_9,
+  THREE_05V = VLD_10,
+  THREE_2V =  VLD_11,
+  THREE_35V = VLD_12,
+  THREE_5V =  VLD_13,
+  THREE_7V =  VLD_14,
+  EXTERNAL =  VLD_EXT
+};
diff --git a/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.nc b/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitor.nc
new file mode 100644 (file)
index 0000000..f1ecf88
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2007, Intel Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, 
+ * this list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution. 
+ *
+ * Neither the name of the Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software 
+ * without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author  Steve Ayer
+ * @date    March, 2007
+ *
+ * simple interface to msp430 supply voltage supervisor register
+ */
+
+interface PowerSupplyMonitor{
+
+  command void resetOnLowVoltage(bool reset);
+
+  /* enum in PowerSupplyVoltage.h */
+  command void setVoltageThreshold(uint8_t threshold);
+
+  /* polling for low voltage condtion */
+  command void setMonitorInterval(uint32_t interval_ms);
+
+  command void stopVoltageMonitor();
+
+  command error_t isSupplyMonitorEnabled();
+
+  /* manual query */
+  command error_t queryLowVoltageCondition();
+
+  /* flag is immediately reset unless power problem is remedied */
+  command void clearLowVoltageCondition();
+
+  command void disable();
+
+  /* triggered by polling mechanism */
+  event void voltageThresholdReached(uint8_t t);
+}
diff --git a/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorC.nc b/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorC.nc
new file mode 100644 (file)
index 0000000..4c75d8e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2007, Intel Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, 
+ * this list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution. 
+ *
+ * Neither the name of the Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software 
+ * without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  Author:  Steve Ayer
+ *           March, 2007
+ */
+
+
+configuration PowerSupplyMonitorC {
+  provides {
+    interface Init;
+    interface StdControl;
+    interface PowerSupplyMonitor;
+  }
+}
+implementation {
+  components PowerSupplyMonitorP, new TimerMilliC() as Timer, LedsC;
+
+  Init = PowerSupplyMonitorP;
+  StdControl = PowerSupplyMonitorP;
+  PowerSupplyMonitor = PowerSupplyMonitorP;
+
+  PowerSupplyMonitorP.Timer -> Timer;
+  PowerSupplyMonitorP.Leds  -> LedsC;
+}
diff --git a/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorP.nc b/tos/platforms/shimmer/chips/msp430/PowerSupplyMonitorP.nc
new file mode 100644 (file)
index 0000000..d933bb2
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2007, Intel Corporation
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, 
+ * this list of conditions and the following disclaimer. 
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution. 
+ *
+ * Neither the name of the Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software 
+ * without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author  Steve Ayer
+ * @date    March, 2007
+ *
+ * support for interface to msp430 SVSCTL functionality
+ */
+
+//#include "PowerSupplyMonitor.h"
+
+module PowerSupplyMonitorP {
+  provides {
+    interface Init;
+    interface StdControl;
+    interface PowerSupplyMonitor;
+  }
+  uses{
+    interface Timer<TMilli> as Timer;
+    interface Leds;
+  }
+}
+
+implementation {
+  uint8_t threshold;
+  uint32_t monitor_interval;
+  bool started, spurious_check;
+
+  /* 
+   * sets svs to first voltage level beneath regulator (2.9v), 
+   * no reset, 15 minute polling 
+   */
+  void init() {
+    threshold = THREE_7V;
+    monitor_interval = 900000;  
+
+    SVSCTL = 0;
+    //    CLR_FLAG(SVSCTL, 0xff);
+  }
+  
+  /* turns on svs to highest voltage level (3.7v), no reset */
+  command error_t Init.init(){
+    started = FALSE;
+    spurious_check = FALSE;
+
+    init();
+    
+    return SUCCESS;
+  }
+
+  command error_t StdControl.start(){
+    CLR_FLAG(SVSCTL, VLD_EXT);
+    SET_FLAG(SVSCTL, threshold);
+
+    started = TRUE;
+    call Timer.startPeriodic(monitor_interval);
+    return SUCCESS;
+  }
+
+  command error_t StdControl.stop(){
+    call PowerSupplyMonitor.disable();
+
+    started = FALSE;
+    return SUCCESS;
+  }
+
+
+  task void spurious_test(){
+    call Timer.stop();
+    started = FALSE;
+
+    atomic{
+      CLR_FLAG(SVSCTL, VLD_EXT);  // set vld to zero to stop detection
+      CLR_FLAG(SVSCTL, SVSFG);
+    }
+
+    spurious_check = TRUE;
+    call Timer.startOneShot(5000);
+  }
+
+  task void recheck(){
+    spurious_check = FALSE;
+
+    SET_FLAG(SVSCTL, threshold);  // set vld back to threshold, restart detection
+
+    TOSH_uwait(1000);  // wait for svs to settle back down (max time ~ 50us, but hey)
+
+    atomic if(READ_FLAG(SVSCTL, SVSFG)){
+      CLR_FLAG(SVSCTL, threshold);  // set vld back to threshold, restart detection
+      signal PowerSupplyMonitor.voltageThresholdReached(threshold);
+    }
+  }
+      
+  event void Timer.fired(){
+    if(spurious_check)
+      post recheck();
+    else{
+      atomic if(READ_FLAG(SVSCTL, SVSFG))
+       post spurious_test();
+    }
+  }
+
+  command void PowerSupplyMonitor.resetOnLowVoltage(bool reset){
+    if(reset)
+      SET_FLAG(SVSCTL, PORON);
+    else
+      CLR_FLAG(SVSCTL, PORON);
+  }
+
+  /* enum in PowerSupplyVoltage.h for this */
+  command void PowerSupplyMonitor.setVoltageThreshold(uint8_t t){
+    threshold = t;
+    if(started){
+      CLR_FLAG(SVSCTL, VLD_EXT);  
+      SET_FLAG(SVSCTL, t);
+    }
+  }
+
+  command error_t PowerSupplyMonitor.isSupplyMonitorEnabled(){
+    return READ_FLAG(SVSCTL, SVSON);
+  }
+
+  command error_t PowerSupplyMonitor.queryLowVoltageCondition(){
+    return READ_FLAG(SVSCTL, SVSFG);
+  }
+
+
+  command void PowerSupplyMonitor.setMonitorInterval(uint32_t interval_ms){
+    monitor_interval = interval_ms;
+    if(started){
+      call Timer.stop();
+      call Timer.startPeriodic(monitor_interval);
+    }
+  }
+
+  command void PowerSupplyMonitor.clearLowVoltageCondition(){
+    CLR_FLAG(SVSCTL, SVSFG);
+  }
+
+  command void PowerSupplyMonitor.stopVoltageMonitor() {
+    call Timer.stop();
+  }
+
+  command void PowerSupplyMonitor.disable(){
+    CLR_FLAG(SVSCTL, 0xf0);
+
+    started = FALSE;
+
+    call Timer.stop();
+  }
+}
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/FatFs.h b/tos/platforms/shimmer/chips/sd/fatfs/FatFs.h
new file mode 100644 (file)
index 0000000..42fc896
--- /dev/null
@@ -0,0 +1,774 @@
+/*----------------------------------------------------------------------------/\r
+  FatFs module is an open source project to implement FAT file system to small\r
+  embedded systems. It is opened for education, research and development under\r
+  license policy of following trems.\r
+\r
+  Copyright (C) 2009, ChaN, all right reserved.\r
+\r
+  * The FatFs module is a free software and there is no warranty.\r
+  * You can use, modify and/or redistribute it for personal, non-profit or\r
+  commercial use without any restriction under your responsibility.\r
+  * Redistributions of source code must retain the above copyright notice.\r
+\r
+  ----------------------------------------------------------------------------*/\r
+/*\r
+ * Portions of this code:\r
+ *\r
+ * Copyright (c) 2009, Shimmer Research, Ltd.\r
+ * All rights reserved\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+\r
+ *     * Redistributions of source code must retain the above copyright\r
+ *       notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above\r
+ *       copyright notice, this list of conditions and the following\r
+ *       disclaimer in the documentation and/or other materials provided\r
+ *       with the distribution.\r
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its\r
+ *       contributors may be used to endorse or promote products derived\r
+ *       from this software without specific prior written permission.\r
+\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * tinyos port of ChaN's FatFs module (thank you!):\r
+ *\r
+ * @author Steve Ayer\r
+ * @date   April, 2009\r
+ * ported to tos-2.x\r
+ * @date January, 2010\r
+ */\r
+\r
+#include "integer.h"\r
+\r
+/*---------------------------------------------------------------------------/\r
+/  FatFs - FAT file system module configuration file  R0.07e  (C)ChaN, 2009\r
+           ffconf.h\r
+\r
+   NOTE: these are not broken out to ffconf.h in tinyos for maintenance reasons\r
+         that make sense right now...\r
+/----------------------------------------------------------------------------*/\r
+#ifndef _FATFS\r
+#define _FATFS 0x007E\r
+\r
+#define REALTIME       1\r
+\r
+#if REALTIME\r
+#warning "Using enhanced/optimized version of FatFs............."\r
+#endif\r
+\r
+/*---------------------------------------------------------------------------/\r
+/ Function and Buffer Configurations\r
+/----------------------------------------------------------------------------*/\r
+#define        _FS_TINY        0\r
+/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system\r
+   /  object instead of the sector buffer in the individual file object for file\r
+   /  data transfer. This reduces memory consumption 512 bytes each file object. */\r
+\r
+#define _FS_READONLY   0\r
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes\r
+   /  writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,\r
+   /  f_truncate and useless f_getfree. */\r
+\r
+\r
+#define _FS_MINIMIZE   0\r
+/* The _FS_MINIMIZE option defines minimization level to remove some functions.\r
+   /\r
+   /   0: Full function.\r
+   /   1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename\r
+   /      are removed.\r
+   /   2: f_opendir and f_readdir are removed in addition to level 1.\r
+   /   3: f_lseek is removed in addition to level 2. */\r
+\r
+\r
+\r
+#define        _USE_STRFUNC    1\r
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */\r
+\r
+\r
+#define        _USE_MKFS       1\r
+/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */\r
+\r
+\r
+#define        _USE_FORWARD    0\r
+/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */\r
+\r
+\r
+/*---------------------------------------------------------------------------/\r
+/ Locale and Namespace Configurations\r
+/----------------------------------------------------------------------------*/\r
+#define _CODE_PAGE     437\r
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.\r
+   /\r
+   /   932  - Japanese Shift-JIS (DBCS, OEM, Windows)\r
+   /   936  - Simplified Chinese GBK (DBCS, OEM, Windows)\r
+   /   949  - Korean (DBCS, OEM, Windows)\r
+   /   950  - Traditional Chinese Big5 (DBCS, OEM, Windows)\r
+   /   1250 - Central Europe (Windows)\r
+   /   1251 - Cyrillic (Windows)\r
+   /   1252 - Latin 1 (Windows)\r
+   /   1253 - Greek (Windows)\r
+   /   1254 - Turkish (Windows)\r
+   /   1255 - Hebrew (Windows)\r
+   /   1256 - Arabic (Windows)\r
+   /   1257 - Baltic (Windows)\r
+   /   1258 - Vietnam (OEM, Windows)\r
+   /   437  - U.S. (OEM)\r
+   /   720  - Arabic (OEM)\r
+   /   737  - Greek (OEM)\r
+   /   775  - Baltic (OEM)\r
+   /   850  - Multilingual Latin 1 (OEM)\r
+   /   858  - Multilingual Latin 1 + Euro (OEM)\r
+   /   852  - Latin 2 (OEM)\r
+   /   855  - Cyrillic (OEM)\r
+   /   866  - Russian (OEM)\r
+   /   857  - Turkish (OEM)\r
+   /   862  - Hebrew (OEM)\r
+   /   874  - Thai (OEM, Windows)\r
+   /   1 - ASCII (Valid for only non LFN cfg.)\r
+*/\r
+\r
+#define        _USE_LFN        1\r
+#define        _MAX_LFN        255             /* Maximum LFN length to handle (max:255) */\r
+/* The _USE_LFN option switches the LFN support.\r
+   /\r
+   /   0: Disable LFN.\r
+   /   1: Enable LFN with static working buffer on the bss. Not re-entrant.\r
+   /   2: Enable LFN with dynamic working buffer on the caller's 'stack'.\r
+   /\r
+   /  The working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN,\r
+   /  a Unicode - OEM code conversion function ff_convert() must be linked. */\r
+\r
+\r
+#define        _LFN_UNICODE    0       /* 0 or 1 */\r
+/* To switch the character code set on FatFs API to Unicode,\r
+/  enable LFN feature and set _LFN_UNICODE to 1.\r
+*/\r
+\r
+#define _FS_RPATH      1\r
+/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir,\r
+   /  f_chdrive function are available.\r
+   /  Note that output of the f_readdir fnction is affected by this option. */\r
+\r
+\r
+/*---------------------------------------------------------------------------/\r
+/ Physical Drive Configurations\r
+/----------------------------------------------------------------------------*/\r
+#define _DRIVES                1\r
+/* Number of volumes (logical drives) to be used. */\r
+\r
+\r
+#define        _MAX_SS 512\r
+/* Maximum sector size to be handled. (512/1024/2048/4096) */\r
+/* 512 for memroy card and hard disk, 1024 for floppy disk, 2048 for MO disk */\r
+\r
+#define        _MULTI_PARTITION        0\r
+/* When _MULTI_PARTITION is set to 0, each volume is bound to same physical\r
+   / drive number and can mount only 1st primaly partition. When it is set to 1,\r
+   / each volume is tied to the partition listed in Drives[]. */\r
+\r
+\r
+/*---------------------------------------------------------------------------/\r
+/ System Configurations\r
+/----------------------------------------------------------------------------*/\r
+#define _WORD_ACCESS   0\r
+/* The _WORD_ACCESS option defines which access method is used to the word\r
+   /  data in the FAT structure.\r
+   /\r
+   /   0: Byte-by-byte access. Always compatible with all platforms.\r
+   /   1: Word access. Do not choose this unless following condition is met.\r
+   /\r
+   /  When the byte order on the memory is big-endian or address miss-aligned\r
+   /  word access results incorrect behavior, the _WORD_ACCESS must be set to 0.\r
+   /  If it is not the case, the value can also be set to 1 to improve the\r
+   /  performance and code efficiency. */\r
+\r
+#define _FS_REENTRANT  0\r
+#define _TIMEOUT               1000\r
+#define        _SYNC_t                 HANDLE  /* Type of sync object used on the OS. e.g. HANDLE, OS_EVENT*, ID and etc.. */\r
+/* To make the FatFs module re-entrant, set _FS_REENTRANT to 1 and add user\r
+   /  provided synchronization handlers, ff_req_grant, ff_rel_grant, ff_del_syncobj\r
+   /  and ff_cre_syncobj function to the project. */\r
+\r
+\r
+/* End of configuration options. Do not change followings without care.     */\r
+/*--------------------------------------------------------------------------*/\r
+\r
+\r
+#if _MAX_SS == 512\r
+#define        SS(fs)  512\r
+#else\r
+#if _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096\r
+#define        SS(fs)  ((fs)->s_size)\r
+#else\r
+#error Sector size must be 512, 1024, 2048 or 4096.\r
+#endif\r
+#endif\r
+\r
+/* Type of file name on FatFs API */\r
+\r
+#if _LFN_UNICODE && _USE_LFN\r
+typedef WCHAR XCHAR;   /* Unicode */\r
+#else\r
+typedef char XCHAR;            /* SBCS, DBCS */\r
+#endif\r
+\r
+\r
+/* File system object structure */\r
+\r
+typedef struct _FATFS_ {\r
+  BYTE   fs_type;      /* FAT sub type */\r
+  BYTE   drive;        /* Physical drive number */\r
+  BYTE   csize;        /* Number of sectors per cluster */\r
+  BYTE   n_fats;       /* Number of FAT copies */\r
+  BYTE   wflag;        /* win[] dirty flag (1:must be written back) */\r
+  BYTE   fsi_flag;     /* fsinfo dirty flag (1:must be written back) */\r
+  WORD   id;           /* File system mount ID */\r
+  WORD   n_rootdir;    /* Number of root directory entries (0 on FAT32) */\r
+#if _FS_REENTRANT\r
+  _SYNC_t sobj;                /* Identifier of sync object */\r
+#endif\r
+#if _MAX_SS != 512\r
+  WORD   s_size;       /* Sector size */\r
+#endif\r
+#if !_FS_READONLY\r
+  DWORD          last_clust;   /* Last allocated cluster */\r
+  DWORD          free_clust;   /* Number of free clusters */\r
+  DWORD          fsi_sector;   /* fsinfo sector */\r
+#endif\r
+#if _FS_RPATH\r
+  DWORD          cdir;         /* Current directory (0:root)*/\r
+#endif\r
+  DWORD          sects_fat;    /* Sectors per fat */\r
+  DWORD          max_clust;    /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */\r
+  DWORD          fatbase;      /* FAT start sector */\r
+  DWORD          dirbase;      /* Root directory start sector (Cluster# on FAT32) */\r
+  DWORD          database;     /* Data start sector */\r
+  DWORD          winsect;      /* Current sector appearing in the win[] */\r
+#if REALTIME\r
+  BYTE   *win;\r
+  BYTE   win_dflt[_MAX_SS];\r
+  BYTE   win_alt[_MAX_SS];\r
+  BYTE   current_buffer;\r
+  BYTE   buffer_used;\r
+  DWORD          win_alt_sector;\r
+#else\r
+  BYTE   win[_MAX_SS];  /* Disk access window for Directory/FAT */\r
+#endif\r
+} FATFS;\r
+\r
+\r
+\r
+/* Directory object structure */\r
+\r
+typedef struct _DIR_ {\r
+  FATFS* fs;           /* Pointer to the owner file system object */\r
+  WORD  id;            /* Owner file system mount ID */\r
+  WORD  index;         /* Current read/write index number */\r
+  DWORD         sclust;        /* Table start cluster (0:Static table) */\r
+  DWORD         clust;         /* Current cluster */\r
+  DWORD         sect;          /* Current sector */\r
+  BYTE*         dir;           /* Pointer to the current SFN entry in the win[] */\r
+  BYTE*         fn;            /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */\r
+#if _USE_LFN\r
+  WCHAR* lfn;          /* Pointer to the LFN working buffer */\r
+  WORD  lfn_idx;       /* Last matched LFN index number (0xFFFF:No LFN) */\r
+#endif\r
+} DIR;\r
+\r
+\r
+\r
+/* File object structure */\r
+\r
+typedef struct _FIL_ {\r
+  FATFS* fs;           /* Pointer to the owner file system object */\r
+  WORD  id;            /* Owner file system mount ID */\r
+  BYTE  flag;          /* File status flags */\r
+  BYTE  csect;         /* Sector address in the cluster */\r
+  DWORD         fptr;          /* File R/W pointer */\r
+  DWORD         fsize;         /* File size */\r
+  DWORD         org_clust;     /* File start cluster */\r
+  DWORD         curr_clust;    /* Current cluster */\r
+  DWORD         dsect;         /* Current data sector */\r
+#if !_FS_READONLY\r
+  DWORD         dir_sect;      /* Sector containing the directory entry */\r
+  BYTE*         dir_ptr;       /* Ponter to the directory entry in the window */\r
+#endif\r
+#if !_FS_TINY\r
+  BYTE  buf[_MAX_SS];  /* File R/W buffer */\r
+#endif\r
+} FIL;\r
+\r
+\r
+\r
+/* File status structure */\r
+\r
+typedef struct _FILINFO_ {\r
+  DWORD         fsize;         /* File size */\r
+  WORD  fdate;         /* Last modified date */\r
+  WORD  ftime;         /* Last modified time */\r
+  BYTE  fattrib;       /* Attribute */\r
+  char  fname[13];     /* Short file name (8.3 format) */\r
+#if _USE_LFN\r
+  XCHAR* lfname;       /* Pointer to the LFN buffer */\r
+  int   lfsize;        /* Size of LFN buffer [chrs] */\r
+#endif\r
+} FILINFO;\r
+\r
+\r
+\r
+\r
+/* DBCS code ranges and SBCS extend char conversion table */\r
+\r
+#if _CODE_PAGE == 932  /* Japanese Shift-JIS */\r
+#define _DF1S  0x81    /* DBC 1st byte range 1 start */\r
+#define _DF1E  0x9F    /* DBC 1st byte range 1 end */\r
+#define _DF2S  0xE0    /* DBC 1st byte range 2 start */\r
+#define _DF2E  0xFC    /* DBC 1st byte range 2 end */\r
+#define _DS1S  0x40    /* DBC 2nd byte range 1 start */\r
+#define _DS1E  0x7E    /* DBC 2nd byte range 1 end */\r
+#define _DS2S  0x80    /* DBC 2nd byte range 2 start */\r
+#define _DS2E  0xFC    /* DBC 2nd byte range 2 end */\r
+\r
+#elif _CODE_PAGE == 936        /* Simplified Chinese GBK */\r
+#define _DF1S  0x81\r
+#define _DF1E  0xFE\r
+#define _DS1S  0x40\r
+#define _DS1E  0x7E\r
+#define _DS2S  0x80\r
+#define _DS2E  0xFE\r
+\r
+#elif _CODE_PAGE == 949        /* Korean */\r
+#define _DF1S  0x81\r
+#define _DF1E  0xFE\r
+#define _DS1S  0x41\r
+#define _DS1E  0x5A\r
+#define _DS2S  0x61\r
+#define _DS2E  0x7A\r
+#define _DS3S  0x81\r
+#define _DS3E  0xFE\r
+\r
+#elif _CODE_PAGE == 950        /* Traditional Chinese Big5 */\r
+#define _DF1S  0x81\r
+#define _DF1E  0xFE\r
+#define _DS1S  0x40\r
+#define _DS1E  0x7E\r
+#define _DS2S  0xA1\r
+#define _DS2E  0xFE\r
+\r
+#elif _CODE_PAGE == 437        /* U.S. (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 720        /* Arabic (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 737        /* Greek (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \\r
+      0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 775        /* Baltic (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 850        /* Multilingual Latin 1 (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\r
+      0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 852        /* Latin 2 (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \\r
+      0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 855        /* Cyrillic (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \\r
+      0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \\r
+      0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 857        /* Turkish (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \\r
+      0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 858        /* Multilingual Latin 1 + Euro (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \\r
+      0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 862        /* Hebrew (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 866        /* Russian (OEM) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 874        /* Thai (OEM, Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}\r
+\r
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \\r
+      0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}\r
+\r
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \\r
+      0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}\r
+\r
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \\r
+      0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}\r
+\r
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \\r
+      0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}\r
+\r
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}\r
+\r
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \\r
+      0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}\r
+\r
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */\r
+#define _DF1S  0\r
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \\r
+      0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \\r
+      0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}\r
+\r
+#elif _CODE_PAGE == 1  /* ASCII (for only non-LFN cfg) */\r
+#define _DF1S  0\r
+\r
+#else\r
+#error Unknown code page\r
+\r
+#endif\r
+\r
+\r
+\r
+/* Character code support macros */\r
+\r
+#define IsUpper(c)     (((c)>='A')&&((c)<='Z'))\r
+#define IsLower(c)     (((c)>='a')&&((c)<='z'))\r
+\r
+#if _DF1S      /* DBCS configuration */\r
+\r
+#if _DF2S      /* Two 1st byte areas */\r
+#define IsDBCS1(c)     (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))\r
+#else          /* One 1st byte area */\r
+#define IsDBCS1(c)     ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)\r
+#endif\r
+\r
+#if _DS3S      /* Three 2nd byte areas */\r
+#define IsDBCS2(c)     (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))\r
+#else          /* Two 2nd byte areas */\r
+#define IsDBCS2(c)     (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))\r
+#endif\r
+\r
+#else          /* SBCS configuration */\r
+\r
+#define IsDBCS1(c)     0\r
+#define IsDBCS2(c)     0\r
+\r
+#endif /* _DF1S */\r
+\r
+\r
+\r
+/* Definitions corresponds to multi partition */\r
+\r
+#if _MULTI_PARTITION           /* Multiple partition configuration */\r
+\r
+typedef struct _PARTITION {\r
+  BYTE pd;     /* Physical drive# */\r
+  BYTE pt;     /* Partition # (0-3) */\r
+} PARTITION;\r
+\r
+extern\r
+const PARTITION Drives[];                      /* Logical drive# to physical location conversion table */\r
+#define LD2PD(drv) (Drives[drv].pd)    /* Get physical drive# */\r
+#define LD2PT(drv) (Drives[drv].pt)    /* Get partition# */\r
+\r
+#else                                          /* Single partition configuration */\r
+\r
+#define LD2PD(drv) (drv)       /* Physical drive# is equal to the logical drive# */\r
+#define LD2PT(drv) 0           /* Always mounts the 1st partition */\r
+\r
+#endif\r
+\r
+\r
+\r
+/* File function return code (FRESULT) */\r
+\r
+typedef enum {\r
+  FR_OK = 0,                   /* 0 */\r
+  FR_DISK_ERR,         /* 1 */\r
+  FR_INT_ERR,                  /* 2 */\r
+  FR_NOT_READY,                /* 3 */\r
+  FR_NO_FILE,                  /* 4 */\r
+  FR_NO_PATH,                  /* 5 */\r
+  FR_INVALID_NAME,     /* 6 */\r
+  FR_DENIED,                   /* 7 */\r
+  FR_EXIST,                    /* 8 */\r
+  FR_INVALID_OBJECT,   /* 9 */\r
+  FR_WRITE_PROTECTED,  /* 10 */\r
+  FR_INVALID_DRIVE,    /* 11 */\r
+  FR_NOT_ENABLED,              /* 12 */\r
+  FR_NO_FILESYSTEM,    /* 13 */\r
+  FR_MKFS_ABORTED,     /* 14 */\r
+  FR_TIMEOUT                   /* 15 */\r
+} FRESULT;\r
+\r
+\r
+\r
+/*--------------------------------------------------------------*/\r
+/* FatFs module application interface                           */\r
+\r
+FRESULT f_mount (BYTE, FATFS*);                                                /* Mount/Unmount a logical drive */\r
+FRESULT f_open (FIL*, const XCHAR*, BYTE);                     /* Open or create a file */\r
+FRESULT f_read (FIL*, void*, UINT, UINT*);                     /* Read data from a file */\r
+FRESULT f_write (FIL*, const void*, UINT, UINT*);      /* Write data to a file */\r
+//FRESULT f_lseek (FIL*, DWORD);                                               /* Move file pointer of a file object */\r
+FRESULT f_lseek (FIL*, LONG);                                          /* Move file pointer of a file object */\r
+FRESULT f_close (FIL*);                                                                /* Close an open file object */\r
+FRESULT f_opendir (DIR*, const XCHAR*);                                /* Open an existing directory */\r
+FRESULT f_readdir (DIR*, FILINFO*);                                    /* Read a directory item */\r
+FRESULT f_stat (const XCHAR*, FILINFO*);                               /* Get file status */\r
+FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**);     /* Get number of free clusters on the drive */\r
+FRESULT f_truncate (FIL*);                                                     /* Truncate file */\r
+FRESULT f_sync (FIL*);                                                         /* Flush cached data of a writing file */\r
+FRESULT f_unlink (const XCHAR*);                                               /* Delete an existing file or directory */\r
+FRESULT        f_mkdir (const XCHAR*);                                         /* Create a new directory */\r
+FRESULT f_chmod (const XCHAR*, BYTE, BYTE);                    /* Change attriburte of the file/dir */\r
+FRESULT f_utime (const XCHAR*, const FILINFO*);                /* Change timestamp of the file/dir */\r
+FRESULT f_rename (const XCHAR*, const XCHAR*);         /* Rename/Move a file or directory */\r
+FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);      /* Forward data to the stream */\r
+FRESULT f_mkfs (BYTE, BYTE, WORD);                                     /* Create a file system on the drive */\r
+FRESULT f_chdir (const XCHAR*);                                                /* Change current directory */\r
+FRESULT f_chdrive (BYTE);                                                      /* Change current drive */\r
+\r
+#if _USE_STRFUNC\r
+int f_putc (int, FIL*);                                                                /* Put a character to the file */\r
+int f_puts (const char*, FIL*);                                                /* Put a string to the file */\r
+int f_printf (FIL*, const char*, ...);                         /* Put a formatted string to the file */\r
+char* f_gets (char*, int, FIL*);                                       /* Get a string from the file */\r
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)\r
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)\r
+#ifndef EOF\r
+#define EOF -1\r
+#endif\r
+#endif\r
+\r
+\r
+/*--------------------------------------------------------------*/\r
+/* User defined functions                                       */\r
+\r
+\r
+/* Real time clock */\r
+DWORD get_fattime (void);      /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */\r
+/* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */\r
+\r
+/* Unicode - OEM code conversion */\r
+#if _USE_LFN\r
+WCHAR ff_convert (WCHAR, UINT);\r
+#endif\r
+\r
+\r
+\r
+\r
+/*--------------------------------------------------------------*/\r
+/* Flags and offset address                                     */\r
+\r
+\r
+/* File access control and file status flags (FIL.flag) */\r
+\r
+#define        FA_READ                         0x01\r
+#define        FA_OPEN_EXISTING        0x00\r
+#if _FS_READONLY == 0\r
+#define        FA_WRITE                        0x02\r
+#define        FA_CREATE_NEW           0x04\r
+#define        FA_CREATE_ALWAYS        0x08\r
+#define        FA_OPEN_ALWAYS          0x10\r
+#define FA__WRITTEN                    0x20\r
+#define FA__DIRTY                      0x40\r
+#endif\r
+#define FA__ERROR                      0x80\r
+\r
+\r
+/* FAT sub type (FATFS.fs_type) */\r
+\r
+#define FS_FAT12       1\r
+#define FS_FAT16       2\r
+#define FS_FAT32       3\r
+\r
+\r
+/* File attribute bits for directory entry */\r
+\r
+#define        AM_RDO  0x01    /* Read only */\r
+#define        AM_HID  0x02    /* Hidden */\r
+#define        AM_SYS  0x04    /* System */\r
+#define        AM_VOL  0x08    /* Volume label */\r
+#define AM_LFN 0x0F    /* LFN entry */\r
+#define AM_DIR 0x10    /* Directory */\r
+#define AM_ARC 0x20    /* Archive */\r
+#define AM_MASK        0x3F    /* Mask of defined bits */\r
+\r
+\r
+/* FatFs refers the members in the FAT structures with byte offset instead\r
+   / of structure member because there are incompatibility of the packing option\r
+   / between various compilers. */\r
+\r
+#define BS_jmpBoot                     0\r
+#define BS_OEMName                     3\r
+#define BPB_BytsPerSec         11\r
+#define BPB_SecPerClus         13\r
+#define BPB_RsvdSecCnt         14\r
+#define BPB_NumFATs                    16\r
+#define BPB_RootEntCnt         17\r
+#define BPB_TotSec16           19\r
+#define BPB_Media                      21\r
+#define BPB_FATSz16                    22\r
+#define BPB_SecPerTrk          24\r
+#define BPB_NumHeads           26\r
+#define BPB_HiddSec                    28\r
+#define BPB_TotSec32           32\r
+#define BS_55AA                                510\r
+\r
+#define BS_DrvNum                      36\r
+#define BS_BootSig                     38\r
+#define BS_VolID                       39\r
+#define BS_VolLab                      43\r
+#define BS_FilSysType          54\r
+\r
+#define BPB_FATSz32                    36\r
+#define BPB_ExtFlags           40\r
+#define BPB_FSVer                      42\r
+#define BPB_RootClus           44\r
+#define BPB_FSInfo                     48\r
+#define BPB_BkBootSec          50\r
+#define BS_DrvNum32                    64\r
+#define BS_BootSig32           66\r
+#define BS_VolID32                     67\r
+#define BS_VolLab32                    71\r
+#define BS_FilSysType32                82\r
+\r
+#define        FSI_LeadSig                     0\r
+#define        FSI_StrucSig            484\r
+#define        FSI_Free_Count          488\r
+#define        FSI_Nxt_Free            492\r
+\r
+#define MBR_Table                      446\r
+\r
+#define        DIR_Name                        0\r
+#define        DIR_Attr                        11\r
+#define        DIR_NTres                       12\r
+#define        DIR_CrtTime                     14\r
+#define        DIR_CrtDate                     16\r
+#define        DIR_FstClusHI           20\r
+#define        DIR_WrtTime                     22\r
+#define        DIR_WrtDate                     24\r
+#define        DIR_FstClusLO           26\r
+#define        DIR_FileSize            28\r
+#define        LDIR_Ord                        0\r
+#define        LDIR_Attr                       11\r
+#define        LDIR_Type                       12\r
+#define        LDIR_Chksum                     13\r
+#define        LDIR_FstClusLO          26\r
+\r
+\r
+\r
+/*--------------------------------*/\r
+/* Multi-byte word access macros  */\r
+\r
+#if _WORD_ACCESS == 1  /* Enable word access to the FAT structure */\r
+#define        LD_WORD(ptr)            (WORD)(*(WORD*)(BYTE*)(ptr))\r
+#define        LD_DWORD(ptr)           (DWORD)(*(DWORD*)(BYTE*)(ptr))\r
+#define        ST_WORD(ptr,val)        *(WORD*)(BYTE*)(ptr)=(WORD)(val)\r
+#define        ST_DWORD(ptr,val)       *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)\r
+#else                                  /* Use byte-by-byte access to the FAT structure */\r
+#define        LD_WORD(ptr)            (WORD)(((WORD)*(volatile BYTE*)((ptr)+1)<<8)|(WORD)*(volatile BYTE*)(ptr))\r
+#define        LD_DWORD(ptr)           (DWORD)(((DWORD)*(volatile BYTE*)((ptr)+3)<<24)|((DWORD)*(volatile BYTE*)((ptr)+2)<<16)|((WORD)*(volatile BYTE*)((ptr)+1)<<8)|*(volatile BYTE*)(ptr))\r
+#define        ST_WORD(ptr,val)        *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)\r
+#define        ST_DWORD(ptr,val)       *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(volatile BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(volatile BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)\r
+#endif\r
+\r
+\r
+#endif /* _FATFS */\r
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/FatFs.nc b/tos/platforms/shimmer/chips/sd/fatfs/FatFs.nc
new file mode 100644 (file)
index 0000000..5f42b1a
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2009, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author  Steve Ayer
+ * @date    April, 2009
+ * ported to tos-2.x
+ * @date January, 2010
+ */
+
+#include "FatFs.h"
+
+interface FatFs { 
+  /* non-zero return codes indicate an error; the codes for these are in an enum in FatFs.h */
+
+  command void asc_fattime(char * timestring);
+  /*
+   * do this before performing file ops!
+   * pointer to FATFS struct provides required bookkeeping space for the fs
+   */
+  command error_t mount(FATFS * fs);
+
+  command error_t unmount();
+
+  /*
+   * take the card down, "unmount" the fs
+   */
+  command void disable();
+
+  /*
+   * mode flags:
+   * FA_READ   Specifies read access to the object. Data can be read from the file.
+   *            Combine with FA_WRITE for read-write access.
+   * FA_WRITE  Specifies write access to the object. Data can be written to the file.
+                Combine with FA_READ for read-write access.
+   * FA_OPEN_EXISTING  Opens the file. The function fails if the file is not existing. (Default)
+   * FA_OPEN_ALWAYS    Opens the file, if it is existing. If not, the function creates the new file.
+   * FA_CREATE_NEW     Creates a new file. The function fails if the file is already existing.
+   * FA_CREATE_ALWAYS  Creates a new file. If the file is existing, it is truncated and overwritten.
+   *
+   */
+  command error_t fopen(FIL * fp, const char * filename, BYTE mode);
+  
+  command error_t fclose(FIL * fp);
+  
+  command error_t fread(FIL * fp, 
+                        void * buffer,
+                        uint bytesToRead,
+                        uint * bytesRead);
+
+  command error_t fwrite(FIL * fp,
+                         const void * buffer,
+                         uint bytesToWrite,
+                         uint * bytesWritten);
+
+  // no ftell, but fp has fptr once file is open
+  command error_t fseek(FIL * fp, int32_t offset);
+  
+  // truncate this file at the current location of the pointer
+  command error_t ftruncate(FIL * fp);
+  
+  /* 
+   * flush the file cache to physical media;  good for avoiding data loss due
+   * to potential platform disruption (battery, app failure, media removal),
+   * but should be used sparingly to avoid excess flash r/w cycles; fp struct has 
+   * a 512-byte buffer.  
+   */
+  command error_t fsync(FIL * fp);
+
+  command error_t mkdir(const char * dirname);
+
+  command error_t chdir(const char * dirname);
+
+  // feed it an empty DIR struct, used before doing readdir calls
+  command error_t opendir(DIR * dp, const char * dirname);
+  
+  /*
+   * reads dir entries in sequence until fi->fname is "" (fname[0] == NULL)
+   *
+   * since long filenames are used here, a buffer of sufficient size 
+   * (_MAX_LFN + 1, unless using some asian code pages.  see FatFs.h)
+   * must be attached to fi->lfname, with its size in fi->lfsize
+   * 
+   */
+  command error_t readdir(DIR * dp, FILINFO * fi);
+  
+  /*
+   * path is to root; fatfs struct is statically declared in driver
+   */
+  command error_t getfree(const char * path, uint32_t * clusters, FATFS ** fs);
+
+  command error_t stat(const char * filename, FILINFO * fi);
+
+  command error_t unlink(const char * filename);
+
+  /*
+   * this one's a bit weird
+   * these are the flags for "value":
+   * AM_RDO    Read only         (0x01)
+   * AM_ARC     Archive           (0x20)
+   * AM_SYS     System            (0x04)
+   * AM_HID     Hidden            (0x02)
+   * 
+   * mask is for exposing attributes to effect of value flag; i.e., 
+   * if the value bit is zero for a particular attribute and the mask 
+   * has a 1 in that position (e.g. value is AM_HID|AM_RDO and mask is 
+   * AM_HID|AM_SYS|AM_RDO, then AM_SYS will be turned off) that attribute 
+   * will be disabled.
+   */
+  command error_t chmod(const char * filename, BYTE value, BYTE mask);
+
+  /*
+   * a good time to introduce this:
+   * in timedate, the fields break out thus:
+   * for fdate, 
+   *    bit15:9
+   *     Year origin from 1980 (0..127)
+   *    bit8:5
+   *     Month (1..12)
+   *    bit4:0
+   *     Day (1..31)
+   *
+   * for ftime,
+   *    bit15:11
+   *     Hour (0..23)
+   *    bit10:5
+   *     Minute (0..59)
+   *    bit4:0
+   *     Second / 2 (0..29)
+   */
+  command error_t f_utime(const char * filename, FILINFO * timedate);
+
+  command error_t rename(const char * oldname, const char * newname);
+  
+  /*
+   * size in sectors; 512 bytes/sector is fixed
+   * a zero allocates the whole drive/card
+   */
+  command error_t mkfs(WORD allocSize);
+
+  command const char * ff_strerror(error_t errnum);
+
+  /*
+   * these bubble the sd-driver-level events that the app has, 
+   * or has lost, access to the card
+   */
+  async event void mediaUnavailable();
+  async event void mediaAvailable();
+}
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/FatFsP.nc b/tos/platforms/shimmer/chips/sd/fatfs/FatFsP.nc
new file mode 100644 (file)
index 0000000..f90e7de
--- /dev/null
@@ -0,0 +1,3509 @@
+/*---------------------------------------------------------------------------/
+  / FatFs module is an open source project to implement FAT file system to small
+  / embedded systems. It is opened for education, research and development under
+  / license policy of following trems.
+  /
+  /  Copyright (C) 2009, ChaN, all right reserved.
+  /
+  / * The FatFs module is a free software and there is no warranty.
+  / * You can use, modify and/or redistribute it for personal, non-profit or
+  /   commercial use without any restriction under your responsibility.
+  / * Redistributions of source code must retain the above copyright notice.
+  /
+  /----------------------------------------------------------------------------*/
+/*
+ * Portions of this code are:
+ *
+ * Copyright (c) 2009, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * This is a tinyos-1.x implementation of ChaN's FatFs embedded FAT filesystem.
+ * Many thanks to ChaN for his excellent work, which has been modified as little
+ * as possible to fit into the tinyos framework.
+ *
+ * @author Steve Ayer
+ * @date April, 2009
+ * port to tos-2.x
+ * @date January, 2010
+ *
+ * Cluster window optimizations:
+ * @author Victor Cionca
+ * @date July, 2009
+ *
+ */
+
+#include "FatFs.h"
+
+module FatFsP {
+  provides {
+    interface FatFs;
+  }
+  uses{ 
+    interface StdControl as diskIOStdControl;
+    interface SD as diskIO;
+    interface Leds;
+    interface Time;
+  }
+}
+
+implementation {
+#include "diskio.c"  
+#include "ccsbcs.c"
+
+  FRESULT f_mount (BYTE, FATFS*);                              /* Mount/Unmount a logical drive */
+  FRESULT f_open (FIL*, const char*, BYTE);                    /* Open or create a file */
+  FRESULT f_read (FIL*, void*, UINT, UINT*);                   /* Read data from a file */
+  FRESULT f_write (FIL*, const void*, UINT, UINT*);            /* Write data to a file */
+  FRESULT f_lseek (FIL*, LONG);                                        /* Move file pointer of a file object */
+  FRESULT f_close (FIL*);                                      /* Close an open file object */
+  FRESULT f_opendir (DIR*, const char*);                       /* Open an existing directory */
+  FRESULT f_readdir (DIR*, FILINFO*);                          /* Read a directory item */
+  FRESULT f_stat (const char*, FILINFO*);                      /* Get file status */
+  FRESULT f_getfree (const char*, DWORD*, FATFS**);            /* Get number of free clusters on the drive */
+  FRESULT f_truncate (FIL*);                                   /* Truncate file */
+  FRESULT f_sync (FIL*);                                       /* Flush cached data of a writing file */
+  FRESULT f_unlink (const char*);                              /* Delete an existing file or directory */
+  FRESULT f_mkdir (const char*);                               /* Create a new directory */
+  FRESULT f_chmod (const char*, BYTE, BYTE);                   /* Change attriburte of the file/dir */
+  FRESULT f_utime (const char*, const FILINFO*);               /* Change timestamp of the file/dir */
+  FRESULT f_rename (const char*, const char*);                 /* Rename/Move a file or directory */
+  FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);    /* Forward data to the stream */
+  FRESULT f_mkfs (BYTE, BYTE, WORD);                                   /* Create a file system on the drive */
+  FRESULT f_chdir (const XCHAR*);                              /* Change current directory */
+  FRESULT f_chdrive (BYTE);                                    /* Change current drive */
+  DWORD   get_fattime ();
+
+
+#if REALTIME
+  /**
+   * Dirty hack function to get the address of the file directory entry
+   * in the window, if we are using buffers for the window
+   *
+   * The problem is that the original code represents the directory entry 
+   * as a fixed address in the fs window (dirty hack, but makes sense).
+   * As we use two windows (dflt and buffer), we need to update the address
+   * of the dir entry: find in what window the address was, get the offset 
+   * in the window and add it to the main window address
+   */
+  BYTE *get_dir_ptr(FATFS *fs, BYTE *ptr){
+    //    UINT window_address;
+    //    UINT dir_obj_address;
+
+    INT diff_win;
+
+    diff_win = ptr - fs->win_dflt;
+
+    if (diff_win < 0 || diff_win >= _MAX_SS){  // the dir_ptr is not in the first window buffer
+      diff_win = ptr - fs->win_alt; // try the second buffer
+      if (diff_win < 0 || diff_win >= _MAX_SS){ 
+       // error, can't find the dir_ptr in any of the buffers
+       return 0;
+      }
+    }
+
+    return fs->win + diff_win;
+  }
+
+  BYTE cluster_in_alt_window(FATFS *fs, DWORD cluster){
+    switch (fs->fs_type){
+    case FS_FAT16:
+      if (cluster/(SS(fs)/2)+fs->fatbase == fs->win_alt_sector) return 1;
+      else return 0;
+    case FS_FAT32:
+      if (cluster/(SS(fs)/4)+fs->fatbase == fs->win_alt_sector) return 1;
+      else return 0;
+    }
+
+    return 0;
+  }
+
+  void invert_buffers(FATFS *fs){
+    DWORD wsect = 0;
+    atomic{
+      switch (fs->current_buffer){
+      case 0:
+       fs->win = fs->win_alt;
+       fs->current_buffer = 1;
+       break;
+      case 1:
+       fs->win = fs->win_dflt;
+       fs->current_buffer = 0;
+       break;
+      }
+
+      wsect = fs->winsect;
+      fs->winsect = fs->win_alt_sector; // update the active sector, since we are moving the window
+      fs->win_alt_sector = wsect;      // update the buffer sector
+    }
+  }
+
+  DWORD sync_both_win_buffers(FATFS *fs){
+
+    // write both buffers to disk
+    switch (fs->current_buffer){
+    case 0:    // we are currently using buffer 0 (dflt), so write buffer 1 (alt)
+      if (disk_write(fs->drive, fs->win_alt, fs->win_alt_sector, 1) != RES_OK)
+       return FR_DISK_ERR;
+      if (disk_write(fs->drive, fs->win_dflt, fs->winsect, 1) != RES_OK)
+       return FR_DISK_ERR;
+      break;
+    case 1:
+      if (disk_write(fs->drive, fs->win_dflt, fs->win_alt_sector, 1) != RES_OK)
+       return FR_DISK_ERR;
+      if (disk_write(fs->drive, fs->win_alt, fs->winsect, 1) != RES_OK)
+       return FR_DISK_ERR;
+      break;
+    }
+
+    fs->win_alt_sector = 0;    // the alternative sector is 0 (not used)
+    fs->buffer_used = 0;       // buffer is not used 
+
+    return RES_OK;
+  }
+
+  DWORD sync_win_buffers(FATFS *fs){
+    switch (fs->current_buffer){
+    case 0:    // we are currently using buffer 0 (dflt), so write buffer 1 (alt)
+      if (disk_write(fs->drive, fs->win_alt, fs->win_alt_sector, 1) != RES_OK)
+       return FR_DISK_ERR;
+      break;
+    case 1:
+      if (disk_write(fs->drive, fs->win_dflt, fs->win_alt_sector, 1) != RES_OK)
+       return FR_DISK_ERR;
+      break;
+    }
+
+    fs->win_alt_sector = 0;    // the alternative sector is 0 (not used)
+    fs->buffer_used = 0;       // buffer is not used 
+
+    return RES_OK;
+  }
+
+  void init_fatfs(FATFS *fs){
+    atomic{
+      fs->win = fs->win_dflt;
+      fs->current_buffer = 0;
+      fs->win_alt_sector = 0;
+      fs->buffer_used = 0;
+    }
+  }
+#endif
+
+  /*
+   * do this before performing file ops!
+   * pointer to FATFS struct provides required bookkeeping space for the fs
+   */
+  command error_t FatFs.mount(FATFS * fs){
+    return f_mount(0, fs);
+  }
+
+  command error_t FatFs.unmount(){
+    dock_disk();   // this calls diskiostdcontrol.start()
+    return SUCCESS;
+  }
+
+  /*
+   * disable_disk is a wrapper for diskIOStdControl.stop,
+   * which disables the card completely
+   */
+  command void FatFs.disable(){
+    f_mount(0, NULL);  // this is supposed to unmount
+    
+    disable_disk();  
+  }
+  
+  /*
+   * mode flags:
+   * FA_READ   Specifies read access to the object. Data can be read from the file.
+   *            Combine with FA_WRITE for read-write access.
+   * FA_WRITE  Specifies write access to the object. Data can be written to the file.
+   Combine with FA_READ for read-write access.
+   * FA_OPEN_EXISTING  Opens the file. The function fails if the file is not existing. (Default)
+   * FA_OPEN_ALWAYS    Opens the file, if it is existing. If not, the function creates the new file.
+   * FA_CREATE_NEW     Creates a new file. The function fails if the file is already existing.
+   * FA_CREATE_ALWAYS  Creates a new file. If the file is existing, it is truncated and overwritten.
+   *
+   */
+  command error_t FatFs.fopen(FIL * fp, const char * filename, BYTE mode){
+    return f_open(fp, filename, mode);
+  }
+
+  command void FatFs.asc_fattime(char * timestring) {
+    time_t time_now;
+    struct tm ltime;
+    DWORD now;
+
+    now = get_fattime();
+#ifdef SPRINTF
+    sprintf(timestring, "raw fattime is %08lx", now);
+#endif
+    /*
+      call Time.time(&time_now);
+      call Time.localtime(&time_now, &ltime);
+      call Time.asctime(&ltime, timestring, 128);
+    */
+  }
+
+  command error_t FatFs.fclose(FIL * fp){
+    return f_close(fp);
+  }
+
+  command error_t FatFs.fread(FIL * fp, 
+                              void * buffer,
+                              uint bytesToRead,
+                              uint * bytesRead){
+
+    if (!fp || !fp->fs) 
+      return FR_INVALID_OBJECT;
+
+#if REALTIME
+    if (cluster_in_alt_window(fp->fs, fp->curr_clust)){        
+      // the current cluster FAT entry is in the buffered window,
+      // so invert the buffers
+      invert_buffers(fp->fs);
+    }
+
+    if (fp->fs->buffer_used) 
+      if (sync_win_buffers(fp->fs) != RES_OK) 
+       return FR_DISK_ERR;
+#endif
+
+    return f_read(fp, buffer, bytesToRead, bytesRead);
+  }
+
+  command error_t FatFs.fwrite(FIL * fp,
+                               const void * buffer,
+                               uint bytesToWrite,
+                               uint * bytesWritten){
+
+    if (!fp || !fp->fs) 
+      return FR_INVALID_OBJECT;
+
+#if REALTIME
+    if (cluster_in_alt_window(fp->fs, fp->curr_clust)){        
+      // the current cluster FAT entry is in the buffered window,
+      // so invert the buffers
+      invert_buffers(fp->fs);
+    }
+
+    if (fp->fs->buffer_used) 
+      if (sync_win_buffers(fp->fs) != RES_OK) 
+       return FR_DISK_ERR;
+#endif
+
+    return f_write(fp, buffer, bytesToWrite, bytesWritten);
+  }
+
+  // no ftell, but fp has fptr once file is open
+  command error_t FatFs.fseek(FIL * fp, int32_t offset){
+
+    if (!fp || !fp->fs) 
+      return FR_INVALID_OBJECT;
+
+#if REALTIME
+    if (cluster_in_alt_window(fp->fs, fp->curr_clust)){        
+      // the current cluster FAT entry is in the buffered window,
+      // so invert the buffers
+      invert_buffers(fp->fs);
+    }
+
+    if (fp->fs->buffer_used) 
+      if (sync_win_buffers(fp->fs) != RES_OK) 
+       return FR_DISK_ERR;
+#endif
+    return f_lseek(fp, offset);
+  }
+
+  // truncate this file at the current location of the pointer
+  command error_t FatFs.ftruncate(FIL * fp){
+
+    if (!fp || !fp->fs) 
+      return FR_INVALID_OBJECT;
+
+#if REALTIME
+    if (cluster_in_alt_window(fp->fs, fp->curr_clust)){        
+      // the current cluster FAT entry is in the buffered window,
+      // so invert the buffers
+      invert_buffers(fp->fs);
+    }
+
+    if (fp->fs->buffer_used) 
+      if (sync_win_buffers(fp->fs) != RES_OK) 
+       return FR_DISK_ERR;
+#endif
+
+    return f_truncate(fp);
+  }
+
+  /* 
+   * flush the file cache to physical media;  good for avoiding data loss due
+   * to potential platform disruption (battery, app failure, media removal),
+   * but should be used sparingly to avoid excess flash r/w cycles; fp struct has 
+   * a 512-byte buffer.  
+   */
+  command error_t FatFs.fsync(FIL * fp){
+    return f_sync(fp);
+  }
+
+  command error_t FatFs.mkdir(const char * dirname){
+    return f_mkdir(dirname);
+  }
+
+  //  feed it an empty DIR struct, used before doing readdir calls
+  command error_t FatFs.opendir(DIR * dp, const char * dirname){
+    return f_opendir(dp, dirname);
+  }
+
+  // set current working directory
+  command error_t FatFs.chdir(const char * dirname){
+    return f_chdir(dirname);
+  }
+
+  /*
+   * reads dir entries in sequence until fi->fname is "" (fname[0] == NULL)
+   *
+   * since long filenames are used here, a buffer of sufficient size 
+   * (_MAX_LFN + 1, unless using some asian code pages.  see FatFs.h)
+   * must be attached to fi->lfname, with its size in fi->lfsize
+   * 
+   */
+  command error_t FatFs.readdir(DIR * dp, FILINFO * fi){
+    return f_readdir(dp, fi);
+  }
+
+  /*
+   * path is to root; fatfs struct is statically declared in driver
+   */
+  command error_t FatFs.getfree(const char * path, uint32_t * clusters, FATFS ** fs){
+    return f_getfree(path, clusters, fs);
+  }
+
+  command error_t FatFs.stat(const char * filename, FILINFO * fi){
+    return f_stat(filename, fi);
+  }
+
+  command error_t FatFs.unlink(const char * filename){
+    return f_unlink(filename);
+  }
+
+  command const char * FatFs.ff_strerror(error_t errnum){
+    switch(errnum){
+    case FR_OK:                        /* 0 */
+      return "No Error";
+    case FR_DISK_ERR:          /* 1 */
+      return "Disk Error";
+    case FR_INT_ERR:                   /* 2 */
+      return "Internal Error (bad or out of range cluster?)";
+    case FR_NOT_READY:         /* 3 */
+      return "Drive Not Ready (uninitialized?)";
+    case FR_NO_FILE:                   /* 4 */
+      return "File Not Found";
+    case FR_NO_PATH:                   /* 5 */
+      return "Path Not Found";
+    case FR_INVALID_NAME:      /* 6 */
+      return "Invalid Name (root dir?)";
+    case FR_DENIED:                    /* 7 */
+      return "Operation Denied";
+    case FR_EXIST:                     /* 8 */
+      return "File Object Already Exists";
+    case FR_INVALID_OBJECT:    /* 9 */
+      return "Invalid Object";
+    case FR_WRITE_PROTECTED:   /* 10 */
+      return "Write Protected";
+    case FR_INVALID_DRIVE:     /* 11 */
+      return "Invalid Drive";
+    case FR_NOT_ENABLED:               /* 12 */
+      return "Filesystem Not Enabled (improper drive?)";
+    case FR_NO_FILESYSTEM:     /* 13 */
+      return "No Valid FAT Partition Found";
+    case FR_MKFS_ABORTED:      /* 14 */
+      return "Unable to Build Filesystem; Aborting";
+    case FR_TIMEOUT:                   /* 15 */
+      return "Re-entrant Operation Timed Out (invalid error in tos!)";
+    case 22:
+      return "My ERROR!";
+    default:
+      return "Unknown Error";
+    }
+  }
+
+  /*
+   * this one's a bit weird
+   * these are the flags for "value":
+   * AM_RDO    Read only         (0x01)
+   * AM_ARC     Archive           (0x20)
+   * AM_SYS     System            (0x04)
+   * AM_HID     Hidden            (0x02)
+   * 
+   * mask is for exposing attributes to effect of value flag; i.e., 
+   * if the value bit is zero for a particular attribute and the mask 
+   * has a 1 in that position (e.g. value is AM_HID|AM_RDO and mask is 
+   * AM_HID|AM_SYS|AM_RDO, then AM_SYS will be turned off) that attribute 
+   * will be disabled.
+   */
+  command error_t FatFs.chmod(const char * filename, BYTE value, BYTE mask){
+    return f_chmod(filename, value, mask);
+  }
+
+  /*
+   * a good time to introduce this:
+   * in timedate, the fields break out thus:
+   * for fdate, 
+   *    bit15:9
+   *     Year origin from 1980 (0..127)
+   *    bit8:5
+   *     Month (1..12)
+   *    bit4:0
+   *     Day (1..31)
+   *
+   * for ftime,
+   *    bit15:11
+   *     Hour (0..23)
+   *    bit10:5
+   *     Minute (0..59)
+   *    bit4:0
+   *     Second / 2 (0..29)
+   */
+  command error_t FatFs.f_utime(const char * filename, FILINFO * timedate){
+    return f_utime(filename, timedate);
+  }
+
+  command error_t FatFs.rename(const char * oldname, const char * newname){
+    return f_rename(oldname, newname);
+  }
+
+  /*
+   * size in sectors; 512 bytes/sector is fixed
+   * a zero allocates the whole drive/card
+   */
+  command error_t FatFs.mkfs(WORD allocSize){
+    return f_mkfs(0, 0, allocSize);
+  }
+
+#define        ENTER_FF(fs)
+#define LEAVE_FF(fs, res)      return res
+
+#define        ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+  /* Name status flags */
+#define NS                     11              /* Offset of name status byte */
+#define NS_LOSS                0x01    /* Out of 8.3 format */
+#define NS_LFN         0x02    /* Force to create LFN entry */
+#define NS_LAST                0x04    /* Last segment */
+#define NS_BODY                0x08    /* Lower case flag (body) */
+#define NS_EXT         0x10    /* Lower case flag (ext) */
+#define NS_DOT         0x20    /* Dot entry */
+
+#if _DRIVES < 1 || _DRIVES > 9
+#error Number of drives must be 1-9.
+#endif
+  
+  static FATFS * FatFs[_DRIVES];       /* Pointer to the file system objects (logical drives) */
+  static WORD Fsid;                            /* File system mount ID */
+
+#if _FS_RPATH
+  static
+    BYTE Drive;                                /* Current drive */
+#endif
+
+
+#if _USE_LFN == 1      /* LFN with static LFN working buffer */
+  static WCHAR LfnBuf[_MAX_LFN + 1];
+#define        NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR *lp = LfnBuf
+#define INITBUF(dj,sp,lp)      dj.fn = sp; dj.lfn = lp
+
+#elif _USE_LFN > 1     /* LFN with dynamic LFN working buffer */
+#define        NAMEBUF(sp,lp)  BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf
+#define INITBUF(dj,sp,lp)      dj.fn = sp; dj.lfn = lp
+
+#else                          /* No LFN */
+#define        NAMEBUF(sp,lp)  BYTE sp[12]
+#define INITBUF(dj,sp,lp)      dj.fn = sp
+
+#endif
+
+  event void Time.tick() {}
+  /* return type bitmask:
+   * 31-25: Year(0-127 org.1980) 
+   * 24-21: Month(1-12)
+   * 20-16: Day(1-31)  
+   * 15-11: Hour(0-23) 
+   * 10-5: Minute(0-59) 
+   * 4-0: Second(0-29 *2) 
+   */
+  DWORD get_fattime (){
+    DWORD now;
+    uint32_t year_from_80, month, day, hour, minute, second;
+    struct tm ltime;   
+    time_t time_now;
+    call Time.time(&time_now);
+    call Time.localtime(&time_now, &ltime);
+    year_from_80 = ltime.tm_year - 1980;
+    month        = ltime.tm_mon + 1;
+    day          = ltime.tm_mday;
+    hour         = ltime.tm_hour;
+    minute       = ltime.tm_min;
+    second       = ltime.tm_sec / 2;
+
+    now = (year_from_80 << 25) | (month << 21) | (day << 16) | (hour << 11) | (minute << 5) | ((second * 2));
+
+    return now;
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Change window offset                                                  */
+  /*-----------------------------------------------------------------------*/
+
+  /* Move to zero only writes back dirty window */
+  static FRESULT move_window ( FATFS *fs,      DWORD sector)   /* File system object, Sector number to make apperance in the fs->win[] */
+    {
+      DWORD wsect;
+
+
+      wsect = fs->winsect;
+      if (wsect != sector) {   /* Changed current window */
+#if !_FS_READONLY
+#if REALTIME
+       // since we are buffering windows, we can check if the 
+       // request matches the buffer, and avoid the move
+       if (fs->buffer_used && fs->win_alt_sector == sector){
+         // we have to switch the buffers, without writing
+         // as the next window to be accessed might be the other one
+         invert_buffers(fs);
+
+         return FR_OK;
+       }
+#endif
+
+       if (fs->wflag) {        /* Write back dirty window if needed */
+#if REALTIME
+         // moving the window to 0 shouldn't do any buffering
+         // plus, leave the buffer alone, it will be handled
+         // in the future
+         if (sector == 0){
+           fs->wflag = 0;
+           return disk_write(fs->drive, fs->win, wsect, 1);
+         }
+
+         // buffer the current window and write it on the next I/O op,
+         // thus creating an even I/O load per write request
+         // (currently, we have 3 additional I/O when this event happens; 
+         // with this change we have only two split accross two consecutive requests)
+         if (fs->buffer_used) {
+           if (sync_win_buffers(fs) != RES_OK) return FR_DISK_ERR;
+         }
+
+         atomic{
+           switch (fs->current_buffer){
+           case 0:
+             fs->win = fs->win_alt;
+             fs->current_buffer = 1;
+             break;
+           case 1:
+             fs->win = fs->win_dflt;
+             fs->current_buffer = 0;
+             break;
+           }
+           fs->win_alt_sector = fs->winsect;
+           fs->buffer_used = 1;
+         }
+#else
+         if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK)
+           return FR_DISK_ERR;
+#endif
+
+         fs->wflag = 0;
+#if !REALTIME
+         // in realtime we care about timing, not redundancy
+         if (wsect < (fs->fatbase + fs->sects_fat)) {  /* In FAT area */
+           BYTE nf;
+           for (nf = fs->n_fats; nf > 1; nf--) {       /* Refrect the change to FAT copy */
+             wsect += fs->sects_fat;
+             disk_write(fs->drive, fs->win, wsect, 1);
+           }
+         }
+#endif
+       }
+#endif
+       if (sector) {
+         if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK)
+           return FR_DISK_ERR;
+         fs->winsect = sector;
+       }
+      }
+
+      return FR_OK;
+    }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Clean-up cached data                                                  */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+  /* FR_OK: successful, FR_DISK_ERR: failed */
+  static FRESULT sync (FATFS *fs)
+    {
+      FRESULT res = 0;
+
+#if !REALTIME
+      res = move_window(fs, 0);
+      if (res == FR_OK) {
+#endif
+       /* Update FSInfo sector if needed */
+       if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+#if !REALTIME
+         fs->winsect = 0;
+#else
+         res = move_window(fs, fs->fsi_sector);
+#endif
+         memset(fs->win, 0, 512);
+         ST_WORD(fs->win+BS_55AA, 0xAA55);
+         ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+         ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+         ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+         ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+#if !REALTIME
+         disk_write(fs->drive, fs->win, fs->fsi_sector, 1);
+#endif
+         fs->fsi_flag = 0;
+         fs->wflag = 1;
+       }
+       /* Make sure that no pending write process in the physical drive */
+       if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK)
+         res = FR_DISK_ERR;
+#if !REALTIME
+      }
+#endif
+
+#if REALTIME
+      if (fs->buffer_used) if (sync_win_buffers(fs) != RES_OK) return FR_DISK_ERR;
+      move_window(fs, 0);
+#endif
+
+      return res;
+    }
+#endif
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* FAT access - Read value of a FAT entry                                */
+  /*-----------------------------------------------------------------------*/
+
+  /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ 
+  DWORD get_fat (FATFS *fs, DWORD clst)        /* Cluster# to get the link information */
+  {
+    UINT wc, bc;
+    DWORD fsect;
+
+    if (clst < 2 || clst >= fs->max_clust)     /* Range check */
+      return 1;
+
+    fsect = fs->fatbase;
+    switch (fs->fs_type) {
+    case FS_FAT12:
+      bc = clst; bc += bc / 2;
+      if (move_window(fs, fsect + (bc / SS(fs)))) break;
+      wc = fs->win[bc & (SS(fs) - 1)]; bc++;
+      if (move_window(fs, fsect + (bc / SS(fs)))) break;
+      wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8;
+      return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+    case FS_FAT16 :
+      if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break;
+      return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]);
+
+    case FS_FAT32 :
+      if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) 
+       break;
+      return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF;
+    }
+
+    return 0xFFFFFFFF; /* An error occured at the disk I/O layer */
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /*  FAT access - Change value of a FAT entry                             */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+  FRESULT put_fat (FATFS *fs,  /* File system object */
+                  DWORD clst,  /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */
+                  DWORD val)   /* New value to mark the cluster */
+  {
+    UINT bc;
+    BYTE *p;
+    DWORD fsect;
+    FRESULT res;
+
+    if (clst < 2 || clst >= fs->max_clust) {   /* Range check */
+      res = FR_INT_ERR;
+    } 
+    else {
+      fsect = fs->fatbase;
+      switch (fs->fs_type) {
+      case FS_FAT12 :
+       bc = clst; bc += bc / 2;
+       res = move_window(fs, fsect + (bc / SS(fs)));
+       if (res != FR_OK) break;
+       p = &fs->win[bc & (SS(fs) - 1)];
+       *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+       bc++;
+       fs->wflag = 1;
+       res = move_window(fs, fsect + (bc / SS(fs)));
+       if (res != FR_OK) break;
+       p = &fs->win[bc & (SS(fs) - 1)];
+       *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+       break;
+
+      case FS_FAT16 :
+       res = move_window(fs, fsect + (clst / (SS(fs) / 2)));
+       if (res != FR_OK) break;
+       ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val);
+       break;
+
+      case FS_FAT32 :
+       res = move_window(fs, fsect + (clst / (SS(fs) / 4)));
+       if (res != FR_OK) break;
+       ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val);
+       break;
+
+      default :
+       res = FR_INT_ERR;
+      }
+      fs->wflag = 1;
+    }
+
+    return res;
+  }
+#endif /* !_FS_READONLY */
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Remove a cluster chain                                                */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+  static FRESULT remove_chain (FATFS *fs,      DWORD clst)                     /* Cluster# to a remove chain from */
+    {
+      FRESULT res;
+      DWORD nxt;
+
+      if (clst < 2 || clst >= fs->max_clust) { /* Check the range of cluster# */
+       res = FR_INT_ERR;
+      } 
+      else {
+       res = FR_OK;
+       while (clst < fs->max_clust) {                  /* Not a last link? */
+         nxt = get_fat(fs, clst);              /* Get cluster status */
+         if (nxt == 0) break;                          /* Empty cluster? */
+         if (nxt == 1) { res = FR_INT_ERR; break; }    /* Internal error? */
+         if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }  /* Disk error? */
+         res = put_fat(fs, clst, 0);           /* Mark the cluster "empty" */
+         if (res != FR_OK) break;
+         if (fs->free_clust != 0xFFFFFFFF) {   /* Update FSInfo */
+           fs->free_clust++;
+           fs->fsi_flag = 1;
+         }
+         clst = nxt;   /* Next cluster */
+       }
+      }
+
+      return res;
+    }
+#endif
+
+  /*-----------------------------------------------------------------------*/
+  /* FAT handling - Stretch or Create a cluster chain                      */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+  /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+  static DWORD create_chain (FATFS *fs, DWORD clst)                    /* Cluster# to stretch. 0 means create a new chain. */
+    {
+      DWORD cs, ncl, scl, mcl;
+
+      mcl = fs->max_clust;
+      if (clst == 0) {         /* Create new chain */
+       scl = fs->last_clust;                   /* Get suggested start point */
+       if (scl == 0 || scl >= mcl) scl = 1;
+      }
+      else {                                   /* Stretch existing chain */
+       cs = get_fat(fs, clst);         /* Check the cluster status */
+       if (cs < 2) return 1;                   /* It is an invalid cluster */
+       if (cs < mcl) return cs;                /* It is already followed by next cluster */
+       scl = clst;
+      }
+
+      ncl = scl;                               /* Start cluster */
+      for (;;) {
+       ncl++;                                                  /* Next cluster */
+       if (ncl >= mcl) {                               /* Wrap around */
+         ncl = 2;
+         if (ncl > scl) return 0;      /* No free custer */
+       }
+       cs = get_fat(fs, ncl);          /* Get the cluster status */
+       if (cs == 0) break;                             /* Found a free cluster */
+       if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */
+         return cs;
+       if (ncl == scl) return 0;               /* No free custer */
+      }
+
+      if (put_fat(fs, ncl, 0x0FFFFFFF))        /* Mark the new cluster "in use" */
+       return 0xFFFFFFFF;
+      if (clst != 0) {                                 /* Link it to the previous one if needed */
+       if (put_fat(fs, clst, ncl))
+         return 0xFFFFFFFF;
+      }
+
+      fs->last_clust = ncl;                            /* Update FSINFO */
+      if (fs->free_clust != 0xFFFFFFFF) {
+       fs->free_clust--;
+       fs->fsi_flag = 1;
+      }
+
+      return ncl;              /* Return new cluster number */
+    }
+#endif /* !_FS_READONLY */
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Get sector# from cluster#                                             */
+  /*-----------------------------------------------------------------------*/
+
+  /* !=0: sector number, 0: failed - invalid cluster# */
+  static DWORD clust2sect (FATFS *fs, DWORD clst)              /* Cluster# to be converted */
+    {
+      clst -= 2;
+      if (clst >= (fs->max_clust - 2)) return 0;               /* Invalid cluster# */
+      return clst * fs->csize + fs->database;
+    }
+
+  /*-----------------------------------------------------------------------*/
+  /* Directory handling - Seek directory index                             */
+  /*-----------------------------------------------------------------------*/
+
+  static FRESULT dir_seek (DIR *dj, WORD idx)  /* Pointer to directory object, Directory index number */
+    {
+      DWORD clst;
+      WORD ic;
+
+      dj->index = idx;
+      clst = dj->sclust;
+      if (clst == 1 || clst >= dj->fs->max_clust)      /* Check start cluster range */
+       return FR_INT_ERR;
+      if (!clst && dj->fs->fs_type == FS_FAT32)        /* Replace cluster# 0 with root cluster# if in FAT32 */
+       clst = dj->fs->dirbase;
+
+      if (clst == 0) { /* Static table */
+       dj->clust = clst;
+       if (idx >= dj->fs->n_rootdir)           /* Index is out of range */
+         return FR_INT_ERR;
+       dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32);   /* Sector# */
+      }
+      else {                           /* Dynamic table */
+       ic = SS(dj->fs) / 32 * dj->fs->csize;   /* Entries per cluster */
+       while (idx >= ic) {                        /* Follow cluster chain */
+         clst = get_fat(dj->fs, clst);                 /* Get next cluster */
+         if (clst == 0xFFFFFFFF) return FR_DISK_ERR;   /* Disk error */
+         if (clst < 2 || clst >= dj->fs->max_clust)    /* Reached to end of table or int error */
+           return FR_INT_ERR;
+         idx -= ic;
+       }
+       dj->clust = clst;
+       dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32);  /* Sector# */
+      }
+      dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32;
+
+      return FR_OK;    /* Seek succeeded */
+    }
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Directory handling - Move directory index next                        */
+  /*-----------------------------------------------------------------------*/
+
+  /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */
+  static FRESULT dir_next (DIR *dj, BOOL streach)    /* FALSE: Do not streach table, TRUE: Streach table if needed */
+    {
+      DWORD clst;
+      WORD i;
+
+      i = dj->index + 1;
+      if (!i || !dj->sect)     /* Report EOT when index has reached 65535 */
+       return FR_NO_FILE;
+
+      if (!(i % (SS(dj->fs) / 32))) {  /* Sector changed? */
+       dj->sect++;                                     /* Next sector */
+
+       if (dj->clust == 0) {   /* Static table */
+         if (i >= dj->fs->n_rootdir)   /* Report EOT when end of table */
+           return FR_NO_FILE;
+       }
+       else {                                  /* Dynamic table */
+         if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) {   /* Cluster changed? */
+           clst = get_fat(dj->fs, dj->clust);                  /* Get next cluster */
+           if (clst <= 1) return FR_INT_ERR;
+           if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+           if (clst >= dj->fs->max_clust) {                            /* When it reached end of dynamic table */
+#if !_FS_READONLY
+             BYTE c;
+             if (!streach) return FR_NO_FILE;                  /* When do not streach, report EOT */
+             clst = create_chain(dj->fs, dj->clust);           /* Streach cluster chain */
+             if (clst == 0) return FR_DENIED;                  /* No free cluster */
+             if (clst == 1) return FR_INT_ERR;
+             if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+             /* Clean-up streached table */
+             if (move_window(dj->fs, 0)) return FR_DISK_ERR;   /* Flush active window */
+             memset(dj->fs->win, 0, SS(dj->fs));                       /* Clear window buffer */
+             dj->fs->winsect = clust2sect(dj->fs, clst);       /* Cluster start sector */
+             for (c = 0; c < dj->fs->csize; c++) {             /* Fill the new cluster with 0 */
+               dj->fs->wflag = 1;
+               if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+               dj->fs->winsect++;
+             }
+             dj->fs->winsect -= c;                                             /* Rewind window address */
+#else
+             return FR_NO_FILE;                        /* Report EOT */
+#endif
+           }
+           dj->clust = clst;                           /* Initialize data for new cluster */
+           dj->sect = clust2sect(dj->fs, clst);
+         }
+       }
+      }
+
+      dj->index = i;
+      dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
+
+      return FR_OK;
+    }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+  /*-----------------------------------------------------------------------*/
+#if _USE_LFN
+  static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};    /* Offset of LFN chars in the directory entry */
+
+  /* TRUE:Matched, FALSE:Not matched */
+  static BOOL cmp_lfn (WCHAR *lfnbuf, BYTE *dir) /* Pointer to the LFN to be compared, Pointer to the directory entry containing a part of LFN */
+    {
+      int i, s;
+      WCHAR wc, uc;
+      
+      i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13;   /* Get offset in the LFN buffer */
+      s = 0; wc = 1;
+      do {
+       uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
+       if (wc) {       /* Last char has not been processed */
+         wc = ff_wtoupper(uc);         /* Convert it to upper case */
+         if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))  /* Compare it */
+           return FALSE;                       /* Not matched */
+       } 
+       else if (uc != 0xFFFF) 
+         return FALSE; /* Check filler */
+      } while (++s < 13);                              /* Repeat until all chars in the entry are checked */
+      
+      if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i])   /* Last segment matched but different length */
+       return FALSE;
+      
+      return TRUE;                                     /* The part of LFN matched */
+    }
+  
+  /* TRUE:Succeeded, FALSE:Buffer overflow */
+  static BOOL pick_lfn (WCHAR *lfnbuf, BYTE *dir) /* Pointer to the Unicode-LFN buffer, Pointer to the directory entry */
+    {
+      int i, s;
+      WCHAR wc, uc;
+
+
+      i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;   /* Offset in the LFN buffer */
+
+      s = 0; wc = 1;
+      do {
+       uc = LD_WORD(dir+LfnOfs[s]);                    /* Pick an LFN character from the entry */
+       if (wc) {       /* Last char has not been processed */
+         if (i >= _MAX_LFN) 
+           return FALSE;       /* Buffer overflow? */
+         lfnbuf[i++] = wc = uc;                                /* Store it */
+       } 
+       else if (uc != 0xFFFF) 
+         return FALSE;         /* Check filler */
+      } while (++s < 13);                                              /* Read all character in the entry */
+
+      if (dir[LDIR_Ord] & 0x40) {                              /* Put terminator if it is the last LFN part */
+       if (i >= _MAX_LFN) return FALSE;        /* Buffer overflow? */
+       lfnbuf[i] = 0;
+      }
+
+      return TRUE;
+    }
+
+
+#if !_FS_READONLY
+  static void fit_lfn (const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
+                      BYTE *dir,                       /* Pointer to the directory entry */
+                      BYTE ord,                        /* LFN order (1-20) */
+                      BYTE sum)                        /* SFN sum */
+    {
+      int i, s;
+      WCHAR wc;
+
+
+      dir[LDIR_Chksum] = sum;                  /* Set check sum */
+      dir[LDIR_Attr] = AM_LFN;         /* Set attribute. LFN entry */
+      dir[LDIR_Type] = 0;
+      ST_WORD(dir+LDIR_FstClusLO, 0);
+
+      i = (ord - 1) * 13;                              /* Get offset in the LFN buffer */
+      s = wc = 0;
+      do {
+       if (wc != 0xFFFF) wc = lfnbuf[i++];     /* Get an effective char */
+       ST_WORD(dir+LfnOfs[s], wc);     /* Put it */
+       if (!wc) wc = 0xFFFF;           /* Padding chars following last char */
+      } while (++s < 13);
+      if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40;     /* Bottom LFN part is the start of LFN sequence */
+      dir[LDIR_Ord] = ord;                     /* Set the LFN order */
+    }
+
+#endif
+#endif
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Create numbered name                                                  */
+  /*-----------------------------------------------------------------------*/
+#if _USE_LFN
+  void gen_numname (BYTE *dst,                 /* Pointer to genartated SFN */
+                   const BYTE *src,    /* Pointer to source SFN to be modified */
+                   const WCHAR *lfn,   /* Pointer to LFN */
+                   WORD num)                   /* Sequense number */
+  {
+    char ns[8];
+    int i, j;
+
+
+    memcpy(dst, src, 11);
+
+    if (num > 5) {     /* On many collisions, generate a hash number instead of sequencial number */
+      do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn);
+    }
+
+    /* itoa */
+    i = 7;
+    do {
+      ns[i--] = (num % 10) + '0';
+      num /= 10;
+    } while (num);
+    ns[i] = '~';
+
+    /* Append the number */
+    for (j = 0; j < i && dst[j] != ' '; j++) {
+      if (IsDBCS1(dst[j])) {
+       if (j == i - 1) break;
+       j++;
+      }
+    }
+    do {
+      dst[j++] = (i < 8) ? ns[i++] : ' ';
+    } while (j < 8);
+  }
+#endif
+
+  /*-----------------------------------------------------------------------*/
+  /* Calculate sum of an SFN                                               */
+  /*-----------------------------------------------------------------------*/
+#if _USE_LFN
+  static BYTE sum_sfn (const BYTE *dir)                /* Ptr to directory entry */
+    {
+      BYTE sum = 0;
+      int n = 11;
+
+      do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+      return sum;
+    }
+#endif
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Directory handling - Find an object in the directory                  */
+  /*-----------------------------------------------------------------------*/
+
+  static FRESULT dir_find (DIR *dj)            /* Pointer to the directory object linked to the file name */
+    {
+      FRESULT res;
+      BYTE c, *dir;
+#if _USE_LFN
+      BYTE a, ord, sum;
+#endif
+
+      res = dir_seek(dj, 0);                   /* Rewind directory object */
+      if (res != FR_OK) return res;
+
+#if _USE_LFN
+      ord = sum = 0xFF; 
+#endif
+      do {
+       res = move_window(dj->fs, dj->sect);
+       if (res != FR_OK) break;
+#if REALTIME
+       dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+       dir = dj->dir;                                  /* Ptr to the directory entry of current index */
+#endif
+       c = dir[DIR_Name];
+       if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
+#if _USE_LFN   /* LFN configuration */
+       a = dir[DIR_Attr] & AM_MASK;
+       if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) {       /* An entry without valid data */
+         ord = 0xFF;
+       } 
+       else {
+         if (a == AM_LFN) {                    /* An LFN entry is found */
+           if (dj->lfn) {
+             if (c & 0x40) {           /* Is it start of LFN sequence? */
+               sum = dir[LDIR_Chksum];
+               c &= 0xBF; ord = c;             /* LFN start order */
+               dj->lfn_idx = dj->index;
+             }
+             /* Check validity of the LFN entry and compare it with given name */
+             ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+           }
+         } 
+         else {                                        /* An SFN entry is found */
+           if (!ord && sum == sum_sfn(dir)) break;     /* LFN matched? */
+           ord = 0xFF; dj->lfn_idx = 0xFFFF;   /* Reset LFN sequence */
+           if (!(dj->fn[NS] & NS_LOSS) && !memcmp(dir, dj->fn, 11)) break;     /* SFN matched? */
+         }
+       }
+#else          /* Non LFN configuration */
+       if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+         break;
+#endif
+       res = dir_next(dj, FALSE);                              /* Next entry */
+      } while (res == FR_OK);
+      
+      return res;
+    }
+  
+
+  
+  
+  /*-----------------------------------------------------------------------*/
+  /* Read an object from the directory                                     */
+  /*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+  static FRESULT dir_read (DIR *dj)            /* Pointer to the directory object to store read object name */
+    {
+      FRESULT res;
+      BYTE c, *dir;
+#if _USE_LFN
+      BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+      res = FR_NO_FILE;
+      while (dj->sect) {
+       res = move_window(dj->fs, dj->sect);
+       if (res != FR_OK) break;
+#if REALTIME
+       dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+       dir = dj->dir;                                  /* Ptr to the directory entry of current index */
+#endif
+       c = dir[DIR_Name];
+       if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
+#if _USE_LFN   /* LFN configuration */
+       a = dir[DIR_Attr] & AM_MASK;
+       if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {   /* An entry without valid data */
+         ord = 0xFF;
+       } else {
+         if (a == AM_LFN) {                    /* An LFN entry is found */
+           if (c & 0x40) {                     /* Is it start of LFN sequence? */
+             sum = dir[LDIR_Chksum];
+             c &= 0xBF; ord = c;
+             dj->lfn_idx = dj->index;
+           }
+           /* Check LFN validity and capture it */
+           ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+         } else {                              /* An SFN entry is found */
+           if (ord || sum != sum_sfn(dir))     /* Is there a valid LFN? */
+             dj->lfn_idx = 0xFFFF;             /* It has no LFN. */
+           break;
+         }
+       }
+#else          /* Non LFN configuration */
+       if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))  /* Is it a valid entry? */
+         break;
+#endif
+       res = dir_next(dj, FALSE);                              /* Next entry */
+       if (res != FR_OK) break;
+      }
+
+      if (res != FR_OK) dj->sect = 0;
+
+      return res;
+    }
+#endif
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Register an object to the directory                                   */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+  static FRESULT dir_register (DIR *dj)                                /* Target directory with object name to be created */
+    {
+      FRESULT res;
+      BYTE c, *dir;
+
+#if _USE_LFN   /* LFN configuration */
+      WORD n, ne, is;
+      BYTE sn[12], *fn, sum;
+      WCHAR *lfn;
+
+
+      fn = dj->fn; lfn = dj->lfn;
+      memcpy(sn, fn, 12);
+
+      if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME;      /* Cannot create dot entry */
+
+      if (sn[NS] & NS_LOSS) {                  /* When LFN is out of 8.3 format, generate a numbered name */
+       fn[NS] = 0; dj->lfn = NULL;                     /* Find only SFN */
+       for (n = 1; n < 100; n++) {
+         gen_numname(fn, sn, lfn, n);  /* Generate a numbered name */
+         res = dir_find(dj);                           /* Check if the name collides with existing SFN */
+         if (res != FR_OK) break;
+       }
+       if (n == 100) return FR_DENIED;         /* Abort if too many collisions */
+       if (res != FR_NO_FILE) return res;      /* Abort if the result is other than 'not collided' */
+       fn[NS] = sn[NS]; dj->lfn = lfn;
+      }
+
+      if (sn[NS] & NS_LFN) {                   /* When LFN is to be created, reserve reserve an SFN + LFN entries. */
+       for (ne = 0; lfn[ne]; ne++) ;
+       ne = (ne + 25) / 13;
+      } else {                                         /* Otherwise reserve only an SFN entry. */
+       ne = 1;
+      }
+
+      /* Reserve contiguous entries */
+      res = dir_seek(dj, 0);
+      if (res != FR_OK) return res;
+      n = is = 0;
+      do {
+       res = move_window(dj->fs, dj->sect);
+       if (res != FR_OK) break;
+#if REALTIME
+       c = *(get_dir_ptr(dj->fs, dj->dir));
+#else
+       c = *dj->dir;                           /* Check the entry status */
+#endif
+       if (c == 0xE5 || c == 0) {      /* Is it a blank entry? */
+         if (n == 0) is = dj->index;   /* First index of the contigulus entry */
+         if (++n == ne) break; /* A contiguous entry that requiered count is found */
+       } else {
+         n = 0;                                        /* Not a blank entry. Restart to search */
+       }
+       res = dir_next(dj, TRUE);       /* Next entry with table streach */
+      } while (res == FR_OK);
+
+      if (res == FR_OK && ne > 1) {    /* Initialize LFN entry if needed */
+       res = dir_seek(dj, is);
+       if (res == FR_OK) {
+         sum = sum_sfn(dj->fn);        /* Sum of the SFN tied to the LFN */
+         ne--;
+         do {                                  /* Store LFN entries in bottom first */
+           res = move_window(dj->fs, dj->sect);
+           if (res != FR_OK) break;
+#if REALTIME
+           dj->dir = get_dir_ptr(dj->fs, dj->dir);
+#endif
+           fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+           dj->fs->wflag = 1;
+           res = dir_next(dj, FALSE);  /* Next entry */
+         } while (res == FR_OK && --ne);
+       }
+      }
+
+#else  /* Non LFN configuration */
+      res = dir_seek(dj, 0);
+      if (res == FR_OK) {
+       do {    /* Find a blank entry for the SFN */
+         res = move_window(dj->fs, dj->sect);
+         if (res != FR_OK) break;
+#if REALTIME
+         c = *(get_dir_ptr(dj->dir));
+#else
+         c = *dj->dir;
+#endif
+         if (c == 0xE5 || c == 0) break;       /* Is it a blank entry? */
+         res = dir_next(dj, TRUE);             /* Next entry with table streach */
+       } while (res == FR_OK);
+      }
+#endif
+
+      if (res == FR_OK) {              /* Initialize the SFN entry */
+       res = move_window(dj->fs, dj->sect);
+       if (res == FR_OK) {
+#if REALTIME
+         dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+         dir = dj->dir;
+#endif
+         memset(dir, 0, 32);           /* Clean the entry */
+         memcpy(dir, dj->fn, 11);      /* Put SFN */
+         dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT);   /* Put NT flag */
+         dj->fs->wflag = 1;
+       }
+      }
+
+      return res;
+    }
+#endif /* !_FS_READONLY */
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Remove an object from the directory                                   */
+  /*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+  /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+  static FRESULT dir_remove (DIR *dj)                          /* Directory object pointing the entry to be removed */
+    {
+      FRESULT res;
+
+#if _USE_LFN   /* LFN configuration */
+      WORD i;
+
+      i = dj->index;   /* SFN index */
+      res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));   /* Goto the SFN or top of the LFN entries */
+      if (res == FR_OK) {
+       do {
+         res = move_window(dj->fs, dj->sect);
+         if (res != FR_OK) break;
+#if REALTIME
+         dj->dir = get_dir_ptr(dj->fs, dj->dir);
+#endif
+         *dj->dir = 0xE5;                      /* Mark the entry "deleted" */
+         dj->fs->wflag = 1;
+         if (dj->index >= i) break;    /* When SFN is deleted, all entries of the object is deleted. */
+         res = dir_next(dj, FALSE);    /* Next entry */
+       } while (res == FR_OK);
+       if (res == FR_NO_FILE) res = FR_INT_ERR;
+      }
+
+#else                  /* Non LFN configuration */
+      res = dir_seek(dj, dj->index);
+      if (res == FR_OK) {
+       res = move_window(dj->fs, dj->sect);
+       if (res == FR_OK) {
+#if REALTIME
+         dj->dir = get_dir_ptr(dj->fs, dj->dir);
+#endif
+         *dj->dir = 0xE5;                      /* Mark the entry "deleted" */
+         dj->fs->wflag = 1;
+       }
+      }
+#endif
+
+      return res;
+    }
+#endif /* !_FS_READONLY */
+
+  /*-----------------------------------------------------------------------*/
+  /* Pick a segment and create the object name in directory form           */
+  /*-----------------------------------------------------------------------*/
+
+  static
+    FRESULT create_name (DIR *dj, const XCHAR **path)  /* Pointer to pointer to the segment in the path string */
+    {
+#ifdef _EXCVT
+      static const BYTE cvt[] = _EXCVT;
+#endif
+#if _USE_LFN   /* LFN configuration */
+      BYTE b, cf;
+      WCHAR w, *lfn;
+      int i, ni, si, di;
+      const XCHAR *p;
+
+      /* Create LFN in Unicode */
+      si = di = 0;
+      p = *path;
+      lfn = dj->lfn;
+      for (;;) {
+       w = p[si++];                                    /* Get a character */
+       if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
+       if (di >= _MAX_LFN)                             /* Reject too long name */
+         return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+       w &= 0xFF;
+       if (IsDBCS1(w)) {                               /* If it is a DBC 1st byte */
+         b = p[si++];                  /* Get 2nd byte */
+         if (!IsDBCS2(b))                      /* Reject invalid code for DBC */
+           return FR_INVALID_NAME;
+         w = (w << 8) + b;
+       }
+       w = ff_convert(w, 1);                   /* Convert OEM to Unicode */
+       if (!w) return FR_INVALID_NAME; /* Reject invalid code */
+#endif
+       if (w < 0x80 && strchr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+         return FR_INVALID_NAME;
+       lfn[di++] = w;                                  /* Store the Unicode char */
+      }
+      *path = &p[si];                                          /* Rerurn pointer to the next segment */
+      cf = (w < ' ') ? NS_LAST : 0;            /* Set last segment flag if end of path */
+#if _FS_RPATH
+      if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */
+         (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == L'.')) {
+       lfn[di] = 0;
+       for (i = 0; i < 11; i++)
+         dj->fn[i] = (i < di) ? '.' : ' ';
+       dj->fn[i] = cf | NS_DOT;                /* This is a dot entry */
+       return FR_OK;
+      }
+#endif
+      while (di) {                                             /* Strip trailing spaces and dots */
+       w = lfn[di - 1];
+       if (w != ' ' && w != '.') break;
+       di--;
+      }
+      if (!di) return FR_INVALID_NAME; /* Reject null string */
+
+      lfn[di] = 0;                                             /* LFN is created */
+
+      /* Create SFN in directory form */
+      memset(dj->fn, ' ', 11);
+      for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;   /* Strip leading spaces and dots */
+      if (si) cf |= NS_LOSS | NS_LFN;
+      while (di && lfn[di - 1] != '.') di--;   /* Find extension (di<=si: no extension) */
+
+      b = i = 0; ni = 8;
+      for (;;) {
+       w = lfn[si++];                                  /* Get an LFN char */
+       if (!w) break;                                  /* Break on enf of the LFN */
+       if (w == ' ' || (w == '.' && si != di)) {       /* Remove spaces and dots */
+         cf |= NS_LOSS | NS_LFN; continue;
+       }
+
+       if (i >= ni || si == di) {              /* Extension or end of SFN */
+         if (ni == 11) {                               /* Long extension */
+           cf |= NS_LOSS | NS_LFN; break;
+         }
+         if (si != di) 
+           cf |= NS_LOSS | NS_LFN;     /* Out of 8.3 format */
+         if (si > di) break;           /* No extension */
+         si = di; i = 8; ni = 11;      /* Enter extension section */
+         b <<= 2; continue;
+       }
+
+       if (w >= 0x80) {                                /* Non ASCII char */
+#ifdef _EXCVT
+         w = ff_convert(w, 0);         /* Unicode -> OEM code */
+         if (w) 
+           w = cvt[w - 0x80];          /* Convert extend char to upper (SBCS) */
+#else
+         w = ff_convert(ff_wtoupper(w), 0);    /* Upper converted Unicode -> OEM code */
+#endif
+         cf |= NS_LFN;                         /* Force create LFN entry */
+       }
+
+       if (_DF1S && w >= 0x100) {              /* Double byte char */
+         if (i >= ni - 1) {
+           cf |= NS_LOSS | NS_LFN; i = ni; continue;
+         }
+         dj->fn[i++] = (BYTE)(w >> 8);
+       } else {                                                /* Single byte char */
+         if (!w || strchr("+,;[=]", w)) {      /* Replace illegal chars for SFN */
+           w = '_'; cf |= NS_LOSS | NS_LFN;    /* Lossy conversion */
+         } else {
+           if (IsUpper(w)) {           /* ASCII Large capital */
+             b |= 2;
+           } else {
+             if (IsLower(w)) {         /* ASCII Small capital */
+               b |= 1; w -= 0x20;
+             }
+           }
+         }
+       }
+       dj->fn[i++] = (BYTE)w;
+      }
+
+      if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */
+
+      if (ni == 8) b <<= 2;
+      if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)    /* Create LFN entry when there are composite capitals */
+       cf |= NS_LFN;
+      if (!(cf & NS_LFN)) {                                            /* When LFN is in 8.3 format without extended char, NT flags are created */
+       if ((b & 0x03) == 0x01) cf |= NS_EXT;   /* NT flag (Extension has only small capital) */
+       if ((b & 0x0C) == 0x04) cf |= NS_BODY;  /* NT flag (Filename has only small capital) */
+      }
+
+      dj->fn[NS] = cf; /* SFN is created */
+
+      return FR_OK;
+
+#else  /* Non-LFN configuration */
+      BYTE b, c, d, *sfn;
+      int ni, si, i;
+      const char *p;
+
+      /* Create file name in directory form */
+      sfn = dj->fn;
+      memset(sfn, ' ', 11);
+      si = i = b = 0; ni = 8;
+      p = *path;
+#if _FS_RPATH
+      if (p[si] == '.') { /* Is this a dot entry? */
+       for (;;) {
+         c = p[si++];
+         if (c != '.' || si >= 3) break;
+         sfn[i++] = c;
+       }
+       if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+       *path = &p[si];                                                                 /* Rerurn pointer to the next segment */
+       sfn[NS] = (c < ' ') ? NS_LAST|NS_DOT : NS_DOT;  /* Set last segment flag if end of path */
+       return FR_OK;
+      }
+#endif
+      for (;;) {
+       c = p[si++];
+       if (c <= ' ' || c == '/' || c == '\\') break;   /* Break on end of segment */
+       if (c == '.' || i >= ni) {
+         if (ni != 8 || c != '.') return FR_INVALID_NAME;
+         i = 8; ni = 11;
+         b <<= 2; continue;
+       }
+       if (c >= 0x80) {                                /* Extended char */
+#ifdef _EXCVT
+         c = cvt[c - 0x80];                    /* Convert extend char (SBCS) */
+#else
+         b |= 3;                                               /* Eliminate NT flag if ext char is exist */
+#if !_DF1S     /* ASCII only cfg */
+         return FR_INVALID_NAME;
+#endif
+#endif
+       }
+       if (IsDBCS1(c)) {                       /* DBC 1st byte? */
+         d = p[si++];                          /* Get 2nd byte */
+         if (!IsDBCS2(d) || i >= ni - 1)       /* Reject invalid DBC */
+           return FR_INVALID_NAME;
+         sfn[i++] = c;
+         sfn[i++] = d;
+       } else {
+         if (strchr(" \"*+,[=]|\x7F", c))      /* Reject illegal chrs for SFN */
+           return FR_INVALID_NAME;
+         if (IsUpper(c)) {                     /* ASCII large capital? */
+           b |= 2;
+         } else {
+           if (IsLower(c)) {                   /* ASCII small capital? */
+             b |= 1; c -= 0x20;
+           }
+         }
+         sfn[i++] = c;
+       }
+      }
+      *path = &p[si];                                          /* Rerurn pointer to the next segment */
+      c = (c <= ' ') ? NS_LAST : 0;            /* Set last segment flag if end of path */
+
+      if (!i) return FR_INVALID_NAME;          /* Reject null string */
+      if (sfn[0] == 0xE5) sfn[0] = 0x05;       /* When first char collides with 0xE5, replace it with 0x05 */
+
+      if (ni == 8) b <<= 2;
+      if ((b & 0x03) == 0x01) c |= NS_EXT;     /* NT flag (Extension has only small capital) */
+      if ((b & 0x0C) == 0x04) c |= NS_BODY;    /* NT flag (Filename has only small capital) */
+
+      sfn[NS] = c;             /* Store NT flag, File name is created */
+
+      return FR_OK;
+#endif
+    }
+
+  /*-----------------------------------------------------------------------*/
+  /* Get file information from directory entry                             */
+  /*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+  static void get_fileinfo (DIR *dj, FILINFO *fno)             /* Pointer to the file information to be filled */
+    {
+      int i;
+      BYTE c, nt, *dir;
+      char *p;
+
+
+      p = fno->fname;
+      if (dj->sect) {
+#if REALTIME
+       dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+       dir = dj->dir;
+#endif
+       nt = dir[DIR_NTres];            /* NT flag */
+       for (i = 0; i < 8; i++) {       /* Copy name body */
+         c = dir[i];
+         if (c == ' ') break;
+         if (c == 0x05) c = 0xE5;
+         if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+         *p++ = c;
+       }
+       if (dir[8] != ' ') {            /* Copy name extension */
+         *p++ = '.';
+         for (i = 8; i < 11; i++) {
+           c = dir[i];
+           if (c == ' ') break;
+           if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+           *p++ = c;
+         }
+       }
+       fno->fattrib = dir[DIR_Attr];                           /* Attribute */
+       fno->fsize = LD_DWORD(dir+DIR_FileSize);        /* Size */
+       fno->fdate = LD_WORD(dir+DIR_WrtDate);          /* Date */
+       fno->ftime = LD_WORD(dir+DIR_WrtTime);          /* Time */
+      }
+      *p = 0;
+
+#if _USE_LFN
+       if (fno->lfname) {
+               XCHAR *tp = fno->lfname;
+               WCHAR w, *lfn;
+
+       i = 0;
+       if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+         lfn = dj->lfn;
+         while ((w = *lfn++) != 0) {                   /* Get an LFN char */
+#if !_LFN_UNICODE
+           w = ff_convert(w, 0);                       /* Unicode -> OEM conversion */
+           if (!w) { i = 0; break; }           /* Could not convert, no LFN */
+           if (_DF1S && w >= 0x100)            /* Put 1st byte if it is a DBC */
+             tp[i++] = (XCHAR)(w >> 8);
+#endif
+           if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */
+           tp[i++] = (XCHAR)w;
+         }
+       }
+       tp[i] = 0;      /* Terminator */
+      }
+#endif
+    }
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Follow a file path                                                    */
+  /*-----------------------------------------------------------------------*/
+
+  /* FR_OK(0): successful, !=0: error code */
+  static FRESULT follow_path (DIR *dj, const char *path)       
+    /* Directory object to return last directory and found object, Full-path string to find a file or directory */
+    {
+      FRESULT res;
+      BYTE *dir, last;
+
+      while (!_USE_LFN && *path == ' ') path++;        /* Skip leading spaces */
+#if _FS_RPATH
+      if (*path == '/' || *path == '\\') { /* There is a heading separator */
+       path++; dj->sclust = 0;         /* Strip it and start from the root dir */
+      } else {                                                 /* No heading saparator */
+       dj->sclust = dj->fs->cdir;      /* Start from the current dir */
+      }
+#else
+      if (*path == '/' || *path == '\\')       /* Strip heading separator if exist */
+       path++;
+      dj->sclust = 0;                                          /* Start from the root dir */
+#endif
+
+      if ((UINT)*path < ' ') {                 /* Null path means the start directory itself */
+       res = dir_seek(dj, 0);
+       dj->dir = NULL;
+
+      } 
+      else {                                                   /* Follow path */
+       for (;;) {
+         res = create_name(dj, &path); /* Get a segment */
+         if (res != FR_OK) break;
+         res = dir_find(dj);                           /* Find it */
+         last = *(dj->fn+NS) & NS_LAST;
+         if (res != FR_OK) {                           /* Could not find the object */
+           if (res == FR_NO_FILE && !last)
+             res = FR_NO_PATH;
+           break;
+         }
+         if (last) break;                              /* Last segment match. Function completed. */
+#if REALTIME
+         dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+         dir = dj->dir;                                        /* There is next segment. Follow the sub directory */
+#endif
+         if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
+           res = FR_NO_PATH; break;
+         }
+         dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+       }
+      }
+
+      return res;
+    }
+
+  /*-----------------------------------------------------------------------*/
+  /* Load boot record and check if it is an FAT boot record                */
+  /*-----------------------------------------------------------------------*/
+
+  /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */
+  static BYTE check_fs (FATFS *fs, DWORD sect) /* File system object, Sector# (lba) to check if it is an FAT boot record or not */
+    {
+      if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK)    /* Load boot record */
+       return 3;
+      if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)                /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+       return 2;
+
+      if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)  /* Check "FAT" string */
+       return 0;
+      if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+       return 0;
+
+      return 1;
+    }
+
+  /*-----------------------------------------------------------------------*/
+  /* Make sure that the file system is valid                               */
+  /*-----------------------------------------------------------------------*/
+
+  /* FR_OK(0): successful, !=0: any error occured */
+  static FRESULT chk_mounted (const XCHAR **path,      /* Pointer to pointer to the path name (drive number) */
+                            FATFS **rfs,               /* Pointer to pointer to the found file system object */
+                            BYTE chk_wp)               /* !=0: Check media write protection for write access */
+    {
+      BYTE fmt, *tbl;
+      UINT vol;
+      DSTATUS stat;
+      DWORD bsect, fsize, tsect, mclst;
+      const XCHAR *p = *path;
+      FATFS *fs;
+
+
+      /* Get logical drive number from the path name */
+      vol = p[0] - '0';                                /* Is there a drive number? */
+      if (vol <= 9 && p[1] == ':') {   /* Found a drive number, get and strip it */
+       p += 2; *path = p;                      /* Return pointer to the path name */
+      } else {                                         /* No drive number is given */
+#if _FS_RPATH
+       vol = Drive;                            /* Use current drive */
+#else
+       vol = 0;                                        /* Use drive 0 */
+#endif
+      }
+
+      /* Check if the logical drive is valid or not */
+      if (vol >= _DRIVES)                      /* Is the drive number valid? */
+       return FR_INVALID_DRIVE;
+      *rfs = fs = FatFs[vol];                  /* Returen pointer to the corresponding file system object */
+      if (!fs) return FR_NOT_ENABLED;  /* Is the file system object available? */
+
+      ENTER_FF(fs);                                    /* Lock file system */
+
+      if (fs->fs_type) {                               /* If the logical drive has been mounted */
+       stat = disk_status(fs->drive);
+       if (!(stat & STA_NOINIT)) {     /* and the physical drive is kept initialized (has not been changed), */
+#if !_FS_READONLY
+         if (chk_wp && (stat & STA_PROTECT))   /* Check write protection if needed */
+           return FR_WRITE_PROTECTED;
+#endif
+         return FR_OK;                 /* The file system object is valid */
+       }
+      }
+
+      /* The logical drive must be mounted. Following code attempts to mount the volume */
+
+      fs->fs_type = 0;                                 /* Clear the file system object */
+      fs->drive = (BYTE)LD2PD(vol);            /* Bind the logical drive and a physical drive */
+      stat = disk_initialize(fs->drive);       /* Initialize low level disk I/O layer */
+      if (stat & STA_NOINIT)                           /* Check if the drive is ready */
+       return FR_NOT_READY;
+#if _MAX_SS != 512                                             /* Get disk sector size if needed */
+      if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+       return FR_NO_FILESYSTEM;
+#endif
+#if !_FS_READONLY
+      if (chk_wp && (stat & STA_PROTECT))      /* Check disk write protection if needed */
+       return FR_WRITE_PROTECTED;
+#endif
+      /* Search FAT partition on the drive */
+      fmt = check_fs(fs, bsect = 0);           /* Check sector 0 as an SFD format */
+      if (fmt == 1) {                                          /* Not an FAT boot record, it may be patitioned */
+       /* Check a partition listed in top of the partition table */
+       tbl = &fs->win[MBR_Table + LD2PT(vol) * 16];    /* Partition table */
+       if (tbl[4]) {                                                                   /* Is the partition existing? */
+         bsect = LD_DWORD(&tbl[8]);                                    /* Partition offset in LBA */
+         fmt = check_fs(fs, bsect);                                    /* Check the partition */
+       }
+      }
+      if (fmt == 3) return FR_DISK_ERR;
+      if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))    /* No valid FAT patition is found */
+       return FR_NO_FILESYSTEM;
+
+      /* Initialize the file system object */
+      fsize = LD_WORD(fs->win+BPB_FATSz16);                            /* Number of sectors per FAT */
+      if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32);
+      fs->sects_fat = fsize;
+      fs->n_fats = fs->win[BPB_NumFATs];                                       /* Number of FAT copies */
+      fsize *= fs->n_fats;                                                             /* (Number of sectors in FAT area) */
+      fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */
+      fs->csize = fs->win[BPB_SecPerClus];                             /* Number of sectors per cluster */
+      fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);      /* Nmuber of root directory entries */
+      tsect = LD_WORD(fs->win+BPB_TotSec16);                /* Number of sectors on the volume */
+      if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+      fs->max_clust = mclst = (tsect                                        /* Last cluster# + 1  (Number of clusters + 2) */
+                              - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32)
+                              ) / fs->csize + 2;
+
+      fmt = FS_FAT12;                                                                          /* Determine the FAT sub type */
+      if (mclst >= 0xFF7) fmt = FS_FAT16;                                      /* Number of clusters >= 0xFF5 */
+      if (mclst >= 0xFFF7) fmt = FS_FAT32;                             /* Number of clusters >= 0xFFF5 */
+
+      if (fmt == FS_FAT32)
+       fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);   /* Root directory start cluster */
+      else
+       fs->dirbase = fs->fatbase + fsize;                              /* Root directory start sector (lba) */
+      fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32);        /* Data start sector (lba) */
+
+#if !_FS_READONLY
+      /* Initialize allocation information */
+      fs->free_clust = 0xFFFFFFFF;
+      fs->wflag = 0;
+      /* Get fsinfo if needed */
+      if (fmt == FS_FAT32) {
+       fs->fsi_flag = 0;
+       fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+       if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK &&
+           LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
+           LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
+           LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
+         fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+         fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+       }
+      }
+#endif
+      fs->fs_type = fmt;               /* FAT sub-type */
+      fs->winsect = 0;         /* Invalidate sector cache */
+#if _FS_RPATH
+      fs->cdir = 0;                    /* Current directory (root dir) */
+#endif
+      fs->id = ++Fsid;         /* File system mount ID */
+
+      return FR_OK;
+    }
+
+  /*-----------------------------------------------------------------------*/
+  /* Check if the file/dir object is valid or not                          */
+  /*-----------------------------------------------------------------------*/
+
+  /* FR_OK(0): The object is valid, !=0: Invalid */
+  static FRESULT validate (FATFS *fs, WORD id) /* Member id of the target object to be checked */
+    {
+      if (!fs || !fs->fs_type || fs->id != id)
+       return FR_INVALID_OBJECT;
+
+      ENTER_FF(fs);            /* Lock file system */
+
+      if (disk_status(fs->drive) & STA_NOINIT)
+       return FR_NOT_READY;
+
+      return FR_OK;
+    }
+
+  /*--------------------------------------------------------------------------
+
+  Public Functions
+
+  --------------------------------------------------------------------------*/
+  /*-----------------------------------------------------------------------*/
+  /* Mount/Unmount a Locical Drive                                         */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_mount (BYTE vol,   /* Logical drive number to be mounted/unmounted */
+                  FATFS *fs)   /* Pointer to new file system object (NULL for unmount)*/
+  {
+    FATFS *rfs;
+    DSTATUS stat;
+
+    if (vol >= _DRIVES)                                /* Check if the drive number is valid */
+      return FR_INVALID_DRIVE;
+    rfs = FatFs[vol];                          /* Get current fs object */
+
+    if (rfs) {
+#if _FS_REENTRANT                                      /* Discard sync object of the current volume */
+      if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+      rfs->fs_type = 0;                        /* Clear old fs object */
+    }
+
+    FatFs[vol] = fs;                           /* Register new fs object */
+    if (fs) {
+      fs->fs_type = 0;                 /* Clear new fs object */
+#if _FS_REENTRANT                                      /* Create sync object for the new volume */
+      if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+      
+#if REALTIME
+      init_fatfs(fs);
+#endif
+      
+      fs->drive = (BYTE)LD2PD(vol);            /* Bind the logical drive and a physical drive */
+      stat = disk_initialize(fs->drive);       /* Initialize low level disk I/O layer */
+      if (stat & STA_NOINIT)                           /* Check if the drive is ready */
+       return FR_NOT_READY;
+    }
+
+    return FR_OK;
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Open or Create a File                                                 */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_open (FIL *fp,                     /* Pointer to the blank file object */
+                 const XCHAR *path,    /* Pointer to the file name */
+                 BYTE mode)                    /* Access mode and file open mode flags */
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+    fp->fs = NULL;             /* Clear file object */
+#if !_FS_READONLY
+    mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW);
+    res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)));
+#else
+    mode &= FA_READ;
+    res = chk_mounted(&path, &dj.fs, 0);
+#endif
+    if (res != FR_OK) 
+      LEAVE_FF(dj.fs, res);
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);      /* Follow the file path */
+#if !_FS_READONLY
+    /* Create or Open a file */
+    if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+      DWORD ps, cl;
+
+      if (res != FR_OK) {                              /* No file, create new */
+       if (res == FR_NO_FILE)                    /* There is no file to open, create a new entry */
+         res = dir_register(&dj);
+       if (res != FR_OK) LEAVE_FF(dj.fs, res);
+       mode |= FA_CREATE_ALWAYS;
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+       dir = dj.dir;                             /* Created entry (SFN entry) */
+#endif
+      }
+      else {                                   /* Any object is already existing */
+       if (mode & FA_CREATE_NEW)                       /* Cannot create new */
+         LEAVE_FF(dj.fs, FR_EXIST);
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+       dir = dj.dir;
+#endif
+       if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR)))        /* Cannot overwrite it (R/O or DIR) */
+         LEAVE_FF(dj.fs, FR_DENIED);
+       if (mode & FA_CREATE_ALWAYS) {          /* Resize it to zero on overwrite mode */
+         cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);  /* Get start cluster */
+         ST_WORD(dir+DIR_FstClusHI, 0);        /* cluster = 0 */
+         ST_WORD(dir+DIR_FstClusLO, 0);
+         ST_DWORD(dir+DIR_FileSize, 0);        /* size = 0 */
+         dj.fs->wflag = 1;
+         ps = dj.fs->winsect;                  /* Remove the cluster chain */
+         if (cl) {
+           res = remove_chain(dj.fs, cl);
+           if (res) LEAVE_FF(dj.fs, res);
+           dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */
+         }
+         res = move_window(dj.fs, ps);
+         if (res != FR_OK) LEAVE_FF(dj.fs, res);
+       }
+      }
+      if (mode & FA_CREATE_ALWAYS) {
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dir);
+#endif
+       dir[DIR_Attr] = 0;                                      /* Reset attribute */
+       ps = get_fattime();
+       ST_DWORD(dir+DIR_CrtTime, ps);          /* Created time */
+       dj.fs->wflag = 1;
+       mode |= FA__WRITTEN;                            /* Set file changed flag */
+      }
+    }
+    /* Open an existing file */
+    else {
+#endif /* !_FS_READONLY */
+      if (res != FR_OK) LEAVE_FF(dj.fs, res);  /* Follow failed */
+#if REALTIME
+      dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+      dir = dj.dir;
+#endif
+      if (!dir || (dir[DIR_Attr] & AM_DIR))    /* It is a directory */
+       LEAVE_FF(dj.fs, FR_NO_FILE);
+#if !_FS_READONLY
+      if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+       LEAVE_FF(dj.fs, FR_DENIED);
+    }
+    fp->dir_sect = dj.fs->winsect;             /* Pointer to the directory entry */
+    fp->dir_ptr = dj.dir;
+#endif
+    fp->flag = mode;                                   /* File access mode */
+    fp->org_clust =                                            /* File start cluster */
+      ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+    fp->fsize = LD_DWORD(dir+DIR_FileSize);    /* File size */
+    fp->fptr = 0; fp->csect = 255;             /* File pointer */
+    fp->dsect = 0;
+    fp->fs = dj.fs; fp->id = dj.fs->id;        /* Owner file system object of the file */
+
+#if REALTIME
+    if (fp && fp->fs && fp->fs->buffer_used) if (sync_win_buffers(fp->fs) != RES_OK) return FR_DISK_ERR;
+    if (fp && fp->fs) move_window(fp->fs, 0);
+#endif
+
+    LEAVE_FF(dj.fs, FR_OK);
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Read File                                                             */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_read (FIL *fp,             /* Pointer to the file object */
+                 void *buff,           /* Pointer to data buffer */
+                 UINT btr,             /* Number of bytes to read */
+                 UINT *br)             /* Pointer to number of bytes read */
+  {
+    FRESULT res;
+    DWORD clst, sect, remain;
+    UINT rcnt, cc;
+    BYTE *rbuff = buff;
+
+    *br = 0;   /* Initialize bytes read */
+
+    res = validate(fp->fs, fp->id);                                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                                          /* Check abort flag */
+      LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                                                 /* Check access mode */
+      LEAVE_FF(fp->fs, FR_DENIED);
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;                      /* Truncate btr by remaining bytes */
+
+    for ( ;  btr;                                                                      /* Repeat until all data transferred */
+         rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+      if ((fp->fptr % SS(fp->fs)) == 0) {                      /* On the sector boundary? */
+       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+         clst = (fp->fptr == 0) ?                      /* On the top of the file? */
+           fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+         if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+         if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+         fp->curr_clust = clst;                                /* Update current cluster */
+         fp->csect = 0;                                                /* Reset sector offset in the cluster */
+       }
+       sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
+       if (!sect) ABORT(fp->fs, FR_INT_ERR);
+       sect += fp->csect;
+       cc = btr / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
+       if (cc) {                                                               /* Read maximum contiguous sectors directly */
+         if (fp->csect + cc > fp->fs->csize)   /* Clip at cluster boundary */
+           cc = fp->fs->csize - fp->csect;
+         if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK)
+           ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2
+#if _FS_TINY
+         if (fp->fs->wflag && fp->fs->winsect - sect < cc)             /* Replace one of the read sectors with cached data if it contains a dirty sector */
+           memcpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+         if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)  /* Replace one of the read sectors with cached data if it contains a dirty sector */
+           memcpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+         fp->csect += (BYTE)cc;                                /* Next sector address in the cluster */
+         rcnt = SS(fp->fs) * cc;                               /* Number of bytes transferred */
+         continue;
+       }
+#if !_FS_TINY
+#if !_FS_READONLY
+       if (fp->flag & FA__DIRTY) {                     /* Write sector I/O buffer if needed */
+         if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+           ABORT(fp->fs, FR_DISK_ERR);
+         fp->flag &= ~FA__DIRTY;
+       }
+#endif
+       if (fp->dsect != sect) {                        /* Fill sector buffer with file data */
+         if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+           ABORT(fp->fs, FR_DISK_ERR);
+       }
+#endif
+       fp->dsect = sect;
+       fp->csect++;                                                    /* Next sector address in the cluster */
+      }
+      rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));     /* Get partial sector data from sector buffer */
+      if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+      if (move_window(fp->fs, fp->dsect))                      /* Move sector window */
+       ABORT(fp->fs, FR_DISK_ERR);
+      memcpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);        /* Pick partial sector */
+#else
+      memcpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
+#endif
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+  }
+
+#if !_FS_READONLY
+  /*-----------------------------------------------------------------------*/
+  /* Write File                                                            */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_write (FIL *fp,                    /* Pointer to the file object */
+                  const void *buff,    /* Pointer to the data to be written */
+                  UINT btw,                    /* Number of bytes to write */
+                  UINT *bw)                    /* Pointer to number of bytes written */
+  {
+    FRESULT res;
+    DWORD clst, sect;
+    UINT wcnt, cc;
+    const BYTE *wbuff = buff;
+
+
+    *bw = 0;   /* Initialize bytes written */
+
+    res = validate(fp->fs, fp->id);                                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                                          /* Check abort flag */
+      LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))                                                /* Check access mode */
+      LEAVE_FF(fp->fs, FR_DENIED);
+    if (fp->fsize + btw < fp->fsize) btw = 0;          /* File size cannot reach 4GB */
+
+    for ( ;  btw;                                                                      /* Repeat until all data transferred */
+         wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+      if ((fp->fptr % SS(fp->fs)) == 0) {                      /* On the sector boundary? */
+       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+         if (fp->fptr == 0) {                          /* On the top of the file? */
+           clst = fp->org_clust;                       /* Follow from the origin */
+           if (clst == 0)                                      /* When there is no cluster chain, */
+             fp->org_clust = clst = create_chain(fp->fs, 0);   /* Create a new cluster chain */
+         } else {                                                      /* Middle or end of the file */
+           clst = create_chain(fp->fs, fp->curr_clust);                        /* Follow or streach cluster chain */
+         }
+         if (clst == 0) break;                         /* Could not allocate a new cluster (disk full) */
+         if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+         if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+         fp->curr_clust = clst;                                /* Update current cluster */
+         fp->csect = 0;                                                /* Reset sector address in the cluster */
+       }
+#if _FS_TINY
+       if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))     /* Write back data buffer prior to following direct transfer */
+         ABORT(fp->fs, FR_DISK_ERR);
+#else
+       if (fp->flag & FA__DIRTY) {             /* Write back data buffer prior to following direct transfer */
+         if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+           ABORT(fp->fs, FR_DISK_ERR);
+         fp->flag &= ~FA__DIRTY;
+       }
+#endif
+       sect = clust2sect(fp->fs, fp->curr_clust);      /* Get current sector */
+       if (!sect) ABORT(fp->fs, FR_INT_ERR);
+       sect += fp->csect;
+       cc = btw / SS(fp->fs);                                  /* When remaining bytes >= sector size, */
+       if (cc) {                                                               /* Write maximum contiguous sectors directly */
+         if (fp->csect + cc > fp->fs->csize)   /* Clip at cluster boundary */
+           cc = fp->fs->csize - fp->csect;
+         if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK)
+           ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+         if (fp->fs->winsect - sect < cc) {  /* Refill sector cache if it gets dirty by the direct write */
+           memcpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+           fp->fs->wflag = 0;
+         }
+#else
+         if (fp->dsect - sect < cc) {  /* Refill sector cache if it gets dirty by the direct write */
+           memcpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+           fp->flag &= ~FA__DIRTY;
+         }
+#endif
+         fp->csect += (BYTE)cc;                                /* Next sector address in the cluster */
+         wcnt = SS(fp->fs) * cc;                               /* Number of bytes transferred */
+         continue;
+       }
+#if _FS_TINY
+       if (fp->fptr >= fp->fsize) {                    /* Avoid silly buffer filling at growing edge */
+         if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+         fp->fs->winsect = sect;
+       }
+#else
+       if (fp->dsect != sect) {                                /* Fill sector buffer with file data */
+         if (fp->fptr < fp->fsize){
+           if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK)
+             ABORT(fp->fs, FR_DISK_ERR);
+         }
+       }
+#endif
+       fp->dsect = sect;
+       fp->csect++;                                                    /* Next sector address in the cluster */
+      }
+      wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));     /* Put partial sector into file I/O buffer */
+      if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+      if (move_window(fp->fs, fp->dsect))                      /* Move sector window */
+       ABORT(fp->fs, FR_DISK_ERR);
+      memcpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);        /* Fit partial sector */
+      fp->fs->wflag = 1;
+#else
+      memcpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
+      fp->flag |= FA__DIRTY;
+#endif
+    }
+
+    if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;    /* Update file size if needed */
+    fp->flag |= FA__WRITTEN;                                           /* Set file changed flag */
+
+    LEAVE_FF(fp->fs, FR_OK);
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Synchronize the File Object                                           */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_sync (FIL *fp)     /* Pointer to the file object */
+  {
+    FRESULT res;
+    DWORD tim;
+    BYTE *dir;
+
+
+    res = validate(fp->fs, fp->id);            /* Check validity of the object */
+    if (res == FR_OK) {
+      if (fp->flag & FA__WRITTEN) {    /* Has the file been written? */
+#if !_FS_TINY  /* Write-back dirty buffer */
+       if (fp->flag & FA__DIRTY) {
+         if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+           LEAVE_FF(fp->fs, FR_DISK_ERR);
+         fp->flag &= ~FA__DIRTY;
+       }
+#endif
+
+       /* Update the directory entry */
+       res = move_window(fp->fs, fp->dir_sect);
+       if (res == FR_OK) {
+#if REALTIME
+         dir = get_dir_ptr(fp->fs, fp->dir_ptr);
+#else
+         dir = fp->dir_ptr;
+#endif
+         dir[DIR_Attr] |= AM_ARC;                                      /* Set archive bit */
+         ST_DWORD(dir+DIR_FileSize, fp->fsize);                /* Update file size */
+         ST_WORD(dir+DIR_FstClusLO, fp->org_clust);    /* Update start cluster */
+         ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16);
+         tim = get_fattime();                          /* Updated time */
+         ST_DWORD(dir+DIR_WrtTime, tim);
+         fp->flag &= ~FA__WRITTEN;
+         fp->fs->wflag = 1;
+         res = sync(fp->fs);
+       }
+      }
+    }
+
+#if REALTIME
+    if (fp && fp->fs && fp->fs->buffer_used) if (sync_win_buffers(fp->fs) != RES_OK) return FR_DISK_ERR;
+    if (fp && fp->fs) move_window(fp->fs, 0);
+#endif
+
+    LEAVE_FF(fp->fs, res);
+  }
+
+#endif /* !_FS_READONLY */
+
+  /*-----------------------------------------------------------------------*/
+  /* Close File                                                            */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_close (FIL *fp)            /* Pointer to the file object to be closed */
+  {
+    FRESULT res;
+
+#if _FS_READONLY
+    FATFS *fs = fp->fs;
+    res = validate(fp->fs, fp->id);
+    if (res == FR_OK) fp->fs = NULL;
+    LEAVE_FF(fs, res);
+#else
+    res = f_sync(fp);
+
+#if REALTIME
+    if (fp && fp->fs && fp->fs->buffer_used) if (sync_win_buffers(fp->fs) != RES_OK) return FR_DISK_ERR;
+    if (fp && fp->fs) move_window(fp->fs, 0);
+#endif
+
+    if (res == FR_OK) fp->fs = NULL;
+    return res;
+#endif
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Change Current Drive/Directory                                        */
+  /*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH
+  FRESULT f_chdrive (BYTE drv)         /* Drive number */
+  {
+    if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+  
+    Drive = drv;
+  
+    return FR_OK;
+  }
+
+  FRESULT f_chdir (
+                  const XCHAR *path    /* Pointer to the directory path */
+                  )
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+      INITBUF(dj, sfn, lfn);
+      res = follow_path(&dj, path);            /* Follow the file path */
+      if (res == FR_OK) {                                      /* Follow completed */
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+       dir = dj.dir;                                   /* Pointer to the entry */
+#endif
+       if (!dir) {
+         dj.fs->cdir = 0;                      /* No entry (root dir) */
+       } else {
+         if (dir[DIR_Attr] & AM_DIR)   /* Reached to the dir */
+           dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+         else
+           res = FR_NO_PATH;           /* Could not reach the dir (it is a file) */
+       }
+      }
+      if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+#endif
+
+
+#if _FS_MINIMIZE <= 2
+  /*-----------------------------------------------------------------------*/
+  /* Seek File R/W Pointer                                                 */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_lseek (FIL *fp, LONG ofs)          /* File pointer from top of file */
+  {
+    FRESULT res;
+    DWORD clst, bcs, nsect, ifptr;
+
+
+    res = validate(fp->fs, fp->id);            /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                  /* Check abort flag */
+      LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (ofs > fp->fsize                                        /* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+       && !(fp->flag & FA_WRITE)
+#endif
+       ) 
+      ofs = fp->fsize;
+
+    ifptr = fp->fptr;
+    fp->fptr = nsect = 0; fp->csect = 255;
+    if (ofs > 0) {
+      bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */
+      if (ifptr > 0 &&
+         (ofs - 1) / bcs >= (ifptr - 1) / bcs) {       /* When seek to same or following cluster, */
+       fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
+       ofs -= fp->fptr;
+       clst = fp->curr_clust;
+      } 
+      else {                                                                   /* When seek to back cluster, */
+       clst = fp->org_clust;                                   /* start from the first cluster */
+#if !_FS_READONLY
+       if (clst == 0) {                                                /* If no cluster chain, create a new chain */
+         clst = create_chain(fp->fs, 0);
+         if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+         if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+         fp->org_clust = clst;
+       }
+#endif
+       fp->curr_clust = clst;
+      }
+      if (clst != 0) {
+       while (ofs > bcs) {                                             /* Cluster following loop */
+#if !_FS_READONLY
+         if (fp->flag & FA_WRITE) {                    /* Check if in write mode or not */
+           clst = create_chain(fp->fs, clst);  /* Force streached if in write mode */
+           if (clst == 0) {                            /* When disk gets full, clip file size */
+             ofs = bcs; break;
+           }
+         } else
+#endif
+           clst = get_fat(fp->fs, clst);       /* Follow cluster chain if not in write mode */
+         if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+         if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR);
+         fp->curr_clust = clst;
+         fp->fptr += bcs;
+         ofs -= bcs;
+       }
+       fp->fptr += ofs;
+       fp->csect = (BYTE)(ofs / SS(fp->fs));   /* Sector offset in the cluster */
+       if (ofs % SS(fp->fs)) {
+         nsect = clust2sect(fp->fs, clst);     /* Current sector */
+         if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+         nsect += fp->csect;
+         fp->csect++;
+       }
+      }
+    }
+    if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {
+#if !_FS_TINY
+#if !_FS_READONLY
+      if (fp->flag & FA__DIRTY) {                      /* Write-back dirty buffer if needed */
+       if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK)
+         ABORT(fp->fs, FR_DISK_ERR);
+       fp->flag &= ~FA__DIRTY;
+      }
+#endif
+      if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK)
+       ABORT(fp->fs, FR_DISK_ERR);
+#endif
+      fp->dsect = nsect;
+    }
+#if !_FS_READONLY
+    if (fp->fptr > fp->fsize) {                        /* Set changed flag if the file size is extended */
+      fp->fsize = fp->fptr;
+      fp->flag |= FA__WRITTEN;
+    }
+#endif
+
+    LEAVE_FF(fp->fs, res);
+  }
+
+
+#if _FS_MINIMIZE <= 1
+  /*-----------------------------------------------------------------------*/
+  /* Create a Directroy Object                                             */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_opendir (DIR *dj,  /* Pointer to directory object to create */
+                    const XCHAR *path) /* Pointer to the directory path */
+  {
+    FRESULT res;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+    res = chk_mounted(&path, &dj->fs, 0);
+    if (res == FR_OK) {
+      INITBUF((*dj), sfn, lfn);
+      res = follow_path(dj, path);                     /* Follow the path to the directory */
+      if (res == FR_OK) {                                              /* Follow completed */
+#if REALTIME
+       dir = get_dir_ptr(dj->fs, dj->dir);
+#else
+       dir = dj->dir;
+#endif
+       if (dir) {                                                      /* It is not the root dir */
+         if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */
+           dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+         } else {                                              /* The object is not a directory */
+           res = FR_NO_PATH;
+         }
+       }
+       if (res == FR_OK) {
+         dj->id = dj->fs->id;
+         res = dir_seek(dj, 0);                        /* Rewind dir */
+       }
+      }
+      if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj->fs, res);
+  }
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Read Directory Entry in Sequense                                      */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_readdir (DIR *dj,                  /* Pointer to the open directory object */
+                    FILINFO *fno)              /* Pointer to file information to return */
+  {
+    FRESULT res;
+    NAMEBUF(sfn, lfn);
+
+
+    res = validate(dj->fs, dj->id);                    /* Check validity of the object */
+    if (res == FR_OK) {
+      INITBUF((*dj), sfn, lfn);
+      if (!fno) {
+       res = dir_seek(dj, 0);
+      } else {
+       res = dir_read(dj);
+       if (res == FR_NO_FILE) {
+         dj->sect = 0;
+         res = FR_OK;
+       }
+       if (res == FR_OK) {                             /* A valid entry is found */
+         get_fileinfo(dj, fno);                /* Get the object information */
+         res = dir_next(dj, FALSE);    /* Increment index for next */
+         if (res == FR_NO_FILE) {
+           dj->sect = 0;
+           res = FR_OK;
+         }
+       }
+      }
+    }
+
+    LEAVE_FF(dj->fs, res);
+  }
+
+
+
+#if _FS_MINIMIZE == 0
+  /*-----------------------------------------------------------------------*/
+  /* Get File Status                                                       */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_stat (const XCHAR *path,   /* Pointer to the file path */
+                 FILINFO *fno)         /* Pointer to file information to return */
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+      INITBUF(dj, sfn, lfn);
+      res = follow_path(&dj, path);    /* Follow the file path */
+      if (res == FR_OK) {                              /* Follwo completed */
+#if REALTIME
+       if (get_dir_ptr(dj.fs, dj.dir))
+#else
+         if (dj.dir)   /* Found an object */
+#endif
+           get_fileinfo(&dj, fno);
+         else          /* It is root dir */
+           res = FR_INVALID_NAME;
+      }
+    }
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+#if !_FS_READONLY
+  /*-----------------------------------------------------------------------*/
+  /* Get Number of Free Clusters                                           */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_getfree (
+                    const XCHAR *path, /* Pointer to the logical drive number (root dir) */
+                    DWORD *nclst,              /* Pointer to the variable to return number of free clusters */
+                    FATFS **fatfs              /* Pointer to pointer to corresponding file system object to return */
+                    )
+  {
+    FRESULT res;
+    DWORD n, clst, sect, stat;
+    UINT i;
+    BYTE fat, *p;
+
+
+    /* Get drive number */
+    res = chk_mounted(&path, fatfs, 0);
+    if (res != FR_OK) LEAVE_FF(*fatfs, res);
+
+    /* If number of free cluster is valid, return it without cluster scan. */
+    if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) {
+      *nclst = (*fatfs)->free_clust;
+      LEAVE_FF(*fatfs, FR_OK);
+    }
+
+    /* Get number of free clusters */
+    fat = (*fatfs)->fs_type;
+    n = 0;
+    if (fat == FS_FAT12) {
+      clst = 2;
+      do {
+       stat = get_fat(*fatfs, clst);
+       if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR);
+       if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR);
+       if (stat == 0) n++;
+      } while (++clst < (*fatfs)->max_clust);
+    } 
+    else {
+      clst = (*fatfs)->max_clust;
+      sect = (*fatfs)->fatbase;
+      i = 0; p = 0;
+      do {
+       if (!i) {
+         res = move_window(*fatfs, sect++);
+         if (res != FR_OK)
+           LEAVE_FF(*fatfs, res);
+         p = (*fatfs)->win;
+         i = SS(*fatfs);
+       }
+       if (fat == FS_FAT16) {
+         if (LD_WORD(p) == 0) n++;
+         p += 2; i -= 2;
+       } 
+       else {
+         if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+         p += 4; i -= 4;
+       }
+      } while (--clst);
+    }
+    (*fatfs)->free_clust = n;
+    if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+    *nclst = n;
+
+    LEAVE_FF(*fatfs, FR_OK);
+  }
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Truncate File                                                         */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_truncate (
+                     FIL *fp           /* Pointer to the file object */
+                     )
+  {
+    FRESULT res;
+    DWORD ncl;
+
+
+    res = validate(fp->fs, fp->id);            /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                  /* Check abort flag */
+      LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))                        /* Check access mode */
+      LEAVE_FF(fp->fs, FR_DENIED);
+
+    if (fp->fsize > fp->fptr) {
+      fp->fsize = fp->fptr;    /* Set file size to current R/W point */
+      fp->flag |= FA__WRITTEN;
+      if (fp->fptr == 0) {     /* When set file size to zero, remove entire cluster chain */
+       res = remove_chain(fp->fs, fp->org_clust);
+       fp->org_clust = 0;
+      } else {                         /* When truncate a part of the file, remove remaining clusters */
+       ncl = get_fat(fp->fs, fp->curr_clust);
+       res = FR_OK;
+       if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+       if (ncl == 1) res = FR_INT_ERR;
+       if (res == FR_OK && ncl < fp->fs->max_clust) {
+         res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF);
+         if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+       }
+      }
+    }
+    if (res != FR_OK) fp->flag |= FA__ERROR;
+
+    LEAVE_FF(fp->fs, res);
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Delete a File or Directory                                            */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_unlink (const XCHAR *path)         /* Pointer to the file or directory path */
+  {
+    FRESULT res;
+    DIR dj, sdj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+    DWORD dclst;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);                      /* Follow the file path */
+    if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+      res = FR_INVALID_NAME;
+    if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */
+
+#if REALTIME
+    dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+    dir = dj.dir;
+#endif
+    if (!dir)                                                          /* Is it the root directory? */
+      LEAVE_FF(dj.fs, FR_INVALID_NAME);
+    if (dir[DIR_Attr] & AM_RDO)                                /* Is it a R/O object? */
+      LEAVE_FF(dj.fs, FR_DENIED);
+    dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+
+    if (dir[DIR_Attr] & AM_DIR) {                      /* It is a sub-directory */
+      if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR);
+      memcpy(&sdj, &dj, sizeof(DIR));          /* Check if the sub-dir is empty or not */
+      sdj.sclust = dclst;
+      res = dir_seek(&sdj, 2);
+      if (res != FR_OK) LEAVE_FF(dj.fs, res);
+      res = dir_read(&sdj);
+      if (res == FR_OK) res = FR_DENIED;       /* Not empty sub-dir */
+      if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res);
+    }
+
+    res = dir_remove(&dj);                                     /* Remove directory entry */
+    if (res == FR_OK) {
+      if (dclst)
+       res = remove_chain(dj.fs, dclst);       /* Remove the cluster chain */
+      if (res == FR_OK) res = sync(dj.fs);
+    }
+
+#if REALTIME
+    if (dj.fs && dj.fs->buffer_used) if (sync_win_buffers(dj.fs) != RES_OK) return FR_DISK_ERR;
+    if (dj.fs) move_window(dj.fs, 0);
+#endif
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Create a Directory                                                    */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_mkdir (const XCHAR *path)  /* Pointer to the directory path */
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir, n;
+    DWORD dsect, dclst, pclst, tim;
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+
+    INITBUF(dj, sfn, lfn);
+    res = follow_path(&dj, path);                      /* Follow the file path */
+    if (res == FR_OK) res = FR_EXIST;          /* Any file or directory is already existing */
+    if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+      res = FR_INVALID_NAME;
+    if (res != FR_NO_FILE)                                     /* Any error occured */
+      LEAVE_FF(dj.fs, res);
+
+    dclst = create_chain(dj.fs, 0);                    /* Allocate a new cluster for new directory table */
+    res = FR_OK;
+    if (dclst == 0) res = FR_DENIED;
+    if (dclst == 1) res = FR_INT_ERR;
+    if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR;
+    if (res == FR_OK)
+      res = move_window(dj.fs, 0);
+    if (res != FR_OK) LEAVE_FF(dj.fs, res);
+    dsect = clust2sect(dj.fs, dclst);
+
+    dir = dj.fs->win;                                          /* Initialize the new directory table */
+    memset(dir, 0, SS(dj.fs));
+    memset(dir+DIR_Name, ' ', 8+3);            /* Create "." entry */
+    dir[DIR_Name] = '.';
+    dir[DIR_Attr] = AM_DIR;
+    tim = get_fattime();
+    ST_DWORD(dir+DIR_WrtTime, tim);
+    ST_WORD(dir+DIR_FstClusLO, dclst);
+    ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+    memcpy(dir+32, dir, 32);                   /* Create ".." entry */
+    dir[33] = '.';
+    pclst = dj.sclust;
+    if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase)
+      pclst = 0;
+    ST_WORD(dir+32+DIR_FstClusLO, pclst);
+    ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16);
+    for (n = 0; n < dj.fs->csize; n++) {       /* Write dot entries and clear left sectors */
+      dj.fs->winsect = dsect++;
+      dj.fs->wflag = 1;
+      res = move_window(dj.fs, 0);
+      if (res) LEAVE_FF(dj.fs, res);
+#if REALTIME
+      dir = dj.fs->win;
+#endif
+      memset(dir, 0, SS(dj.fs));
+    }
+
+    res = dir_register(&dj);
+    if (res != FR_OK) {
+      remove_chain(dj.fs, dclst);
+    } else {
+#if REALTIME
+      dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+      dir = dj.dir;
+#endif
+      dir[DIR_Attr] = AM_DIR;                                  /* Attribute */
+      ST_DWORD(dir+DIR_WrtTime, tim);                  /* Crated time */
+      ST_WORD(dir+DIR_FstClusLO, dclst);               /* Table start cluster */
+      ST_WORD(dir+DIR_FstClusHI, dclst >> 16);
+      dj.fs->wflag = 1;
+      res = sync(dj.fs);
+    }
+
+#if REALTIME
+    if (dj.fs && dj.fs->buffer_used) if (sync_win_buffers(dj.fs) != RES_OK) return FR_DISK_ERR;
+    if (dj.fs) move_window(dj.fs, 0);
+#endif
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Change File Attribute                                                 */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_chmod (const XCHAR *path,  /* Pointer to the file path */
+                  BYTE value,                  /* Attribute bits */
+                  BYTE mask)                   /* Attribute mask to change */
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+      INITBUF(dj, sfn, lfn);
+      res = follow_path(&dj, path);            /* Follow the file path */
+      if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+       res = FR_INVALID_NAME;
+      if (res == FR_OK) {
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+       dir = dj.dir;
+#endif
+
+       if (!dir) {                                             /* Is it a root directory? */
+         res = FR_INVALID_NAME;
+       } 
+       else {                                          /* File or sub directory */
+         mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;  /* Valid attribute mask */
+         dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);       /* Apply attribute change */
+         dj.fs->wflag = 1;
+         res = sync(dj.fs);
+       }
+      }
+    }
+
+#if REALTIME
+    if (dj.fs && dj.fs->buffer_used) if (sync_win_buffers(dj.fs) != RES_OK) return FR_DISK_ERR;
+    if (dj.fs) move_window(dj.fs, 0);
+#endif
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /* Change Timestamp                                                      */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_utime (const XCHAR *path,  /* Pointer to the file/directory name */
+                  const FILINFO *fno)  /* Pointer to the timestamp to be set */
+  {
+    FRESULT res;
+    DIR dj;
+    NAMEBUF(sfn, lfn);
+    BYTE *dir;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+      INITBUF(dj, sfn, lfn);
+      res = follow_path(&dj, path);    /* Follow the file path */
+      if (_FS_RPATH && res == FR_OK && (dj.fn[11] & NS_DOT))
+       res = FR_INVALID_NAME;
+      if (res == FR_OK) {
+#if REALTIME
+       dir = get_dir_ptr(dj.fs, dj.dir);
+#else
+       dir = dj.dir;
+#endif
+       if (!dir) {                             /* Root directory */
+         res = FR_INVALID_NAME;
+       } else {                                /* File or sub-directory */
+         ST_WORD(dir+DIR_WrtTime, fno->ftime);
+         ST_WORD(dir+DIR_WrtDate, fno->fdate);
+         dj.fs->wflag = 1;
+         res = sync(dj.fs);
+       }
+      }
+    }
+
+#if REALTIME
+    if (dj.fs && dj.fs->buffer_used) if (sync_win_buffers(dj.fs) != RES_OK) return FR_DISK_ERR;
+    if (dj.fs) move_window(dj.fs, 0);
+#endif
+
+    LEAVE_FF(dj.fs, res);
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Rename File/Directory                                                 */
+  /*-----------------------------------------------------------------------*/
+
+  FRESULT f_rename (const XCHAR *path_old,     /* Pointer to the old name */
+                   const XCHAR *path_new)      /* Pointer to the new name */
+  {
+    FRESULT res = 0;
+    DIR dj_old, dj_new;
+    NAMEBUF(sfn, lfn);
+    BYTE buf[21], *dir;
+    DWORD dw;
+
+
+    INITBUF(dj_old, sfn, lfn);
+    res = chk_mounted(&path_old, &dj_old.fs, 1);
+    if (res == FR_OK) {
+      dj_new.fs = dj_old.fs;
+      res = follow_path(&dj_old, path_old);    /* Check old object */
+      if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT))
+       res = FR_INVALID_NAME;
+    }
+    if (res != FR_OK) LEAVE_FF(dj_old.fs, res);        /* The old object is not found */
+
+    if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE);  /* Is root dir? */
+    memcpy(buf, dj_old.dir+DIR_Attr, 21);              /* Save the object information */
+
+    memcpy(&dj_new, &dj_old, sizeof(DIR));
+    res = follow_path(&dj_new, path_new);              /* Check new object */
+    if (res == FR_OK) res = FR_EXIST;                  /* The new object name is already existing */
+    if (res == FR_NO_FILE) {                                   /* Is it a valid path and no name collision? */
+      res = dir_register(&dj_new);                     /* Register the new object */
+      if (res == FR_OK) {
+#if REALTIME
+       dir = get_dir_ptr(dj_new.fs, dj_new.dir);
+#else
+       dir = dj_new.dir;                                       /* Copy object information into new entry */
+#endif
+       memcpy(dir+13, buf+2, 19);
+       dir[DIR_Attr] = buf[0] | AM_ARC;
+       dj_old.fs->wflag = 1;
+       if (dir[DIR_Attr] & AM_DIR) {           /* Update .. entry in the directory if needed */
+         dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
+         if (!dw) {
+           res = FR_INT_ERR;
+         } else {
+           res = move_window(dj_new.fs, dw);
+           dir = dj_new.fs->win+32;
+           if (res == FR_OK && dir[1] == '.') {
+             dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust;
+             ST_WORD(dir+DIR_FstClusLO, dw);
+             ST_WORD(dir+DIR_FstClusHI, dw >> 16);
+             dj_new.fs->wflag = 1;
+           }
+         }
+       }
+       if (res == FR_OK) {
+         res = dir_remove(&dj_old);                    /* Remove old entry */
+         if (res == FR_OK)
+           res = sync(dj_old.fs);
+       }
+      }
+    }
+
+#if REALTIME
+    if (dj_old.fs && dj_old.fs->buffer_used) if (sync_win_buffers(dj_old.fs) != RES_OK) return FR_DISK_ERR;
+    if (dj_old.fs) move_window(dj_old.fs, 0);
+#endif
+    LEAVE_FF(dj_old.fs, res);
+  }
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Forward data to the stream directly (Available on only _FS_TINY cfg)  */
+  /*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+  FRESULT f_forward (FIL *fp,                                          /* Pointer to the file object */
+                    UINT (*func)(const BYTE*,UINT),    /* Pointer to the streaming function */
+                    UINT btr,                                          /* Number of bytes to forward */
+                    UINT *bf)                                  /* Pointer to number of bytes forwarded */
+  {
+    FRESULT res;
+    DWORD remain, clst, sect;
+    UINT rcnt;
+
+
+    *bf = 0;
+
+    res = validate(fp->fs, fp->id);                                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                                          /* Check error flag */
+      LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                                         /* Check access mode */
+      LEAVE_FF(fp->fs, FR_DENIED);
+
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;                      /* Truncate btr by remaining bytes */
+
+    for ( ;  btr && (*func)(NULL, 0);                          /* Repeat until all data transferred or stream becomes busy */
+         fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+      if ((fp->fptr % SS(fp->fs)) == 0) {                      /* On the sector boundary? */
+       if (fp->csect >= fp->fs->csize) {               /* On the cluster boundary? */
+         clst = (fp->fptr == 0) ?                      /* On the top of the file? */
+           fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+         if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+         if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+         fp->curr_clust = clst;                                /* Update current cluster */
+         fp->csect = 0;                                                /* Reset sector address in the cluster */
+       }
+       fp->csect++;                                                    /* Next sector address in the cluster */
+      }
+      sect = clust2sect(fp->fs, fp->curr_clust);       /* Get current data sector */
+      if (!sect) ABORT(fp->fs, FR_INT_ERR);
+      sect += fp->csect - 1;
+      if (move_window(fp->fs, sect))                           /* Move sector window */
+       ABORT(fp->fs, FR_DISK_ERR);
+      fp->dsect = sect;
+      rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));       /* Forward data from sector window */
+      if (rcnt > btr) rcnt = btr;
+      rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+      if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+  }
+#endif /* _USE_FORWARD */
+
+
+#if _USE_MKFS && !_FS_READONLY
+  /*-----------------------------------------------------------------------*/
+  /* Create File System on the Drive                                       */
+  /*-----------------------------------------------------------------------*/
+#define N_ROOTDIR      512                     /* Multiple of 32 and <= 2048 */
+#define N_FATS         1                       /* 1 or 2 */
+#define MAX_SECTOR     131072000UL     /* Maximum partition size */
+#define MIN_SECTOR     2000UL          /* Minimum partition size */
+
+
+  FRESULT f_mkfs (BYTE drv,                    /* Logical drive number */
+                 BYTE partition,               /* Partitioning rule 0:FDISK, 1:SFD */
+                 WORD allocsize)               /* Allocation unit size [bytes] */
+  {
+    uint32_t sstbl[] = { 2048000UL, 1024000UL, 512000UL, 256000UL, 128000UL, 64000UL, 32000, 16000, 8000, 4000,   0 };
+    uint16_t cstbl[] =  {   32768,   16384,   8192,   4096,   2048, 16384,  8192,  4096, 2048, 1024, 512 };
+    BYTE fmt, m, *tbl;
+    DWORD b_part, b_fat, b_dir, b_data;                /* Area offset (LBA) */
+    DWORD n_part, n_rsv, n_fat, n_dir;         /* Area size */
+    DWORD n_clst, d, n;
+    WORD ass;   // fatfs uses 'as' but that's a keyword
+    FATFS *fs;
+    DSTATUS stat;
+
+
+    /* Check validity of the parameters */
+    if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+    if (partition >= 2) return FR_MKFS_ABORTED;
+
+    /* Check mounted drive and clear work area */
+    fs = FatFs[drv];
+    if (!fs) return FR_NOT_ENABLED;
+    fs->fs_type = 0;
+    drv = LD2PD(drv);
+
+    /* Get disk statics */
+    stat = disk_initialize(drv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                                             /* Get disk sector size */
+    if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+       || SS(fs) > _MAX_SS)
+      return FR_MKFS_ABORTED;
+#endif
+    if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR)
+      return FR_MKFS_ABORTED;
+    if (n_part > MAX_SECTOR) n_part = MAX_SECTOR;
+    b_part = (!partition) ? 63 : 0;            /* Boot sector */
+    n_part -= b_part;
+    for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ;    /* Check validity of the allocation unit size */
+    if (d != allocsize) allocsize = 0;
+    if (!allocsize) {                                  /* Auto selection of cluster size */
+      d = n_part;
+      for (ass = SS(fs); ass > 512U; ass >>= 1) d >>= 1;
+      for (n = 0; d < sstbl[n]; n++) ;
+      allocsize = cstbl[n];
+    }
+    if (allocsize < SS(fs)) allocsize = SS(fs);
+
+    allocsize /= SS(fs);               /* Number of sectors per cluster */
+
+    /* Pre-compute number of clusters and FAT type */
+    n_clst = n_part / allocsize;
+    fmt = FS_FAT12;
+    if (n_clst >= 0xFF5) fmt = FS_FAT16;
+    if (n_clst >= 0xFFF5) fmt = FS_FAT32;
+
+    /* Determine offset and size of FAT structure */
+    switch (fmt) {
+    case FS_FAT12:
+      n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs);
+      n_rsv = 1 + partition;
+      n_dir = N_ROOTDIR * 32 / SS(fs);
+      break;
+    case FS_FAT16:
+      n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs);
+      n_rsv = 1 + partition;
+      n_dir = N_ROOTDIR * 32 / SS(fs);
+      break;
+    default:
+      n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+      n_rsv = 33 - partition;
+      n_dir = 0;
+    }
+    b_fat = b_part + n_rsv;                    /* FATs start sector */
+    b_dir = b_fat + n_fat * N_FATS;    /* Directory start sector */
+    b_data = b_dir + n_dir;                    /* Data start sector */
+
+    /* Align data start sector to erase block boundary (for flash memory media) */
+    if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED;
+    n = (b_data + n - 1) & ~(n - 1);
+    n_fat += (n - b_data) / N_FATS;
+    /* b_dir and b_data are no longer used below */
+
+    /* Determine number of cluster and final check of validity of the FAT type */
+    n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize;
+    if (   (fmt == FS_FAT16 && n_clst < 0xFF5)
+          || (fmt == FS_FAT32 && n_clst < 0xFFF5))
+      return FR_MKFS_ABORTED;
+
+    /* Create partition table if needed */
+    if (!partition) {
+      DWORD n_disk = b_part + n_part;
+
+      memset(fs->win, 0, SS(fs));
+      tbl = fs->win+MBR_Table;
+      ST_DWORD(tbl, 0x00010180);               /* Partition start in CHS */
+      if (n_disk < 63UL * 255 * 1024) {        /* Partition end in CHS */
+       n_disk = n_disk / 63 / 255;
+       tbl[7] = (BYTE)n_disk;
+       tbl[6] = (BYTE)((n_disk >> 2) | 63);
+      } else {
+       ST_WORD(&tbl[6], 0xFFFF);
+      }
+      tbl[5] = 254;
+      if (fmt != FS_FAT32)                     /* System ID */
+       tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06;
+      else
+       tbl[4] = 0x0c;
+      ST_DWORD(tbl+8, 63);                     /* Partition start in LBA */
+      ST_DWORD(tbl+12, n_part);                /* Partition size in LBA */
+      ST_WORD(tbl+64, 0xAA55);         /* Signature */
+      if (disk_write(drv, fs->win, 0, 1) != RES_OK)
+       return FR_DISK_ERR;
+      partition = 0xF8;
+    } else {
+      partition = 0xF0;
+    }
+
+    /* Create boot record */
+    tbl = fs->win;                                                             /* Clear buffer */
+    memset(tbl, 0, SS(fs));
+    ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB);                        /* Boot code (jmp $, nop) */
+    ST_WORD(tbl+BPB_BytsPerSec, SS(fs));               /* Sector size */
+    tbl[BPB_SecPerClus] = (BYTE)allocsize;             /* Sectors per cluster */
+    ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);                        /* Reserved sectors */
+    tbl[BPB_NumFATs] = N_FATS;                                 /* Number of FATs */
+    ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */
+    if (n_part < 0x10000) {                                            /* Number of total sectors */
+      ST_WORD(tbl+BPB_TotSec16, n_part);
+    } else {
+      ST_DWORD(tbl+BPB_TotSec32, n_part);
+    }
+    tbl[BPB_Media] = partition;                                        /* Media descripter */
+    ST_WORD(tbl+BPB_SecPerTrk, 63);                            /* Number of sectors per track */
+    ST_WORD(tbl+BPB_NumHeads, 255);                            /* Number of heads */
+    ST_DWORD(tbl+BPB_HiddSec, b_part);                 /* Hidden sectors */
+    n = get_fattime();                                                 /* Use current time as a VSN */
+    if (fmt != FS_FAT32) {
+      ST_DWORD(tbl+BS_VolID, n);                               /* Volume serial number */
+      ST_WORD(tbl+BPB_FATSz16, n_fat);         /* Number of secters per FAT */
+      tbl[BS_DrvNum] = 0x80;                                   /* Drive number */
+      tbl[BS_BootSig] = 0x29;                                  /* Extended boot signature */
+      memcpy(tbl+BS_VolLab, "NO NAME    FAT     ", 19);        /* Volume lavel, FAT signature */
+    } else {
+      ST_DWORD(tbl+BS_VolID32, n);                     /* Volume serial number */
+      ST_DWORD(tbl+BPB_FATSz32, n_fat);                /* Number of secters per FAT */
+      ST_DWORD(tbl+BPB_RootClus, 2);                   /* Root directory cluster (2) */
+      ST_WORD(tbl+BPB_FSInfo, 1);                              /* FSInfo record offset (bs+1) */
+      ST_WORD(tbl+BPB_BkBootSec, 6);                   /* Backup boot record offset (bs+6) */
+      tbl[BS_DrvNum32] = 0x80;                         /* Drive number */
+      tbl[BS_BootSig32] = 0x29;                                /* Extended boot signature */
+      memcpy(tbl+BS_VolLab32, "NO NAME    FAT32   ", 19);      /* Volume lavel, FAT signature */
+    }
+    ST_WORD(tbl+BS_55AA, 0xAA55);                              /* Signature */
+    if (SS(fs) > 512U) {
+      ST_WORD(tbl+SS(fs)-2, 0xAA55);
+    }
+    if (disk_write(drv, tbl, b_part+0, 1) != RES_OK)
+      return FR_DISK_ERR;
+    if (fmt == FS_FAT32)
+      disk_write(drv, tbl, b_part+6, 1);
+
+    /* Initialize FAT area */
+    for (m = 0; m < N_FATS; m++) {
+      memset(tbl, 0, SS(fs));          /* 1st sector of the FAT  */
+      if (fmt != FS_FAT32) {
+       n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+       n |= partition;
+       ST_DWORD(tbl, n);                               /* Reserve cluster #0-1 (FAT12/16) */
+      } else {
+       ST_DWORD(tbl+0, 0xFFFFFFF8);    /* Reserve cluster #0-1 (FAT32) */
+       ST_DWORD(tbl+4, 0xFFFFFFFF);
+       ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
+      }
+      if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+       return FR_DISK_ERR;
+      memset(tbl, 0, SS(fs));          /* Following FAT entries are filled by zero */
+      for (n = 1; n < n_fat; n++) {
+       if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+         return FR_DISK_ERR;
+      }
+    }
+
+    /* Initialize Root directory */
+    m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir);
+    do {
+      if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+       return FR_DISK_ERR;
+    } while (--m);
+
+    /* Create FSInfo record if needed */
+    if (fmt == FS_FAT32) {
+      ST_WORD(tbl+BS_55AA, 0xAA55);
+      ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+      ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+      ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);
+      ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
+      disk_write(drv, tbl, b_part+1, 1);
+      disk_write(drv, tbl, b_part+7, 1);
+    }
+
+    return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR;
+  }
+
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+  /*-----------------------------------------------------------------------*/
+  /* Get a string from the file                                            */
+  /*-----------------------------------------------------------------------*/
+  char* f_gets (char* buff,    /* Pointer to the string buffer to read */
+               int len,        /* Size of string buffer */
+               FIL* fil)       /* Pointer to the file object */
+    {
+      int i = 0;
+      char *p = buff;
+      UINT rc;
+
+
+      while (i < len - 1) {                    /* Read bytes until buffer gets filled */
+       f_read(fil, p, 1, &rc);
+       if (rc != 1) break;                     /* Break when no data to read */
+#if _USE_STRFUNC >= 2
+       if (*p == '\r') continue;       /* Strip '\r' */
+#endif
+       i++;
+       if (*p++ == '\n') break;        /* Break when reached end of line */
+      }
+      *p = 0;
+      return i ? buff : NULL;                  /* When no data read (eof or error), return with error. */
+    }
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+  /*-----------------------------------------------------------------------*/
+  /* Put a character to the file                                           */
+  /*-----------------------------------------------------------------------*/
+  int f_putc (int chr, /* A character to be output */
+             FIL* fil) /* Ponter to the file object */
+  {
+    UINT bw;
+    char c;
+
+
+#if _USE_STRFUNC >= 2
+    if (chr == '\n') f_putc ('\r', fil);       /* LF -> CRLF conversion */
+#endif
+    if (!fil) {        /* Special value may be used to switch the destination to any other device */
+      /*       put_console(chr);       */
+      return chr;
+    }
+    c = (char)chr;
+    f_write(fil, &c, 1, &bw);  /* Write a byte to the file */
+    return bw ? chr : EOF;             /* Return the result */
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Put a string to the file                                              */
+  /*-----------------------------------------------------------------------*/
+  int f_puts (const char* str, /* Pointer to the string to be output */
+             FIL* fil)                 /* Pointer to the file object */
+  {
+    int n;
+
+
+    for (n = 0; *str; str++, n++) {
+      if (f_putc(*str, fil) == EOF) return EOF;
+    }
+    return n;
+  }
+
+
+
+
+  /*-----------------------------------------------------------------------*/
+  /* Put a formatted string to the file                                    */
+  /*-----------------------------------------------------------------------*/
+  int f_printf (FIL* fil,                      /* Pointer to the file object */
+               const char* str,        /* Pointer to the format string */
+               ...)                                    /* Optional arguments... */
+  {
+    va_list arp;
+    UCHAR c, f, r;
+    ULONG val;
+    char s[16];
+    int i, w, res, cc;
+
+
+    va_start(arp, str);
+
+    for (cc = res = 0; cc != EOF; res += cc) {
+      c = *str++;
+      if (c == 0) break;                       /* End of string */
+      if (c != '%') {                          /* Non escape cahracter */
+       cc = f_putc(c, fil);
+       if (cc != EOF) cc = 1;
+       continue;
+      }
+      w = f = 0;
+      c = *str++;
+      if (c == '0') {                          /* Flag: '0' padding */
+       f = 1; c = *str++;
+      }
+      while (c >= '0' && c <= '9') {   /* Precision */
+       w = w * 10 + (c - '0');
+       c = *str++;
+      }
+      if (c == 'l') {                          /* Prefix: Size is long int */
+       f |= 2; c = *str++;
+      }
+      if (c == 's') {                          /* Type is string */
+       cc = f_puts(va_arg(arp, char*), fil);
+       continue;
+      }
+      if (c == 'c') {                          /* Type is character */
+       cc = f_putc(va_arg(arp, int), fil);
+       if (cc != EOF) cc = 1;
+       continue;
+      }
+      r = 0;
+      if (c == 'd') r = 10;            /* Type is signed decimal */
+      if (c == 'u') r = 10;            /* Type is unsigned decimal */
+      if (c == 'X') r = 16;            /* Type is unsigned hexdecimal */
+      if (r == 0) break;                       /* Unknown type */
+      if (f & 2) {                             /* Get the value */
+       val = (ULONG)va_arg(arp, long);
+      } else {
+       val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int);
+      }
+      /* Put numeral string */
+      if (c == 'd') {
+       if (val & 0x80000000) {
+         val = 0 - val;
+         f |= 4;
+       }
+      }
+      i = sizeof(s) - 1; s[i] = 0;
+      do {
+       c = (UCHAR)(val % r + '0');
+       if (c > '9') c += 7;
+       s[--i] = c;
+       val /= r;
+      } while (i && val);
+      if (i && (f & 4)) s[--i] = '-';
+      w = sizeof(s) - 1 - w;
+      while (i && i > w) s[--i] = (f & 1) ? '0' : ' ';
+      cc = f_puts(&s[i], fil);
+    }
+
+    va_end(arp);
+    return (cc == EOF) ? cc : res;
+  }
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
+  
+}
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/README b/tos/platforms/shimmer/chips/sd/fatfs/README
new file mode 100644 (file)
index 0000000..242ad52
--- /dev/null
@@ -0,0 +1,57 @@
+The FatFs implementation is a direct port of the ChaN FatFs project
+(http://elm-chan.org/fsw/ff/00index_e.html) to TinyOS.  
+
+During testing of the initial port, Victor Cionca at University of
+Limerick discovered a great deal of overhead in the filesystem's
+cluster window operations, and devised an improved method to handle
+these without compromising the integrity of the fs.  These are
+incorporated here.
+
+First note:  Your app must call mount before proceeding with any other file
+operations.  Boot.booted is a good place to do this.
+
+Application developers should note that FatFs development used the IP
+stack and NTP updates in order to provide accurate timestamps.
+tinyos-1.x/contrib/handhelds/swtest/TestFATLogging shows how to do this.
+
+Without these mechanisms, an application will need another method to
+seed the app's time value with something realistic (not
+1/1/1980-relative) at compile time, or devise a way to provide a
+runtime update from a host.
+
+One way:
+-------
+copy the simple python script timeSec.py in tinyos-2.x-contrib/shimmer/apps/JustFATLogging to your
+app directory.
+
+add these lines to your app's Makefile, which will provide a hook for
+the the compile time variable CURRENT_TIME.  TimeP uses this to set the
+runtime g_current_time that sets the baseline for reporting localtime
+on the device:
+
+ifdef CURRENT_TIME
+PFLAGS += -DCURRENT_TIME=$(CURRENT_TIME)
+endif
+
+then, at compile-time, add CURRENT_TIME=`python ./timeSec.py` to the
+build line.
+
+unfortunately, this seed will be restored if you reset the board.
+
+Another way:
+-----------
+add a mechanism to provide the current time from a host machine via
+serial line.  one example of how to do this is in
+tinyos-1.x/contrib/handhelds/apps/ThreeAxisRecorder.
+
+Yet Another way:
+---------------
+use the bluetooth radio to do similar (don't know why this would be
+easier than using the built-in access point-ip stack
+infrastructure...).
+
+
+
+
+
+
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/ccsbcs.c b/tos/platforms/shimmer/chips/sd/fatfs/ccsbcs.c
new file mode 100644 (file)
index 0000000..57439b2
--- /dev/null
@@ -0,0 +1,540 @@
+/*------------------------------------------------------------------------*/\r
+/* Unicode - Local code bidirectional converter  (C)ChaN, 2009            */\r
+/* (SBCS code pages)                                                      */\r
+/*------------------------------------------------------------------------*/\r
+/*  437   U.S. (OEM)\r
+/   720   Arabic (OEM)\r
+/   1256  Arabic (Windows)\r
+/   737   Greek (OEM)\r
+/   1253  Greek (Windows)\r
+/   1250  Central Europe (Windows)\r
+/   775   Baltic (OEM)\r
+/   1257  Baltic (Windows)\r
+/   850   Multilingual Latin 1 (OEM)\r
+/   852   Latin 2 (OEM)\r
+/   1252  Latin 1 (Windows)\r
+/   855   Cyrillic (OEM)\r
+/   1251  Cyrillic (Windows)\r
+/   866   Russian (OEM)\r
+/   857   Turkish (OEM)\r
+/   1254  Turkish (Windows)\r
+/   858   Multilingual Latin 1 + Euro (OEM)\r
+/   862   Hebrew (OEM)\r
+/   1255  Hebrew (Windows)\r
+/   874   Thai (OEM, Windows)\r
+/   1258  Vietnam (OEM, Windows)\r
+*/\r
+\r
+//#include "../ff.h"\r
+\r
+\r
+#if _CODE_PAGE == 437\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP437(0x80-0xFF) to Unicode conversion table */\r
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,\r
+       0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\r
+       0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,\r
+       0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,\r
+       0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+       0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+       0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,\r
+       0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\r
+       0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,\r
+       0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 720\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP720(0x80-0xFF) to Unicode conversion table */\r
+       0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,\r
+       0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,\r
+       0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,\r
+       0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,\r
+       0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+       0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+       0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,\r
+       0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,\r
+       0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248,\r
+       0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 737\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP737(0x80-0xFF) to Unicode conversion table */\r
+       0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,\r
+       0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,\r
+       0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,\r
+       0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,\r
+       0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,\r
+       0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+       0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+       0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,\r
+       0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,\r
+       0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,\r
+       0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 775\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP775(0x80-0xFF) to Unicode conversion table */\r
+       0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,\r
+       0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,\r
+       0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,\r
+       0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,\r
+       0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,\r
+       0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,\r
+       0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,\r
+       0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,\r
+       0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,\r
+       0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,\r
+       0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,\r
+       0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 850\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP850(0x80-0xFF) to Unicode conversion table */\r
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,\r
+       0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\r
+       0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,\r
+       0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,\r
+       0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,\r
+       0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\r
+       0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,\r
+       0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,\r
+       0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,\r
+       0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,\r
+       0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,\r
+       0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 852\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP852(0x80-0xFF) to Unicode conversion table */\r
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,\r
+       0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,\r
+       0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,\r
+       0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,\r
+       0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,\r
+       0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\r
+       0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,\r
+       0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,\r
+       0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,\r
+       0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,\r
+       0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,\r
+       0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 855\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP855(0x80-0xFF) to Unicode conversion table */\r
+       0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,\r
+       0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,\r
+       0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,\r
+       0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,\r
+       0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,\r
+       0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,\r
+       0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\r
+       0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,\r
+       0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,\r
+       0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,\r
+       0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,\r
+       0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,\r
+       0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 857\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP857(0x80-0xFF) to Unicode conversion table */\r
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,\r
+       0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,\r
+       0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,\r
+       0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,\r
+       0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,\r
+       0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\r
+       0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,\r
+       0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,\r
+       0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,\r
+       0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,\r
+       0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,\r
+       0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 858\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP858(0x80-0xFF) to Unicode conversion table */\r
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,\r
+       0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,\r
+       0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,\r
+       0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,\r
+       0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,\r
+       0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,\r
+       0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,\r
+       0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,\r
+       0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,\r
+       0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,\r
+       0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,\r
+       0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 862\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP862(0x80-0xFF) to Unicode conversion table */\r
+       0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,\r
+       0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,\r
+       0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,\r
+       0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,\r
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,\r
+       0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+       0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+       0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,\r
+       0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,\r
+       0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,\r
+       0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 866\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP866(0x80-0xFF) to Unicode conversion table */\r
+       0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+       0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,\r
+       0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+       0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,\r
+       0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+       0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,\r
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\r
+       0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,\r
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,\r
+       0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,\r
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,\r
+       0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,\r
+       0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+       0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,\r
+       0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,\r
+       0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0\r
+};\r
+\r
+#elif _CODE_PAGE == 874\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP874(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,\r
+       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,\r
+       0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,\r
+       0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,\r
+       0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,\r
+       0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,\r
+       0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,\r
+       0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,\r
+       0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,\r
+       0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,\r
+       0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,\r
+       0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,\r
+       0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000\r
+};\r
+\r
+#elif _CODE_PAGE == 1250\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1250(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,\r
+       0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,\r
+       0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,\r
+       0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,\r
+       0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,\r
+       0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,\r
+       0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,\r
+       0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,\r
+       0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,\r
+       0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,\r
+       0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9\r
+};\r
+\r
+#elif _CODE_PAGE == 1251\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1251(0x80-0xFF) to Unicode conversion table */\r
+       0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,\r
+       0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,\r
+       0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,\r
+       0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,\r
+       0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,\r
+       0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,\r
+       0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,\r
+       0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,\r
+       0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,\r
+       0x0428, 0x0429, 0x042A, 0x042D, 0x042C, 0x042D, 0x042E, 0x042F,\r
+       0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,\r
+       0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,\r
+       0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,\r
+       0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F\r
+};\r
+\r
+#elif _CODE_PAGE == 1252\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1252(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,\r
+       0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+       0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+       0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+       0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+       0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x00DD, 0x00DE, 0x00DF,\r
+       0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+       0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+       0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+       0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF\r
+};\r
+\r
+#elif _CODE_PAGE == 1253\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1253(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,\r
+       0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,\r
+       0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,\r
+       0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,\r
+       0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,\r
+       0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,\r
+       0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,\r
+       0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,\r
+       0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,\r
+       0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000\r
+};\r
+\r
+#elif _CODE_PAGE == 1254\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1254(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,\r
+       0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+       0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+       0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,\r
+       0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,\r
+       0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,\r
+       0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+       0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,\r
+       0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,\r
+       0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF\r
+};\r
+\r
+#elif _CODE_PAGE == 1255\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1255(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+       0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,\r
+       0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,\r
+       0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,\r
+       0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,\r
+       0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,\r
+       0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,\r
+       0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,\r
+       0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000\r
+};\r
+\r
+#elif _CODE_PAGE == 1256\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1256(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,\r
+       0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,\r
+       0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,\r
+       0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,\r
+       0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,\r
+       0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,\r
+       0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,\r
+       0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,\r
+       0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,\r
+       0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,\r
+       0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2\r
+}\r
+\r
+#elif _CODE_PAGE == 1257\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1257(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,\r
+       0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,\r
+       0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,\r
+       0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,\r
+       0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,\r
+       0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,\r
+       0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,\r
+       0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,\r
+       0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,\r
+       0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,\r
+       0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9\r
+};\r
+\r
+#elif _CODE_PAGE == 1258\r
+#define _TBLDEF 1\r
+static\r
+const WCHAR Tbl[] = {  /*  CP1258(0x80-0xFF) to Unicode conversion table */\r
+       0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,\r
+       0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,\r
+       0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,\r
+       0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,\r
+       0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,\r
+       0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,\r
+       0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,\r
+       0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,\r
+       0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,\r
+       0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,\r
+       0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,\r
+       0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,\r
+       0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,\r
+       0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,\r
+       0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,\r
+       0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF\r
+};\r
+\r
+#endif\r
+\r
+\r
+#if !_TBLDEF || !_USE_LFN\r
+#error This file is not needed in current configuration\r
+#endif\r
+\r
+\r
+WCHAR ff_convert (     /* Converted character, Returns zero on error */\r
+       WCHAR   src,    /* Character code to be converted */\r
+       UINT    dir             /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */\r
+)\r
+{\r
+       WCHAR c;\r
+\r
+\r
+       if (src < 0x80) {       /* ASCII */\r
+               c = src;\r
+\r
+       } else {\r
+               if (dir) {              /* OEMCP to Unicode */\r
+                       c = (src >= 0x100) ? 0 : Tbl[src - 0x80];\r
+\r
+               } else {                /* Unicode to OEMCP */\r
+                       for (c = 0; c < 0x80; c++) {\r
+                               if (src == Tbl[c]) break;\r
+                       }\r
+                       c = (c + 0x80) & 0xFF;\r
+               }\r
+       }\r
+\r
+       return c;\r
+}\r
+\r
+\r
+WCHAR ff_wtoupper (    /* Upper converted character */\r
+       WCHAR chr               /* Input character */\r
+)\r
+{\r
+       static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 };\r
+       static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 };\r
+       int i;\r
+\r
+\r
+       for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;\r
+\r
+       return tbl_lower[i] ? tbl_upper[i] : chr;\r
+}\r
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/diskIOC.nc b/tos/platforms/shimmer/chips/sd/fatfs/diskIOC.nc
new file mode 100644 (file)
index 0000000..9b20a4a
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2009, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Steve Ayer
+ * @date   April, 2009
+ * port to tos-2.x
+ * @date January, 2010
+ *
+ * wire the FatFs interface to the current implementation
+ * wire the diskIO abstraction in the FatFs module to physical medium
+ */
+
+configuration diskIOC {
+  provides {
+    interface FatFs;
+    interface StdControl as diskIOStdControl;
+    interface SD as diskIO;
+  }
+}
+implementation {
+  components 
+    FatFsP, 
+    SDP, 
+    new Msp430Usart0C(), 
+    new TimerMilliC(), 
+    HplMsp430InterruptP, 
+    LedsC, 
+    TimeP;  
+  //, NTPClientM;
+
+  FatFs = FatFsP;
+  diskIOStdControl = SDP;
+  //  diskIOStdControl = TimeP;
+  diskIO = SDP;
+
+  FatFsP.Leds        -> LedsC;
+
+  components TimeC;
+  FatFsP.Time        -> TimeC;
+  //  FatFsP.Time        -> TimeP;
+
+  SDP.Usart          -> Msp430Usart0C;
+  SDP.DockInterrupt  -> HplMsp430InterruptP.Port23;
+  SDP.Leds           -> LedsC;
+
+
+  /*
+  components Counter32khz64C as Counter;
+  components new CounterToLocalTime64C(T32khz);
+  CounterToLocalTime64C.Counter -> Counter;
+  TimeP.LocalTime64 -> CounterToLocalTime64C;
+   
+  TimeP.Timer                -> TimerMilliC;
+  */
+}
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/diskio.c b/tos/platforms/shimmer/chips/sd/fatfs/diskio.c
new file mode 100644 (file)
index 0000000..07c80e8
--- /dev/null
@@ -0,0 +1,192 @@
+/*----------------------------------------------------------------------------/\r
+  / FatFs module is an open source project to implement FAT file system to small\r
+  / embedded systems. It is opened for education, research and development under\r
+  / license policy of following trems.\r
+  /\r
+  /  Copyright (C) 2009, ChaN, all right reserved.\r
+  /\r
+  / * The FatFs module is a free software and there is no warranty.\r
+  / * You can use, modify and/or redistribute it for personal, non-profit or\r
+  /   commercial use without any restriction under your responsibility.\r
+  / * Redistributions of source code must retain the above copyright notice.\r
+  /\r
+  /----------------------------------------------------------------------------*/\r
+/*\r
+ * Most of this code:\r
+ *\r
+ * Copyright (c) 2009, Shimmer Research, Ltd.\r
+ * All rights reserved\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+\r
+ *     * Redistributions of source code must retain the above copyright\r
+ *       notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above\r
+ *       copyright notice, this list of conditions and the following\r
+ *       disclaimer in the documentation and/or other materials provided\r
+ *       with the distribution.\r
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its\r
+ *       contributors may be used to endorse or promote products derived\r
+ *       from this software without specific prior written permission.\r
+\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * tinyos instantiation of ChaN's diskio stubs (thank you!):\r
+ * function declarations are kept from ChaN's code to make updating easier;\r
+ * contents of these point to abstract calls -- as "diskio" -- \r
+ * to real physical io; for now, the control will point to SD.\r
+ *\r
+ * @author Steve Ayer\r
+ * @date   April, 2009\r
+ * ported from tos-1.x \r
+ * @date   January, 2010\r
+ */\r
+\r
+#include "diskio.h"\r
+\r
+static BOOL disk_available, disk_initialized;\r
+\r
+DSTATUS disk_initialize (BYTE drv)     // we only have one\r
+{\r
+  if(!disk_initialized){\r
+    atomic disk_available = TRUE;\r
+\r
+    call diskIOStdControl.start();\r
+    \r
+    atomic disk_initialized = TRUE;\r
+  }\r
+  return 0;\r
+}\r
+\r
+void disable_disk()\r
+{\r
+  atomic disk_available = FALSE;\r
+  \r
+  disk_initialized = FALSE;\r
+  \r
+  call diskIOStdControl.stop();\r
+}\r
+\r
+void dock_disk()\r
+{\r
+  atomic disk_available = FALSE;\r
+  \r
+  call diskIOStdControl.start();\r
+}\r
+\r
+DSTATUS disk_status (BYTE drv)        // just one\r
+{\r
+  atomic {\r
+    if(!disk_available)\r
+      return STA_NOINIT;\r
+  }\r
+  \r
+  return FR_OK;\r
+\r
+}\r
+\r
+DRESULT disk_read (BYTE drv,           \r
+                  BYTE * buff,         /* Data buffer to store read data */\r
+                  DWORD sector,        /* Sector address (LBA) */\r
+                  BYTE count)          /* Number of sectors to read (1..255) */\r
+{\r
+  int result = FR_OK;\r
+  register int i;\r
+\r
+  if(disk_available){\r
+    for(i = 0; i < count; i++)\r
+      if((result = call diskIO.readBlock(sector++, (uint8_t *)(buff + i * 512))))   // success is (still) 0\r
+       break;\r
+  }\r
+  else\r
+    result = FR_NOT_READY;\r
+\r
+  return result;\r
+}\r
+\r
+#if _READONLY == 0\r
+DRESULT disk_write (BYTE drv,          \r
+                   const BYTE *buff,   /* Data to be written */\r
+                   DWORD sector,               /* Sector address (LBA) */\r
+                   BYTE count)                 /* Number of sectors to write (1..255) */\r
+{\r
+  int result = FR_OK;\r
+  register int i;\r
+\r
+  if(disk_available){\r
+    for(i = 0; i < count; i++)\r
+      if((result = call diskIO.writeBlock(sector++, (uint8_t *)(buff + i * 512))))   // success is (still) 0\r
+       break;\r
+  }\r
+  else\r
+    return FR_NOT_READY;\r
+\r
+  return result;\r
+}\r
+#endif /* _READONLY */\r
+\r
+DRESULT disk_ioctl (BYTE drv,\r
+                   BYTE ctrl,          /* Control code */\r
+                   void * answer)              /* Buffer to send/receive control data */\r
+{\r
+  int result;\r
+  uint32_t capacity;   // bytes\r
+  /* \r
+   * calls we have to deal with (ctrl param) as of ff v0.07a:\r
+   *\r
+   * CTRL_SYNC make sure no accesses are pending\r
+   * GET_SECTOR_SIZE we're only supporting sd so far\r
+   * GET_BLOCK_SIZE  right, as above\r
+   * GET_SECTOR_COUNT sd driver has a read-success hack for this, should work\r
+   */\r
+  switch(ctrl){\r
+  case CTRL_SYNC:   // make sure we have availability\r
+    atomic{\r
+      if(disk_available)\r
+       result = FR_OK;\r
+      else\r
+       result = FR_NOT_READY;\r
+    }\r
+    break;\r
+  case GET_SECTOR_SIZE:\r
+  case GET_BLOCK_SIZE:\r
+    *(WORD *)answer = 512;\r
+    result = RES_OK;\r
+    break;\r
+  case GET_SECTOR_COUNT:\r
+    capacity = call diskIO.readCardSize();\r
+    *(DWORD *)answer = capacity / 512;\r
+    result = FR_OK;\r
+    break;\r
+  default:\r
+    *(WORD *)answer = 0;\r
+    result = FR_INVALID_NAME;\r
+    break;\r
+  }\r
+  return result;\r
+}\r
+\r
+async event void diskIO.available(){\r
+  signal FatFs.mediaAvailable();\r
+  \r
+  atomic disk_available = TRUE;\r
+}\r
+\r
+async event void diskIO.unavailable(){\r
+  signal FatFs.mediaUnavailable();\r
+  \r
+  atomic disk_available = FALSE;\r
+}\r
+\r
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/diskio.h b/tos/platforms/shimmer/chips/sd/fatfs/diskio.h
new file mode 100644 (file)
index 0000000..d20dbae
--- /dev/null
@@ -0,0 +1,115 @@
+/*-----------------------------------------------------------------------------/\r
+/ FatFs module is an open source software to implement FAT file system to\r
+/ small embedded systems. This is a free software and is opened for education,\r
+/ research and commecial developments under license policy of following trems.\r
+/\r
+/  Copyright (C) 2009, ChaN, all right reserved.\r
+/\r
+/ * The FatFs module is a free software and there is NO WARRANTY.\r
+/ * No restriction on use. You can use, modify and redistribute it for\r
+/   personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.\r
+/ * Redistributions of source code must retain the above copyright notice.\r
+/\r
+/-----------------------------------------------------------------------------*/\r
+/*\r
+ * Copyright (c) 2009, Shimmer Research, Ltd.\r
+ * All rights reserved\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+\r
+ *     * Redistributions of source code must retain the above copyright\r
+ *       notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above\r
+ *       copyright notice, this list of conditions and the following\r
+ *       disclaimer in the documentation and/or other materials provided\r
+ *       with the distribution.\r
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its\r
+ *       contributors may be used to endorse or promote products derived\r
+ *       from this software without specific prior written permission.\r
+\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * @author Steve Ayer\r
+ * @date   April, 2009\r
+ * ported to tos-2.x\r
+ * @date January, 2010\r
+ */\r
+\r
+\r
+#ifndef _DISKIO\r
+\r
+#define _READONLY      0       /* 1: Read-only mode */\r
+#define _USE_IOCTL     1\r
+\r
+#include "integer.h"\r
+\r
+/* Status of Disk Functions */\r
+typedef BYTE   DSTATUS;\r
+\r
+/* Results of Disk Functions */\r
+typedef enum {\r
+  RES_OK = 0,          /* 0: Successful */\r
+  RES_ERROR,           /* 1: R/W Error */\r
+  RES_WRPRT,           /* 2: Write Protected */\r
+  RES_NOTRDY,          /* 3: Not Ready */\r
+  RES_PARERR           /* 4: Invalid Parameter */\r
+} DRESULT;\r
+\r
+\r
+/*---------------------------------------*/\r
+/* Prototypes for disk control functions */\r
+\r
+BOOL assign_drives (int argc, char *argv[]);\r
+DSTATUS disk_initialize (BYTE);\r
+void disable_disk(void);\r
+DSTATUS disk_status (BYTE);\r
+DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);\r
+#if    _READONLY == 0\r
+DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);\r
+#endif\r
+DRESULT disk_ioctl (BYTE, BYTE, void*);\r
+\r
+\r
+/* Disk Status Bits (DSTATUS) */\r
+\r
+#define STA_NOINIT             0x01    /* Drive not initialized */\r
+#define STA_NODISK             0x02    /* No medium in the drive */\r
+#define STA_PROTECT            0x04    /* Write protected */\r
+\r
+\r
+/* Command code for disk_ioctrl() */\r
+\r
+/* Generic command */\r
+#define CTRL_SYNC                      0       /* Mandatory for write functions */\r
+#define GET_SECTOR_COUNT       1       /* Mandatory for only f_mkfs() */\r
+#define GET_SECTOR_SIZE                2\r
+#define GET_BLOCK_SIZE         3       /* Mandatory for only f_mkfs() */\r
+#define CTRL_POWER                     4\r
+#define CTRL_LOCK                      5\r
+#define CTRL_EJECT                     6\r
+/* MMC/SDC command */\r
+#define MMC_GET_TYPE           10\r
+#define MMC_GET_CSD                    11\r
+#define MMC_GET_CID                    12\r
+#define MMC_GET_OCR                    13\r
+#define MMC_GET_SDSTAT         14\r
+/* ATA/CF command */\r
+#define ATA_GET_REV                    20\r
+#define ATA_GET_MODEL          21\r
+#define ATA_GET_SN                     22\r
+\r
+\r
+#define _DISKIO\r
+#endif\r
diff --git a/tos/platforms/shimmer/chips/sd/fatfs/integer.h b/tos/platforms/shimmer/chips/sd/fatfs/integer.h
new file mode 100644 (file)
index 0000000..556046c
--- /dev/null
@@ -0,0 +1,83 @@
+/*-----------------------------------------------------------------------------/\r
+/ FatFs module is an open source software to implement FAT file system to\r
+/ small embedded systems. This is a free software and is opened for education,\r
+/ research and commecial developments under license policy of following trems.\r
+/\r
+/  Copyright (C) 2009, ChaN, all right reserved.\r
+/\r
+/ * The FatFs module is a free software and there is NO WARRANTY.\r
+/ * No restriction on use. You can use, modify and redistribute it for\r
+/   personal, non-profit or commercial use UNDER YOUR RESPONSIBILITY.\r
+/ * Redistributions of source code must retain the above copyright notice.\r
+/\r
+/-----------------------------------------------------------------------------*/\r
+/*\r
+ * Portions of this code:\r
+ *\r
+ * Copyright (c) 2009, Shimmer Research, Ltd.\r
+ * All rights reserved\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+\r
+ *     * Redistributions of source code must retain the above copyright\r
+ *       notice, this list of conditions and the following disclaimer.\r
+ *     * Redistributions in binary form must reproduce the above\r
+ *       copyright notice, this list of conditions and the following\r
+ *       disclaimer in the documentation and/or other materials provided\r
+ *       with the distribution.\r
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its\r
+ *       contributors may be used to endorse or promote products derived\r
+ *       from this software without specific prior written permission.\r
+\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\r
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * tinyos port from ChaN's FatFs (thank you!):\r
+ *\r
+ * @author Steve Ayer\r
+ * @date   April, 2009\r
+ * ported to tos-2.x (easy)\r
+ * @date January, 2010\r
+ */\r
+\r
+#ifndef _INTEGER\r
+\r
+// original typedefs converted to nesc stdint.h types for tos consistency\r
+/* These types must be 16-bit, 32-bit or larger integer */\r
+typedef int16_t                INT;\r
+typedef uint16_t        UINT;\r
+\r
+/* These types must be 8-bit integer */\r
+// had to change signed char typedef CHAR to SCHAR to avoid conflict with msp430 usart.h (#define CHAR  0x10)\r
+typedef int8_t SCHAR;\r
+typedef uint8_t        UCHAR;\r
+typedef uint8_t        BYTE;\r
+\r
+/* These types must be 16-bit integer */\r
+typedef int16_t                SHORT;\r
+typedef uint16_t       USHORT;\r
+typedef uint16_t       WORD;\r
+typedef uint16_t       WCHAR;\r
+\r
+/* These types must be 32-bit integer */\r
+typedef int32_t                LONG;\r
+typedef uint32_t       ULONG;\r
+typedef uint32_t       DWORD;\r
+\r
+/* Boolean type */\r
+//typedef enum { FALSE = 0, TRUE } BOOL;\r
+typedef char BOOL;\r
+\r
+#define _INTEGER\r
+#endif\r
diff --git a/tos/platforms/shimmer/shimmerAnalogSetup.nc b/tos/platforms/shimmer/shimmerAnalogSetup.nc
new file mode 100644 (file)
index 0000000..8e2be17
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Steve Ayer
+ * @date March, 2010
+ *
+ * this interface is really just for cleaning up apps.
+ * wire the app's softwareinit.init to the component's 
+ * then call *in sequence* the add<Device>Inputs you want to sample: 
+ * that will be the order of the samples.
+ *
+ * REMEMBER:  only eight channels, only one internal daughter card at a time, but 
+ * notice that the anex is provided.
+ */
+
+interface shimmerAnalogSetup {
+
+  // three channels
+  command void addAccelInputs();   
+
+  // three channels
+  command void addGyroInputs();
+
+  // two channels
+  command void addECGInputs();
+
+  // three channels
+  command void addUVInputs();
+
+  // one channels
+  command void addGSRInput();
+  
+  // two channels
+  command void addAnExInput();
+  
+  /*
+   * call this after adding devices.
+   * pass in a buffer to hold the sampling results
+   * switching buffers can be done in the transferDone event
+   */
+  command void finishADCSetup(uint16_t * buffer);
+  
+  command void triggerConversion();
+  
+  command void stopConversion();
+
+  command uint8_t getNumberOfChannels();
+}
+
diff --git a/tos/platforms/shimmer/shimmerAnalogSetupC.nc b/tos/platforms/shimmer/shimmerAnalogSetupC.nc
new file mode 100644 (file)
index 0000000..0565963
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Steve Ayer
+ * @date March, 2010
+ */
+
+configuration shimmerAnalogSetupC {
+  provides {
+    interface Init;
+    interface shimmerAnalogSetup;
+  }
+}
+
+implementation {
+  components shimmerAnalogSetupP;
+  shimmerAnalogSetup = shimmerAnalogSetupP;
+  Init = shimmerAnalogSetupP;
+  
+  components HplAdc12P;
+  shimmerAnalogSetupP.HplAdc12 -> HplAdc12P;
+  
+  components Msp430DmaC;
+  shimmerAnalogSetupP.Msp430DmaControl -> Msp430DmaC;
+  shimmerAnalogSetupP.Msp430DmaChannel -> Msp430DmaC.Channel0;
+  
+  components LedsC;
+  shimmerAnalogSetupP.Leds -> LedsC;
+  
+}
+  
+  
diff --git a/tos/platforms/shimmer/shimmerAnalogSetupP.nc b/tos/platforms/shimmer/shimmerAnalogSetupP.nc
new file mode 100644 (file)
index 0000000..a4d926b
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2010, Shimmer Research, Ltd.
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of Shimmer Research, Ltd. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Steve Ayer
+ * @date March, 2010
+
+ * this is going to get duplicated in a million apps, so we gather simple setup 
+ * routines with reasonable defaults here.  specific 
+ */
+
+
+module shimmerAnalogSetupP {
+  provides{
+    interface shimmerAnalogSetup;
+    interface Init;
+  }
+  uses {
+    interface Msp430DmaControl;
+    interface Msp430DmaChannel;
+    interface HplAdc12;
+    interface Leds;
+  }
+}
+
+implementation { 
+  void initADC12CTL0();
+  void initADC12CTL1();
+  void initADC12MEMCTLx();
+  void setupDMA(uint16_t * destAddr);
+  void addNewChannels(uint8_t * chans, uint8_t howmany_new);
+
+  uint8_t NUM_ADC_CHANS = 0;
+  uint8_t ADC_CHANS[8];  // msp430 only has eight!
+
+  command void shimmerAnalogSetup.addAccelInputs() {
+    uint8_t new_chans[] = { 5, 4, 3 };
+    addNewChannels(new_chans, 3);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.addGyroInputs() {
+    uint8_t new_chans[] = { 1, 6, 2 };    // x, y, z
+    addNewChannels(new_chans, 3);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.addECGInputs() {
+    uint8_t new_chans[] = { 1, 2 };  // ecg_lall, ecg_rall
+    addNewChannels(new_chans, 2);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.addUVInputs() {
+    uint8_t new_chans[] = { 1, 2, 6 };    // ambient, uvb, uva
+    addNewChannels(new_chans, 3);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.addGSRInput() { 
+    uint8_t new_chans[] = { 1 };
+    addNewChannels(new_chans, 1);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.addAnExInput() { 
+    uint8_t new_chans[] = { 0, 7 };
+    addNewChannels(new_chans, 2);
+    
+    initADC12MEMCTLx();
+  }
+
+  command void shimmerAnalogSetup.finishADCSetup(uint16_t * buffer){
+    setupDMA(buffer);
+  }
+  
+  command void shimmerAnalogSetup.triggerConversion() {
+    call Msp430DmaChannel.startTransfer();
+    call HplAdc12.startConversion();
+  }
+
+  command void shimmerAnalogSetup.stopConversion() {
+    call HplAdc12.stopConversion();
+    call HplAdc12.setIEFlags(0);
+    call HplAdc12.resetIFGs();
+  }                                            
+    
+  command uint8_t shimmerAnalogSetup.getNumberOfChannels() {
+    return NUM_ADC_CHANS;
+  }
+
+  command error_t Init.init() {
+    initADC12CTL0();
+    initADC12CTL1();
+    //    initADC12MEMCTLx();
+
+    TOSH_uwait(50000);
+    return SUCCESS;
+  }
+
+  void addNewChannels(uint8_t * chans, uint8_t howmany_new) {
+    register uint8_t i, j;
+    
+    for(j = 0, i = NUM_ADC_CHANS; (j < howmany_new) && (i < 8) ; i++, j++)
+      ADC_CHANS[i] = chans[j];
+
+    NUM_ADC_CHANS += howmany_new;
+  }
+
+  void initADC12CTL0()
+  {
+    adc12ctl0_t ctl0 = {
+      adc12sc: 0,                      // start conversion: 0 = no sample-and-conversion-start
+      enc: 0,                          // enable conversion: 0 = ADC12 disabled
+      adc12tovie: 0,                   // conversion-time-overflow-interrupt: 0 = interrupt dissabled
+      adc12ovie: 0,                    // ADC12MEMx overflow-interrupt: 0 = dissabled
+      adc12on: 1,                      // ADC12 on: 1 = on
+      refon: 0,                        // reference generator: 0 = off
+      r2_5v: 1,                        // reference generator voltage: 1 = 2.5V
+      msc: 1,                          // multiple sample and conversion: 1 = conversions performed ASAP
+      sht0: SAMPLE_HOLD_4_CYCLES,      // sample-and-hold-time for  ADC12MEM0 to ADC12MEM7  
+      sht1: SAMPLE_HOLD_4_CYCLES       // sample-and-hold-time for  ADC12MEM8 to ADC12MEM15  
+    };
+
+    call HplAdc12.setCtl0(ctl0);
+  }
+
+  void initADC12CTL1()
+  {
+    adc12ctl1_t ctl1 = {
+      adc12busy: 0,                    // no operation is active
+      conseq: 1,                       // conversion mode: sequence of chans
+      adc12ssel: SHT_SOURCE_SMCLK,     // SHT_SOURCE_SMCLK=3; ADC12 clocl source
+      adc12div: SHT_CLOCK_DIV_8,       // SHT_CLOCK_DIV_8=7; ADC12 clock div 1
+      issh: 0,                         // sample-input signal not inverted
+      shp: 1,                          // Sample-and-hold pulse-mode select: SAMPCON signal is sourced from the sampling timer
+      shs: 0,                          // Sample-and-hold source select= ADC12SC bit
+      cstartadd: 0                     // conversion start addres ADC12MEM0
+    }; 
+
+    call HplAdc12.setCtl1(ctl1);
+  }
+
+  void initADC12MEMCTLx()
+  {
+    uint8_t i;
+    adc12memctl_t memctl = {
+      inch: 0,
+      sref: REFERENCE_AVcc_AVss,      // reference voltage: 
+      eos: 1                          // end of sequence flag: 1 indicates last conversion
+    };
+
+    for (i = 0; i < NUM_ADC_CHANS; ++i) {
+      memctl.inch = ADC_CHANS[i];
+
+      if (i < NUM_ADC_CHANS - 1)
+       memctl.eos = 0;
+      else 
+       memctl.eos = 1;                   // eos=1 indicates last conversion in sequence
+
+      call HplAdc12.setMCtl(i, memctl);
+    }
+  }
+
+  void setupDMA(uint16_t * destAddr) {
+    call Msp430DmaControl.init();                                     // blanks registers
+
+    call Msp430DmaControl.setFlags(FALSE, FALSE, FALSE);              // enable_nmi, round_robin, on_fetch
+
+    call Msp430DmaChannel.setupTransfer(DMA_BLOCK_TRANSFER,           //dma_transfer_mode_t transfer_mode, 
+                                       DMA_TRIGGER_ADC12IFGx,        //dma_trigger_t trigger, 
+                                       DMA_EDGE_SENSITIVE,           //dma_level_t level,
+                                       (void *)ADC12MEM0_,            //void *src_addr, 
+                                       (void *)destAddr,              //void *dst_addr, 
+                                       NUM_ADC_CHANS,                //uint16_t size,
+                                       DMA_WORD,                     //dma_byte_t src_byte, 
+                                       DMA_WORD,                     //dma_byte_t dst_byte,
+                                       DMA_ADDRESS_INCREMENTED,      //dma_incr_t src_incr, 
+                                       DMA_ADDRESS_INCREMENTED);     //dma_incr_t dst_incr
+
+    call Msp430DmaChannel.startTransfer();
+  }
+  async event void Msp430DmaChannel.transferDone(error_t success) {
+  }
+  async event void HplAdc12.conversionDone(uint16_t iv) {
+  }
+}
+  
+