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.
%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
--- /dev/null
+
+/* "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;
+}
+
--- /dev/null
+
+/* "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;
+}
+
--- /dev/null
+//$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()
+ {
+ }
+}
+
--- /dev/null
+//$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();
+}
+
--- /dev/null
+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__ */
+};
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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
+ */
+
+}
+
--- /dev/null
+
+
+/* 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;
+ }
+ *****************************************/
+
+}
--- /dev/null
+//$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;
+ }
+}
+
--- /dev/null
+//$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.
+}
+
--- /dev/null
+
+/* "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;
+}
--- /dev/null
+/*
+ * 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
+};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*----------------------------------------------------------------------------/\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
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*---------------------------------------------------------------------------/
+ / 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, <ime);
+ call Time.asctime(<ime, 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, <ime);
+
+ 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 */
+
+}
--- /dev/null
+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...).
+
+
+
+
+
+
--- /dev/null
+/*------------------------------------------------------------------------*/\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
--- /dev/null
+/*
+ * 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;
+ */
+}
--- /dev/null
+/*----------------------------------------------------------------------------/\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
--- /dev/null
+/*-----------------------------------------------------------------------------/\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
--- /dev/null
+/*-----------------------------------------------------------------------------/\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
--- /dev/null
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ * 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;
+
+}
+
+
--- /dev/null
+/*
+ * 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) {
+ }
+}
+
+