Browse Source

bugfix-2.0.x critical fix (#7401)

* Fix mistake in gitignore file and add in missing core files.

The missing leading slash on "lib" meant all folders names lib in the directory tree are ignored, rather than just the top level PlatformIO lib folder

* Add LiquidCrystal Library and associated headers modified to compile.
Chris Pepper 7 years ago
parent
commit
a5cf3a190c

+ 1 - 1
.gitignore

@@ -122,7 +122,7 @@ tags
 .piolibdeps
 .clang_complete
 .gcc-flags.json
-lib/
+/lib/
 
 #Visual Studio
 *.sln

+ 326 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/LiquidCrystal.cpp

@@ -0,0 +1,326 @@
+#include "LiquidCrystal.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include "../../../../src/HAL/HAL_LPC1768/arduino.h"
+
+// When the display powers up, it is configured as follows:
+//
+// 1. Display clear
+// 2. Function set: 
+//    DL = 1; 8-bit interface data 
+//    N = 0; 1-line display 
+//    F = 0; 5x8 dot character font 
+// 3. Display on/off control: 
+//    D = 0; Display off 
+//    C = 0; Cursor off 
+//    B = 0; Blinking off 
+// 4. Entry mode set: 
+//    I/D = 1; Increment by 1 
+//    S = 0; No shift 
+//
+// Note, however, that resetting the Arduino doesn't reset the LCD, so we
+// can't assume that its in that state when a sketch starts (and the
+// LiquidCrystal constructor is called).
+
+LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
+			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
+{
+  init(0, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7);
+}
+
+LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t enable,
+			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+			     uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
+{
+  init(0, rs, 255, enable, d0, d1, d2, d3, d4, d5, d6, d7);
+}
+
+LiquidCrystal::LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
+			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
+{
+  init(1, rs, rw, enable, d0, d1, d2, d3, 0, 0, 0, 0);
+}
+
+LiquidCrystal::LiquidCrystal(uint8_t rs,  uint8_t enable,
+			     uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
+{
+  init(1, rs, 255, enable, d0, d1, d2, d3, 0, 0, 0, 0);
+}
+
+void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
+			 uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+			 uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7)
+{
+  _rs_pin = rs;
+  _rw_pin = rw;
+  _enable_pin = enable;
+  
+  _data_pins[0] = d0;
+  _data_pins[1] = d1;
+  _data_pins[2] = d2;
+  _data_pins[3] = d3; 
+  _data_pins[4] = d4;
+  _data_pins[5] = d5;
+  _data_pins[6] = d6;
+  _data_pins[7] = d7; 
+
+  if (fourbitmode)
+    _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
+  else 
+    _displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;
+  
+  begin(16, 1);  
+}
+
+void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
+  if (lines > 1) {
+    _displayfunction |= LCD_2LINE;
+  }
+  _numlines = lines;
+
+  setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);  
+
+  // for some 1 line displays you can select a 10 pixel high font
+  if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
+    _displayfunction |= LCD_5x10DOTS;
+  }
+
+  pinMode(_rs_pin, OUTPUT);
+  // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
+  if (_rw_pin != 255) { 
+    pinMode(_rw_pin, OUTPUT);
+  }
+  pinMode(_enable_pin, OUTPUT);
+  
+  // Do these once, instead of every time a character is drawn for speed reasons.
+  for (int i=0; i<((_displayfunction & LCD_8BITMODE) ? 8 : 4); ++i)
+  {
+    pinMode(_data_pins[i], OUTPUT);
+   } 
+
+  // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
+  // according to datasheet, we need at least 40ms after power rises above 2.7V
+  // before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
+  delayMicroseconds(50000); 
+  // Now we pull both RS and R/W low to begin commands
+  digitalWrite(_rs_pin, LOW);
+  digitalWrite(_enable_pin, LOW);
+  if (_rw_pin != 255) { 
+    digitalWrite(_rw_pin, LOW);
+  }
+  
+  //put the LCD into 4 bit or 8 bit mode
+  if (! (_displayfunction & LCD_8BITMODE)) {
+    // this is according to the hitachi HD44780 datasheet
+    // figure 24, pg 46
+
+    // we start in 8bit mode, try to set 4 bit mode
+    write4bits(0x03);
+    delayMicroseconds(4500); // wait min 4.1ms
+
+    // second try
+    write4bits(0x03);
+    delayMicroseconds(4500); // wait min 4.1ms
+    
+    // third go!
+    write4bits(0x03); 
+    delayMicroseconds(150);
+
+    // finally, set to 4-bit interface
+    write4bits(0x02); 
+  } else {
+    // this is according to the hitachi HD44780 datasheet
+    // page 45 figure 23
+
+    // Send function set command sequence
+    command(LCD_FUNCTIONSET | _displayfunction);
+    delayMicroseconds(4500);  // wait more than 4.1ms
+
+    // second try
+    command(LCD_FUNCTIONSET | _displayfunction);
+    delayMicroseconds(150);
+
+    // third go
+    command(LCD_FUNCTIONSET | _displayfunction);
+  }
+
+  // finally, set # lines, font size, etc.
+  command(LCD_FUNCTIONSET | _displayfunction);  
+
+  // turn the display on with no cursor or blinking default
+  _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;  
+  display();
+
+  // clear it off
+  clear();
+
+  // Initialize to default text direction (for romance languages)
+  _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+  // set the entry mode
+  command(LCD_ENTRYMODESET | _displaymode);
+
+}
+
+void LiquidCrystal::setRowOffsets(int row0, int row1, int row2, int row3)
+{
+  _row_offsets[0] = row0;
+  _row_offsets[1] = row1;
+  _row_offsets[2] = row2;
+  _row_offsets[3] = row3;
+}
+
+/********** high level commands, for the user! */
+void LiquidCrystal::clear()
+{
+  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
+  delayMicroseconds(2000);  // this command takes a long time!
+}
+
+void LiquidCrystal::home()
+{
+  command(LCD_RETURNHOME);  // set cursor position to zero
+  delayMicroseconds(2000);  // this command takes a long time!
+}
+
+void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
+{
+  const size_t max_lines = sizeof(_row_offsets) / sizeof(*_row_offsets);
+  if ( row >= max_lines ) {
+    row = max_lines - 1;    // we count rows starting w/0
+  }
+  if ( row >= _numlines ) {
+    row = _numlines - 1;    // we count rows starting w/0
+  }
+  
+  command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
+}
+
+// Turn the display on/off (quickly)
+void LiquidCrystal::noDisplay() {
+  _displaycontrol &= ~LCD_DISPLAYON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal::display() {
+  _displaycontrol |= LCD_DISPLAYON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// Turns the underline cursor on/off
+void LiquidCrystal::noCursor() {
+  _displaycontrol &= ~LCD_CURSORON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal::cursor() {
+  _displaycontrol |= LCD_CURSORON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// Turn on and off the blinking cursor
+void LiquidCrystal::noBlink() {
+  _displaycontrol &= ~LCD_BLINKON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal::blink() {
+  _displaycontrol |= LCD_BLINKON;
+  command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// These commands scroll the display without changing the RAM
+void LiquidCrystal::scrollDisplayLeft(void) {
+  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
+}
+void LiquidCrystal::scrollDisplayRight(void) {
+  command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
+}
+
+// This is for text that flows Left to Right
+void LiquidCrystal::leftToRight(void) {
+  _displaymode |= LCD_ENTRYLEFT;
+  command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This is for text that flows Right to Left
+void LiquidCrystal::rightToLeft(void) {
+  _displaymode &= ~LCD_ENTRYLEFT;
+  command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'right justify' text from the cursor
+void LiquidCrystal::autoscroll(void) {
+  _displaymode |= LCD_ENTRYSHIFTINCREMENT;
+  command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'left justify' text from the cursor
+void LiquidCrystal::noAutoscroll(void) {
+  _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
+  command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// Allows us to fill the first 8 CGRAM locations
+// with custom characters
+void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
+  location &= 0x7; // we only have 8 locations 0-7
+  command(LCD_SETCGRAMADDR | (location << 3));
+  for (int i=0; i<8; i++) {
+    write(charmap[i]);
+  }
+}
+
+/*********** mid level commands, for sending data/cmds */
+
+inline void LiquidCrystal::command(uint8_t value) {
+  send(value, LOW);
+}
+
+inline size_t LiquidCrystal::write(uint8_t value) {
+  send(value, HIGH);
+  return 1; // assume sucess
+}
+
+/************ low level data pushing commands **********/
+
+// write either command or data, with automatic 4/8-bit selection
+void LiquidCrystal::send(uint8_t value, uint8_t mode) {
+  digitalWrite(_rs_pin, mode);
+
+  // if there is a RW pin indicated, set it low to Write
+  if (_rw_pin != 255) { 
+    digitalWrite(_rw_pin, LOW);
+  }
+  
+  if (_displayfunction & LCD_8BITMODE) {
+    write8bits(value); 
+  } else {
+    write4bits(value>>4);
+    write4bits(value);
+  }
+}
+
+void LiquidCrystal::pulseEnable(void) {
+  digitalWrite(_enable_pin, LOW);
+  delayMicroseconds(1);    
+  digitalWrite(_enable_pin, HIGH);
+  delayMicroseconds(1);    // enable pulse must be >450ns
+  digitalWrite(_enable_pin, LOW);
+  delayMicroseconds(100);   // commands need > 37us to settle
+}
+
+void LiquidCrystal::write4bits(uint8_t value) {
+  for (int i = 0; i < 4; i++) {
+    digitalWrite(_data_pins[i], (value >> i) & 0x01);
+  }
+
+  pulseEnable();
+}
+
+void LiquidCrystal::write8bits(uint8_t value) {
+  for (int i = 0; i < 8; i++) {
+    digitalWrite(_data_pins[i], (value >> i) & 0x01);
+  }
+  
+  pulseEnable();
+}

+ 109 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/LiquidCrystal.h

@@ -0,0 +1,109 @@
+#ifndef LiquidCrystal_h
+#define LiquidCrystal_h
+
+#include <inttypes.h>
+#include "binary.h"
+#include "Print.h"
+
+// commands
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE 0x10
+#define LCD_4BITMODE 0x00
+#define LCD_2LINE 0x08
+#define LCD_1LINE 0x00
+#define LCD_5x10DOTS 0x04
+#define LCD_5x8DOTS 0x00
+
+class LiquidCrystal : public Print {
+public:
+  LiquidCrystal(uint8_t rs, uint8_t enable,
+		uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+		uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
+  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
+		uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+		uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
+  LiquidCrystal(uint8_t rs, uint8_t rw, uint8_t enable,
+		uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
+  LiquidCrystal(uint8_t rs, uint8_t enable,
+		uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3);
+
+  void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
+	    uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
+	    uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);
+    
+  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
+
+  void clear();
+  void home();
+
+  void noDisplay();
+  void display();
+  void noBlink();
+  void blink();
+  void noCursor();
+  void cursor();
+  void scrollDisplayLeft();
+  void scrollDisplayRight();
+  void leftToRight();
+  void rightToLeft();
+  void autoscroll();
+  void noAutoscroll();
+
+  void setRowOffsets(int row1, int row2, int row3, int row4);
+  void createChar(uint8_t, uint8_t[]);
+  void setCursor(uint8_t, uint8_t); 
+  virtual size_t write(uint8_t);
+  void command(uint8_t);
+  
+  using Print::write;
+private:
+  void send(uint8_t, uint8_t);
+  void write4bits(uint8_t);
+  void write8bits(uint8_t);
+  void pulseEnable();
+
+  uint8_t _rs_pin; // LOW: command.  HIGH: character.
+  uint8_t _rw_pin; // LOW: write to LCD.  HIGH: read from LCD.
+  uint8_t _enable_pin; // activated by a HIGH pulse.
+  uint8_t _data_pins[8];
+
+  uint8_t _displayfunction;
+  uint8_t _displaycontrol;
+  uint8_t _displaymode;
+
+  uint8_t _initialized;
+
+  uint8_t _numlines;
+  uint8_t _row_offsets[4];
+};
+
+#endif

+ 356 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/Print.cpp

@@ -0,0 +1,356 @@
+/*
+ Print.cpp - Base class that provides print() and println()
+ Copyright (c) 2008 David A. Mellis.  All right reserved.
+ 
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ 
+ Modified 23 November 2006 by David A. Mellis
+ Modified 03 August 2015 by Chuck Todd
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <inttypes.h>
+#include "Print.h"
+#include <stdarg.h>
+
+#define PrintfEnable  0
+// Public Methods //////////////////////////////////////////////////////////////
+
+/* default implementation: may be overridden */
+size_t Print::write(const uint8_t *buffer, size_t size)
+{
+	
+  size_t n = 0;
+  while (size--) {
+    if (write(*buffer++)) n++;
+    else break;
+  }
+  return n;
+}
+
+
+size_t Print::print(const char str[])
+{
+	
+	//while(1);
+  return write(str);
+}
+
+size_t Print::print(char c)
+{
+  return write(c);
+}
+
+size_t Print::print(unsigned char b, int base)
+{
+  return print((unsigned long) b, base);
+}
+
+size_t Print::print(int n, int base)
+{
+  return print((long) n, base);
+}
+
+size_t Print::print(unsigned int n, int base)
+{
+  return print((unsigned long) n, base);
+}
+
+size_t Print::print(long n, int base)
+{
+  if (base == 0) {
+    return write(n);
+  } else if (base == 10) {
+    if (n < 0) {
+      int t = print('-');
+      n = -n;
+      return printNumber(n, 10) + t;
+    }
+    return printNumber(n, 10);
+  } else {
+    return printNumber(n, base);
+  }
+}
+
+size_t Print::print(unsigned long n, int base)
+{
+  if (base == 0) return write(n);
+  else return printNumber(n, base);
+}
+
+size_t Print::print(double n, int digits)
+{
+  return printFloat(n, digits);
+}
+
+size_t Print::print(const Printable& x)
+{
+  return x.printTo(*this);
+}
+
+size_t Print::println(void)
+{
+  return write("\r\n");
+}
+
+size_t Print::println(const char c[])
+{
+  size_t n = print(c);
+  n += println();
+  return n;
+}
+
+size_t Print::println(char c)
+{
+  size_t n = print(c);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned char b, int base)
+{
+  size_t n = print(b, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(int num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned int num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(long num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned long num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(double num, int digits)
+{
+  size_t n = print(num, digits);
+  n += println();
+  return n;
+}
+
+size_t Print::println(const Printable& x)
+{
+  size_t n = print(x);
+  n += println();
+  return n;
+}
+
+// Private Methods /////////////////////////////////////////////////////////////
+
+size_t Print::printNumber(unsigned long n, uint8_t base) {
+  char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
+  char *str = &buf[sizeof(buf) - 1];
+
+  *str = '\0';
+
+  // prevent crash if called with base == 1
+  if (base < 2) base = 10;
+
+  do {
+    unsigned long m = n;
+    n /= base;
+    char c = m - base * n;
+    *--str = c < 10 ? c + '0' : c + 'A' - 10;
+  } while(n);
+
+  return write(str);
+}
+
+size_t Print::printFloat(double number, uint8_t digits) 
+{ 
+  size_t n = 0;
+  
+  if (isnan(number)) return print("nan");
+  if (isinf(number)) return print("inf");
+  if (number > 4294967040.0) return print ("ovf");  // constant determined empirically
+  if (number <-4294967040.0) return print ("ovf");  // constant determined empirically
+  
+  // Handle negative numbers
+  if (number < 0.0)
+  {
+     n += print('-');
+     number = -number;
+  }
+
+  // Round correctly so that print(1.999, 2) prints as "2.00"
+  double rounding = 0.5;
+  for (uint8_t i=0; i<digits; ++i)
+    rounding /= 10.0;
+  
+  number += rounding;
+
+  // Extract the integer part of the number and print it
+  unsigned long int_part = (unsigned long)number;
+  double remainder = number - (double)int_part;
+  n += print(int_part);
+
+  // Print the decimal point, but only if there are digits beyond
+  if (digits > 0) {
+    n += print("."); 
+  }
+
+  // Extract digits from the remainder one at a time
+  while (digits-- > 0)
+  {
+    remainder *= 10.0;
+    int toPrint = int(remainder);
+    n += print(toPrint);
+    remainder -= toPrint; 
+  } 
+  
+  return n;
+}
+
+
+#if (PrintfEnable == 1) 
+size_t Print::printf(const char *argList, ...)
+{
+    const char *ptr;
+    double floatNum_f32;
+    va_list argp;
+    sint16_t num_s16;
+    sint32_t num_s32;
+    uint16_t num_u16;
+    uint32_t num_u32;
+    char *str;
+    char  ch;
+    uint8_t numOfDigits;
+
+    va_start(argp, argList);
+
+    /* Loop through the list to extract all the input arguments */
+    for(ptr = argList; *ptr != '\0'; ptr++)
+    {
+
+        ch= *ptr;
+        if(ch == '%')         /*Check for '%' as there will be format specifier after it */
+        {
+            ptr++;
+            ch = *ptr;
+            if((ch>=0x30) && (ch<=0x39))
+            {
+                numOfDigits = 0;
+                while((ch>=0x30) && (ch<=0x39))
+                {
+                    numOfDigits = (numOfDigits * 10) + (ch-0x30);
+                    ptr++;
+                    ch = *ptr;
+                }
+            }
+            else
+            {
+                numOfDigits = 0xff;
+            }                
+
+
+            switch(ch)       /* Decode the type of the argument */
+            {
+
+            case 'C':
+            case 'c':     /* Argument type is of char, hence read char data from the argp */
+                ch = va_arg(argp, int);
+                print(ch);
+                break;
+
+
+
+            case 'd':    /* Argument type is of signed integer, hence read 16bit data from the argp */
+            case 'D':
+                num_s32 = va_arg(argp, int);
+                print(num_s32, 10);
+                break;  
+
+
+            case 'u':
+            case 'U':    /* Argument type is of integer, hence read 32bit unsigend data */
+                num_u32 = va_arg(argp, uint32_t);
+                print(num_u32, 10);               
+                break;            
+
+
+
+
+            case 'x':
+            case 'X':  /* Argument type is of hex, hence hexadecimal data from the argp */
+                num_u32 = va_arg(argp, uint32_t);
+                print(num_u32, 16);                 
+                break;
+
+
+            case 'b':
+            case 'B':  /* Argument type is of binary,Read int and convert to binary */
+                num_u32 = va_arg(argp, uint32_t); 
+                print(num_u32, 2);                 
+                break;
+
+
+
+            case 'F':
+            case 'f': /* Argument type is of float, hence read double data from the argp */
+                floatNum_f32 = va_arg(argp, double);              
+                printFloat(floatNum_f32,10);
+                break;
+
+
+
+            case 'S':
+            case 's': /* Argument type is of string, hence get the pointer to sting passed */
+                str = va_arg(argp, char *);
+                print(str);                
+                break;
+
+
+
+            case '%':
+                print('%');
+                break;
+            }
+        }
+        else
+        {
+            /* As '%' is not detected transmit the char passed */
+            print(ch);
+        }
+    }
+
+    va_end(argp);
+}
+
+
+#endif    

+ 85 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/Print.h

@@ -0,0 +1,85 @@
+/*
+  Print.h - Base class that provides print() and println()
+  Copyright (c) 2008 David A. Mellis.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef Print_h
+#define Print_h
+
+
+#include <inttypes.h>
+#include <stdio.h> // for size_t
+#include <string.h>
+
+
+#include "Printable.h"
+
+
+#define DEC 10
+#define HEX 16
+#define OCT 8
+#define BIN 2
+
+class Print
+{
+  private:
+    int write_error;
+    size_t printNumber(unsigned long, uint8_t);
+    size_t printFloat(double, uint8_t);
+  protected:
+    void setWriteError(int err = 1) { write_error = err; }
+  public:
+    Print() : write_error(0) {}
+  
+    int getWriteError() { return write_error; }
+    void clearWriteError() { setWriteError(0); }
+  
+    virtual size_t write(uint8_t) = 0;
+    size_t write(const char *str) {
+		
+      if (str == NULL) return 0;
+      return write((const uint8_t *)str, strlen(str));
+    }
+    virtual size_t write(const uint8_t *buffer, size_t size);
+    size_t write(const char *buffer, size_t size) {
+      return write((const uint8_t *)buffer, size);
+    }
+    
+    size_t print(const char[]);
+    size_t print(char);
+    size_t print(unsigned char, int = DEC);
+    size_t print(int, int = DEC);
+    size_t print(unsigned int, int = DEC);
+    size_t print(long, int = DEC);
+    size_t print(unsigned long, int = DEC);
+    size_t print(double, int = 2);
+    size_t print(const Printable&);
+
+    size_t println(const char[]);
+    size_t println(char);
+    size_t println(unsigned char, int = DEC);
+    size_t println(int, int = DEC);
+    size_t println(unsigned int, int = DEC);
+    size_t println(long, int = DEC);
+    size_t println(unsigned long, int = DEC);
+    size_t println(double, int = 2);
+    size_t println(const Printable&);
+    size_t println(void);
+    size_t printf(const char *argList, ...);
+};
+
+#endif

+ 41 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/Printable.h

@@ -0,0 +1,41 @@
+/*
+  Printable.h - Interface class that allows printing of complex types
+  Copyright (c) 2011 Adrian McEwen.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef Printable_h
+#define Printable_h
+
+#include <stdlib.h>
+#include <inttypes.h>
+
+class Print;
+
+/** The Printable class provides a way for new classes to allow themselves to be printed.
+    By deriving from Printable and implementing the printTo method, it will then be possible
+    for users to print out instances of this class by passing them into the usual
+    Print::print and Print::println methods.
+*/
+
+class Printable
+{
+  public:
+    virtual size_t printTo(Print& p) const = 0;
+};
+
+#endif
+

+ 534 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/binary.h

@@ -0,0 +1,534 @@
+/*
+  binary.h - Definitions for binary constants
+  Copyright (c) 2006 David A. Mellis.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef Binary_h
+#define Binary_h
+
+#define B0 0
+#define B00 0
+#define B000 0
+#define B0000 0
+#define B00000 0
+#define B000000 0
+#define B0000000 0
+#define B00000000 0
+#define B1 1
+#define B01 1
+#define B001 1
+#define B0001 1
+#define B00001 1
+#define B000001 1
+#define B0000001 1
+#define B00000001 1
+#define B10 2
+#define B010 2
+#define B0010 2
+#define B00010 2
+#define B000010 2
+#define B0000010 2
+#define B00000010 2
+#define B11 3
+#define B011 3
+#define B0011 3
+#define B00011 3
+#define B000011 3
+#define B0000011 3
+#define B00000011 3
+#define B100 4
+#define B0100 4
+#define B00100 4
+#define B000100 4
+#define B0000100 4
+#define B00000100 4
+#define B101 5
+#define B0101 5
+#define B00101 5
+#define B000101 5
+#define B0000101 5
+#define B00000101 5
+#define B110 6
+#define B0110 6
+#define B00110 6
+#define B000110 6
+#define B0000110 6
+#define B00000110 6
+#define B111 7
+#define B0111 7
+#define B00111 7
+#define B000111 7
+#define B0000111 7
+#define B00000111 7
+#define B1000 8
+#define B01000 8
+#define B001000 8
+#define B0001000 8
+#define B00001000 8
+#define B1001 9
+#define B01001 9
+#define B001001 9
+#define B0001001 9
+#define B00001001 9
+#define B1010 10
+#define B01010 10
+#define B001010 10
+#define B0001010 10
+#define B00001010 10
+#define B1011 11
+#define B01011 11
+#define B001011 11
+#define B0001011 11
+#define B00001011 11
+#define B1100 12
+#define B01100 12
+#define B001100 12
+#define B0001100 12
+#define B00001100 12
+#define B1101 13
+#define B01101 13
+#define B001101 13
+#define B0001101 13
+#define B00001101 13
+#define B1110 14
+#define B01110 14
+#define B001110 14
+#define B0001110 14
+#define B00001110 14
+#define B1111 15
+#define B01111 15
+#define B001111 15
+#define B0001111 15
+#define B00001111 15
+#define B10000 16
+#define B010000 16
+#define B0010000 16
+#define B00010000 16
+#define B10001 17
+#define B010001 17
+#define B0010001 17
+#define B00010001 17
+#define B10010 18
+#define B010010 18
+#define B0010010 18
+#define B00010010 18
+#define B10011 19
+#define B010011 19
+#define B0010011 19
+#define B00010011 19
+#define B10100 20
+#define B010100 20
+#define B0010100 20
+#define B00010100 20
+#define B10101 21
+#define B010101 21
+#define B0010101 21
+#define B00010101 21
+#define B10110 22
+#define B010110 22
+#define B0010110 22
+#define B00010110 22
+#define B10111 23
+#define B010111 23
+#define B0010111 23
+#define B00010111 23
+#define B11000 24
+#define B011000 24
+#define B0011000 24
+#define B00011000 24
+#define B11001 25
+#define B011001 25
+#define B0011001 25
+#define B00011001 25
+#define B11010 26
+#define B011010 26
+#define B0011010 26
+#define B00011010 26
+#define B11011 27
+#define B011011 27
+#define B0011011 27
+#define B00011011 27
+#define B11100 28
+#define B011100 28
+#define B0011100 28
+#define B00011100 28
+#define B11101 29
+#define B011101 29
+#define B0011101 29
+#define B00011101 29
+#define B11110 30
+#define B011110 30
+#define B0011110 30
+#define B00011110 30
+#define B11111 31
+#define B011111 31
+#define B0011111 31
+#define B00011111 31
+#define B100000 32
+#define B0100000 32
+#define B00100000 32
+#define B100001 33
+#define B0100001 33
+#define B00100001 33
+#define B100010 34
+#define B0100010 34
+#define B00100010 34
+#define B100011 35
+#define B0100011 35
+#define B00100011 35
+#define B100100 36
+#define B0100100 36
+#define B00100100 36
+#define B100101 37
+#define B0100101 37
+#define B00100101 37
+#define B100110 38
+#define B0100110 38
+#define B00100110 38
+#define B100111 39
+#define B0100111 39
+#define B00100111 39
+#define B101000 40
+#define B0101000 40
+#define B00101000 40
+#define B101001 41
+#define B0101001 41
+#define B00101001 41
+#define B101010 42
+#define B0101010 42
+#define B00101010 42
+#define B101011 43
+#define B0101011 43
+#define B00101011 43
+#define B101100 44
+#define B0101100 44
+#define B00101100 44
+#define B101101 45
+#define B0101101 45
+#define B00101101 45
+#define B101110 46
+#define B0101110 46
+#define B00101110 46
+#define B101111 47
+#define B0101111 47
+#define B00101111 47
+#define B110000 48
+#define B0110000 48
+#define B00110000 48
+#define B110001 49
+#define B0110001 49
+#define B00110001 49
+#define B110010 50
+#define B0110010 50
+#define B00110010 50
+#define B110011 51
+#define B0110011 51
+#define B00110011 51
+#define B110100 52
+#define B0110100 52
+#define B00110100 52
+#define B110101 53
+#define B0110101 53
+#define B00110101 53
+#define B110110 54
+#define B0110110 54
+#define B00110110 54
+#define B110111 55
+#define B0110111 55
+#define B00110111 55
+#define B111000 56
+#define B0111000 56
+#define B00111000 56
+#define B111001 57
+#define B0111001 57
+#define B00111001 57
+#define B111010 58
+#define B0111010 58
+#define B00111010 58
+#define B111011 59
+#define B0111011 59
+#define B00111011 59
+#define B111100 60
+#define B0111100 60
+#define B00111100 60
+#define B111101 61
+#define B0111101 61
+#define B00111101 61
+#define B111110 62
+#define B0111110 62
+#define B00111110 62
+#define B111111 63
+#define B0111111 63
+#define B00111111 63
+#define B1000000 64
+#define B01000000 64
+#define B1000001 65
+#define B01000001 65
+#define B1000010 66
+#define B01000010 66
+#define B1000011 67
+#define B01000011 67
+#define B1000100 68
+#define B01000100 68
+#define B1000101 69
+#define B01000101 69
+#define B1000110 70
+#define B01000110 70
+#define B1000111 71
+#define B01000111 71
+#define B1001000 72
+#define B01001000 72
+#define B1001001 73
+#define B01001001 73
+#define B1001010 74
+#define B01001010 74
+#define B1001011 75
+#define B01001011 75
+#define B1001100 76
+#define B01001100 76
+#define B1001101 77
+#define B01001101 77
+#define B1001110 78
+#define B01001110 78
+#define B1001111 79
+#define B01001111 79
+#define B1010000 80
+#define B01010000 80
+#define B1010001 81
+#define B01010001 81
+#define B1010010 82
+#define B01010010 82
+#define B1010011 83
+#define B01010011 83
+#define B1010100 84
+#define B01010100 84
+#define B1010101 85
+#define B01010101 85
+#define B1010110 86
+#define B01010110 86
+#define B1010111 87
+#define B01010111 87
+#define B1011000 88
+#define B01011000 88
+#define B1011001 89
+#define B01011001 89
+#define B1011010 90
+#define B01011010 90
+#define B1011011 91
+#define B01011011 91
+#define B1011100 92
+#define B01011100 92
+#define B1011101 93
+#define B01011101 93
+#define B1011110 94
+#define B01011110 94
+#define B1011111 95
+#define B01011111 95
+#define B1100000 96
+#define B01100000 96
+#define B1100001 97
+#define B01100001 97
+#define B1100010 98
+#define B01100010 98
+#define B1100011 99
+#define B01100011 99
+#define B1100100 100
+#define B01100100 100
+#define B1100101 101
+#define B01100101 101
+#define B1100110 102
+#define B01100110 102
+#define B1100111 103
+#define B01100111 103
+#define B1101000 104
+#define B01101000 104
+#define B1101001 105
+#define B01101001 105
+#define B1101010 106
+#define B01101010 106
+#define B1101011 107
+#define B01101011 107
+#define B1101100 108
+#define B01101100 108
+#define B1101101 109
+#define B01101101 109
+#define B1101110 110
+#define B01101110 110
+#define B1101111 111
+#define B01101111 111
+#define B1110000 112
+#define B01110000 112
+#define B1110001 113
+#define B01110001 113
+#define B1110010 114
+#define B01110010 114
+#define B1110011 115
+#define B01110011 115
+#define B1110100 116
+#define B01110100 116
+#define B1110101 117
+#define B01110101 117
+#define B1110110 118
+#define B01110110 118
+#define B1110111 119
+#define B01110111 119
+#define B1111000 120
+#define B01111000 120
+#define B1111001 121
+#define B01111001 121
+#define B1111010 122
+#define B01111010 122
+#define B1111011 123
+#define B01111011 123
+#define B1111100 124
+#define B01111100 124
+#define B1111101 125
+#define B01111101 125
+#define B1111110 126
+#define B01111110 126
+#define B1111111 127
+#define B01111111 127
+#define B10000000 128
+#define B10000001 129
+#define B10000010 130
+#define B10000011 131
+#define B10000100 132
+#define B10000101 133
+#define B10000110 134
+#define B10000111 135
+#define B10001000 136
+#define B10001001 137
+#define B10001010 138
+#define B10001011 139
+#define B10001100 140
+#define B10001101 141
+#define B10001110 142
+#define B10001111 143
+#define B10010000 144
+#define B10010001 145
+#define B10010010 146
+#define B10010011 147
+#define B10010100 148
+#define B10010101 149
+#define B10010110 150
+#define B10010111 151
+#define B10011000 152
+#define B10011001 153
+#define B10011010 154
+#define B10011011 155
+#define B10011100 156
+#define B10011101 157
+#define B10011110 158
+#define B10011111 159
+#define B10100000 160
+#define B10100001 161
+#define B10100010 162
+#define B10100011 163
+#define B10100100 164
+#define B10100101 165
+#define B10100110 166
+#define B10100111 167
+#define B10101000 168
+#define B10101001 169
+#define B10101010 170
+#define B10101011 171
+#define B10101100 172
+#define B10101101 173
+#define B10101110 174
+#define B10101111 175
+#define B10110000 176
+#define B10110001 177
+#define B10110010 178
+#define B10110011 179
+#define B10110100 180
+#define B10110101 181
+#define B10110110 182
+#define B10110111 183
+#define B10111000 184
+#define B10111001 185
+#define B10111010 186
+#define B10111011 187
+#define B10111100 188
+#define B10111101 189
+#define B10111110 190
+#define B10111111 191
+#define B11000000 192
+#define B11000001 193
+#define B11000010 194
+#define B11000011 195
+#define B11000100 196
+#define B11000101 197
+#define B11000110 198
+#define B11000111 199
+#define B11001000 200
+#define B11001001 201
+#define B11001010 202
+#define B11001011 203
+#define B11001100 204
+#define B11001101 205
+#define B11001110 206
+#define B11001111 207
+#define B11010000 208
+#define B11010001 209
+#define B11010010 210
+#define B11010011 211
+#define B11010100 212
+#define B11010101 213
+#define B11010110 214
+#define B11010111 215
+#define B11011000 216
+#define B11011001 217
+#define B11011010 218
+#define B11011011 219
+#define B11011100 220
+#define B11011101 221
+#define B11011110 222
+#define B11011111 223
+#define B11100000 224
+#define B11100001 225
+#define B11100010 226
+#define B11100011 227
+#define B11100100 228
+#define B11100101 229
+#define B11100110 230
+#define B11100111 231
+#define B11101000 232
+#define B11101001 233
+#define B11101010 234
+#define B11101011 235
+#define B11101100 236
+#define B11101101 237
+#define B11101110 238
+#define B11101111 239
+#define B11110000 240
+#define B11110001 241
+#define B11110010 242
+#define B11110011 243
+#define B11110100 244
+#define B11110101 245
+#define B11110110 246
+#define B11110111 247
+#define B11111000 248
+#define B11111001 249
+#define B11111010 250
+#define B11111011 251
+#define B11111100 252
+#define B11111101 253
+#define B11111110 254
+#define B11111111 255
+
+#endif

+ 1143 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/chanfs/LPC176x.h

@@ -0,0 +1,1143 @@
+/*-------------------------------------------------------------------*/
+/* LPC176x Register Definitions and Cortex-M3 Supplement Definitions */
+/*       This file is a non-copyrighted public domain software       */
+/*-------------------------------------------------------------------*/
+
+#ifndef __LPC176x
+#define __LPC176x
+#include <stdint.h>
+
+#define	USE_SV_SERVICE	0	/* Enable supervisor service for user mode task */
+
+
+/* System Controls */
+#define	EXTINT		(*(volatile uint32_t*)0x400FC140)
+#define	EXTMODE		(*(volatile uint32_t*)0x400FC148)
+#define	EXTPOLAR	(*(volatile uint32_t*)0x400FC14C)
+#define	RSID		(*(volatile uint32_t*)0x400FC180)
+#define	SCS			(*(volatile uint32_t*)0x400FC1A0)
+
+/* Clocking and Power Controls */
+#define	CLKSRCSEL	(*(volatile uint32_t*)0x400FC10C)
+#define	PLL0CON		(*(volatile uint32_t*)0x400FC080)
+#define	PLL0CFG		(*(volatile uint32_t*)0x400FC084)
+#define	PLL0STAT	(*(volatile uint32_t*)0x400FC088)
+#define	PLL0FEED	(*(volatile uint32_t*)0x400FC08C)
+#define	PLL1CON		(*(volatile uint32_t*)0x400FC0A0)
+#define	PLL1CFG		(*(volatile uint32_t*)0x400FC0A4)
+#define	PLL1STAT	(*(volatile uint32_t*)0x400FC0A8)
+#define	PLL1FEED	(*(volatile uint32_t*)0x400FC0AC)
+#define	CCLKCFG		(*(volatile uint32_t*)0x400FC104)
+#define	USBCLKCFG	(*(volatile uint32_t*)0x400FC108)
+#define	PCLKSEL		( (volatile uint32_t*)0x400FC1A8)
+#define	PCLKSEL0	(*(volatile uint32_t*)0x400FC1A8)
+#define	PCLKSEL1	(*(volatile uint32_t*)0x400FC1AC)
+#define	PCON		(*(volatile uint32_t*)0x400FC0C0)
+#define	PCONP		(*(volatile uint32_t*)0x400FC0C4)
+#define	CLKOUTCFG	(*(volatile uint32_t*)0x400FC1C8)
+
+/* Flash Accelerator */
+#define	FLASHCFG	(*(volatile uint32_t*)0x400FC000)
+
+/* Pin Configurations */
+#define	PINSEL		( (volatile uint32_t*)0x4002C000)
+#define	PINSEL0		(*(volatile uint32_t*)0x4002C000)
+#define	PINSEL1		(*(volatile uint32_t*)0x4002C004)
+#define	PINSEL2		(*(volatile uint32_t*)0x4002C008)
+#define	PINSEL3		(*(volatile uint32_t*)0x4002C00C)
+#define	PINSEL4		(*(volatile uint32_t*)0x4002C010)
+#define	PINSEL7		(*(volatile uint32_t*)0x4002C01C)
+#define	PINSEL8		(*(volatile uint32_t*)0x4002C020)
+#define	PINSEL9		(*(volatile uint32_t*)0x4002C024)
+#define	PINSEL10	(*(volatile uint32_t*)0x4002C028)
+#define	PINMODE		( (volatile uint32_t*)0x4002C040)
+#define	PINMODE0	(*(volatile uint32_t*)0x4002C040)
+#define	PINMODE1	(*(volatile uint32_t*)0x4002C044)
+#define	PINMODE2	(*(volatile uint32_t*)0x4002C048)
+#define	PINMODE3	(*(volatile uint32_t*)0x4002C04C)
+#define	PINMODE4	(*(volatile uint32_t*)0x4002C050)
+#define	PINMODE5	(*(volatile uint32_t*)0x4002C054)
+#define	PINMODE6	(*(volatile uint32_t*)0x4002C058)
+#define	PINMODE7	(*(volatile uint32_t*)0x4002C05C)
+#define	PINMODE9	(*(volatile uint32_t*)0x4002C064)
+#define	PINMODE_OD	( (volatile uint32_t*)0x4002C068)
+#define	PINMODE_OD0	(*(volatile uint32_t*)0x4002C068)
+#define	PINMODE_OD1	(*(volatile uint32_t*)0x4002C06C)
+#define	PINMODE_OD2	(*(volatile uint32_t*)0x4002C070)
+#define	PINMODE_OD3	(*(volatile uint32_t*)0x4002C074)
+#define	PINMODE_OD4	(*(volatile uint32_t*)0x4002C078)
+#define	I2CPADCFG	(*(volatile uint32_t*)0x4002C07C)
+
+/* GPIO */
+#define	FIO0DIR		(*(volatile uint32_t*)0x2009C000)
+#define	FIO0DIRL	(*(volatile uint16_t*)0x2009C000)
+#define	FIO0DIRH	(*(volatile uint16_t*)0x2009C002)
+#define	FIO0DIR0	(*(volatile uint8_t*)0x2009C000)
+#define	FIO0DIR1	(*(volatile uint8_t*)0x2009C001)
+#define	FIO0DIR2	(*(volatile uint8_t*)0x2009C002)
+#define	FIO0DIR3	(*(volatile uint8_t*)0x2009C003)
+
+#define	FIO1DIR		(*(volatile uint32_t*)0x2009C020)
+#define	FIO1DIRL	(*(volatile uint16_t*)0x2009C020)
+#define	FIO1DIRH	(*(volatile uint16_t*)0x2009C022)
+#define	FIO1DIR0	(*(volatile uint8_t*)0x2009C020)
+#define	FIO1DIR1	(*(volatile uint8_t*)0x2009C021)
+#define	FIO1DIR2	(*(volatile uint8_t*)0x2009C022)
+#define	FIO1DIR3	(*(volatile uint8_t*)0x2009C023)
+
+#define	FIO2DIR		(*(volatile uint32_t*)0x2009C040)
+#define	FIO2DIRL	(*(volatile uint16_t*)0x2009C040)
+#define	FIO2DIRH	(*(volatile uint16_t*)0x2009C042)
+#define	FIO2DIR0	(*(volatile uint8_t*)0x2009C040)
+#define	FIO2DIR1	(*(volatile uint8_t*)0x2009C041)
+#define	FIO2DIR2	(*(volatile uint8_t*)0x2009C042)
+#define	FIO2DIR3	(*(volatile uint8_t*)0x2009C043)
+
+#define	FIO3DIR		(*(volatile uint32_t*)0x2009C060)
+#define	FIO3DIRL	(*(volatile uint16_t*)0x2009C060)
+#define	FIO3DIRH	(*(volatile uint16_t*)0x2009C062)
+#define	FIO3DIR0	(*(volatile uint8_t*)0x2009C060)
+#define	FIO3DIR1	(*(volatile uint8_t*)0x2009C061)
+#define	FIO3DIR2	(*(volatile uint8_t*)0x2009C062)
+#define	FIO3DIR3	(*(volatile uint8_t*)0x2009C063)
+
+#define	FIO4DIR		(*(volatile uint32_t*)0x2009C080)
+#define	FIO4DIRL	(*(volatile uint16_t*)0x2009C080)
+#define	FIO4DIRH	(*(volatile uint16_t*)0x2009C082)
+#define	FIO4DIR0	(*(volatile uint8_t*)0x2009C080)
+#define	FIO4DIR1	(*(volatile uint8_t*)0x2009C081)
+#define	FIO4DIR2	(*(volatile uint8_t*)0x2009C082)
+#define	FIO4DIR3	(*(volatile uint8_t*)0x2009C083)
+
+#define	FIO0MASK	(*(volatile uint32_t*)0x2009C010)
+#define	FIO0MASKL	(*(volatile uint16_t*)0x2009C010)
+#define	FIO0MASKH	(*(volatile uint16_t*)0x2009C012)
+#define	FIO0MASK0	(*(volatile uint8_t*)0x2009C010)
+#define	FIO0MASK1	(*(volatile uint8_t*)0x2009C011)
+#define	FIO0MASK2	(*(volatile uint8_t*)0x2009C012)
+#define	FIO0MASK3	(*(volatile uint8_t*)0x2009C013)
+
+#define	FIO1MASK	(*(volatile uint32_t*)0x2009C030)
+#define	FIO1MASKL	(*(volatile uint16_t*)0x2009C030)
+#define	FIO1MASKH	(*(volatile uint16_t*)0x2009C032)
+#define	FIO1MASK0	(*(volatile uint8_t*)0x2009C030)
+#define	FIO1MASK1	(*(volatile uint8_t*)0x2009C031)
+#define	FIO1MASK2	(*(volatile uint8_t*)0x2009C032)
+#define	FIO1MASK3	(*(volatile uint8_t*)0x2009C033)
+
+#define	FIO2MASK	(*(volatile uint32_t*)0x2009C050)
+#define	FIO2MASKL	(*(volatile uint16_t*)0x2009C050)
+#define	FIO2MASKH	(*(volatile uint16_t*)0x2009C052)
+#define	FIO2MASK0	(*(volatile uint8_t*)0x2009C050)
+#define	FIO2MASK1	(*(volatile uint8_t*)0x2009C051)
+#define	FIO2MASK2	(*(volatile uint8_t*)0x2009C052)
+#define	FIO2MASK3	(*(volatile uint8_t*)0x2009C053)
+
+#define	FIO3MASK	(*(volatile uint32_t*)0x2009C070)
+#define	FIO3MASKL	(*(volatile uint16_t*)0x2009C070)
+#define	FIO3MASKH	(*(volatile uint16_t*)0x2009C072)
+#define	FIO3MASK0	(*(volatile uint8_t*)0x2009C070)
+#define	FIO3MASK1	(*(volatile uint8_t*)0x2009C071)
+#define	FIO3MASK2	(*(volatile uint8_t*)0x2009C072)
+#define	FIO3MASK3	(*(volatile uint8_t*)0x2009C073)
+
+#define	FIO4MASK	(*(volatile uint32_t*)0x2009C090)
+#define	FIO4MASKL	(*(volatile uint16_t*)0x2009C090)
+#define	FIO4MASKH	(*(volatile uint16_t*)0x2009C092)
+#define	FIO4MASK0	(*(volatile uint8_t*)0x2009C090)
+#define	FIO4MASK1	(*(volatile uint8_t*)0x2009C091)
+#define	FIO4MASK2	(*(volatile uint8_t*)0x2009C092)
+#define	FIO4MASK3	(*(volatile uint8_t*)0x2009C093)
+
+#define	FIO0PIN		(*(volatile uint32_t*)0x2009C014)
+#define	FIO0PINL	(*(volatile uint16_t*)0x2009C014)
+#define	FIO0PINH	(*(volatile uint16_t*)0x2009C016)
+#define	FIO0PIN0	(*(volatile uint8_t*)0x2009C014)
+#define	FIO0PIN1	(*(volatile uint8_t*)0x2009C015)
+#define	FIO0PIN2	(*(volatile uint8_t*)0x2009C016)
+#define	FIO0PIN3	(*(volatile uint8_t*)0x2009C017)
+
+#define	FIO1PIN		(*(volatile uint32_t*)0x2009C034)
+#define	FIO1PINL	(*(volatile uint16_t*)0x2009C034)
+#define	FIO1PINH	(*(volatile uint16_t*)0x2009C036)
+#define	FIO1PIN0	(*(volatile uint8_t*)0x2009C034)
+#define	FIO1PIN1	(*(volatile uint8_t*)0x2009C035)
+#define	FIO1PIN2	(*(volatile uint8_t*)0x2009C036)
+#define	FIO1PIN3	(*(volatile uint8_t*)0x2009C037)
+
+#define	FIO2PIN		(*(volatile uint32_t*)0x2009C054)
+#define	FIO2PINL	(*(volatile uint16_t*)0x2009C054)
+#define	FIO2PINH	(*(volatile uint16_t*)0x2009C056)
+#define	FIO2PIN0	(*(volatile uint8_t*)0x2009C054)
+#define	FIO2PIN1	(*(volatile uint8_t*)0x2009C055)
+#define	FIO2PIN2	(*(volatile uint8_t*)0x2009C056)
+#define	FIO2PIN3	(*(volatile uint8_t*)0x2009C057)
+
+#define	FIO3PIN		(*(volatile uint32_t*)0x2009C074)
+#define	FIO3PINL	(*(volatile uint16_t*)0x2009C074)
+#define	FIO3PINH	(*(volatile uint16_t*)0x2009C076)
+#define	FIO3PIN0	(*(volatile uint8_t*)0x2009C074)
+#define	FIO3PIN1	(*(volatile uint8_t*)0x2009C075)
+#define	FIO3PIN2	(*(volatile uint8_t*)0x2009C076)
+#define	FIO3PIN3	(*(volatile uint8_t*)0x2009C077)
+
+#define	FIO4PIN		(*(volatile uint32_t*)0x2009C094)
+#define	FIO4PINL	(*(volatile uint16_t*)0x2009C094)
+#define	FIO4PINH	(*(volatile uint16_t*)0x2009C096)
+#define	FIO4PIN0	(*(volatile uint8_t*)0x2009C094)
+#define	FIO4PIN1	(*(volatile uint8_t*)0x2009C095)
+#define	FIO4PIN2	(*(volatile uint8_t*)0x2009C096)
+#define	FIO4PIN3	(*(volatile uint8_t*)0x2009C097)
+
+#define	FIO0SET		(*(volatile uint32_t*)0x2009C018)
+#define	FIO0SETL	(*(volatile uint16_t*)0x2009C018)
+#define	FIO0SETH	(*(volatile uint16_t*)0x2009C01A)
+#define	FIO0SET0	(*(volatile uint8_t*)0x2009C018)
+#define	FIO0SET1	(*(volatile uint8_t*)0x2009C019)
+#define	FIO0SET2	(*(volatile uint8_t*)0x2009C01A)
+#define	FIO0SET3	(*(volatile uint8_t*)0x2009C01B)
+
+#define	FIO1SET		(*(volatile uint32_t*)0x2009C038)
+#define	FIO1SETL	(*(volatile uint16_t*)0x2009C038)
+#define	FIO1SETH	(*(volatile uint16_t*)0x2009C03A)
+#define	FIO1SET0	(*(volatile uint8_t*)0x2009C038)
+#define	FIO1SET1	(*(volatile uint8_t*)0x2009C039)
+#define	FIO1SET2	(*(volatile uint8_t*)0x2009C03A)
+#define	FIO1SET3	(*(volatile uint8_t*)0x2009C03B)
+
+#define	FIO2SET		(*(volatile uint32_t*)0x2009C058)
+#define	FIO2SETL	(*(volatile uint16_t*)0x2009C058)
+#define	FIO2SETH	(*(volatile uint16_t*)0x2009C05A)
+#define	FIO2SET0	(*(volatile uint8_t*)0x2009C058)
+#define	FIO2SET1	(*(volatile uint8_t*)0x2009C059)
+#define	FIO2SET2	(*(volatile uint8_t*)0x2009C05A)
+#define	FIO2SET3	(*(volatile uint8_t*)0x2009C05B)
+
+#define	FIO3SET		(*(volatile uint32_t*)0x2009C078)
+#define	FIO3SETL	(*(volatile uint16_t*)0x2009C078)
+#define	FIO3SETH	(*(volatile uint16_t*)0x2009C07A)
+#define	FIO3SET0	(*(volatile uint8_t*)0x2009C078)
+#define	FIO3SET1	(*(volatile uint8_t*)0x2009C079)
+#define	FIO3SET2	(*(volatile uint8_t*)0x2009C07A)
+#define	FIO3SET3	(*(volatile uint8_t*)0x2009C07B)
+
+#define	FIO4SET		(*(volatile uint32_t*)0x2009C098)
+#define	FIO4SETL	(*(volatile uint16_t*)0x2009C098)
+#define	FIO4SETH	(*(volatile uint16_t*)0x2009C09A)
+#define	FIO4SET0	(*(volatile uint8_t*)0x2009C098)
+#define	FIO4SET1	(*(volatile uint8_t*)0x2009C099)
+#define	FIO4SET2	(*(volatile uint8_t*)0x2009C09A)
+#define	FIO4SET3	(*(volatile uint8_t*)0x2009C09B)
+
+#define	FIO0CLR		(*(volatile uint32_t*)0x2009C01C)
+#define	FIO0CLRL	(*(volatile uint16_t*)0x2009C01C)
+#define	FIO0CLRH	(*(volatile uint16_t*)0x2009C01E)
+#define	FIO0CLR0	(*(volatile uint8_t*)0x2009C01C)
+#define	FIO0CLR1	(*(volatile uint8_t*)0x2009C01D)
+#define	FIO0CLR2	(*(volatile uint8_t*)0x2009C01E)
+#define	FIO0CLR3	(*(volatile uint8_t*)0x2009C01F)
+
+#define	FIO1CLR		(*(volatile uint32_t*)0x2009C03C)
+#define	FIO1CLRL	(*(volatile uint16_t*)0x2009C03C)
+#define	FIO1CLRH	(*(volatile uint16_t*)0x2009C03E)
+#define	FIO1CLR0	(*(volatile uint8_t*)0x2009C03C)
+#define	FIO1CLR1	(*(volatile uint8_t*)0x2009C03D)
+#define	FIO1CLR2	(*(volatile uint8_t*)0x2009C03E)
+#define	FIO1CLR3	(*(volatile uint8_t*)0x2009C03F)
+
+#define	FIO2CLR		(*(volatile uint32_t*)0x2009C05C)
+#define	FIO2CLRL	(*(volatile uint16_t*)0x2009C05C)
+#define	FIO2CLRH	(*(volatile uint16_t*)0x2009C05E)
+#define	FIO2CLR0	(*(volatile uint8_t*)0x2009C05C)
+#define	FIO2CLR1	(*(volatile uint8_t*)0x2009C05D)
+#define	FIO2CLR2	(*(volatile uint8_t*)0x2009C05E)
+#define	FIO2CLR3	(*(volatile uint8_t*)0x2009C05F)
+
+#define	FIO3CLR		(*(volatile uint32_t*)0x2009C07C)
+#define	FIO3CLRL	(*(volatile uint16_t*)0x2009C07C)
+#define	FIO3CLRH	(*(volatile uint16_t*)0x2009C07E)
+#define	FIO3CLR0	(*(volatile uint8_t*)0x2009C07C)
+#define	FIO3CLR1	(*(volatile uint8_t*)0x2009C07D)
+#define	FIO3CLR2	(*(volatile uint8_t*)0x2009C07E)
+#define	FIO3CLR3	(*(volatile uint8_t*)0x2009C07F)
+
+#define	FIO4CLR		(*(volatile uint32_t*)0x2009C09C)
+#define	FIO4CLRL	(*(volatile uint16_t*)0x2009C09C)
+#define	FIO4CLRH	(*(volatile uint16_t*)0x2009C09E)
+#define	FIO4CLR0	(*(volatile uint8_t*)0x2009C09C)
+#define	FIO4CLR1	(*(volatile uint8_t*)0x2009C09D)
+#define	FIO4CLR2	(*(volatile uint8_t*)0x2009C09E)
+#define	FIO4CLR3	(*(volatile uint8_t*)0x2009C09F)
+
+#define	IOIntStatus	(*(volatile uint32_t*)0x40028080)
+#define	IO0IntStatR	(*(volatile uint32_t*)0x40028084)
+#define	IO0IntStatF	(*(volatile uint32_t*)0x40028088)
+#define	IO0IntClr	(*(volatile uint32_t*)0x4002808C)
+#define	IO0IntEnR	(*(volatile uint32_t*)0x40028090)
+#define	IO0IntEnF	(*(volatile uint32_t*)0x40028094)
+#define	IO2IntStatR	(*(volatile uint32_t*)0x400280A4)
+#define	IO2IntStatF	(*(volatile uint32_t*)0x400280A8)
+#define	IO2IntClr	(*(volatile uint32_t*)0x400280AC)
+#define	IO2IntEnR	(*(volatile uint32_t*)0x400280B0)
+#define	IO2IntEnF	(*(volatile uint32_t*)0x400280B4)
+
+/* Ethernet MAC */
+#define	MAC1			(*(volatile uint32_t*)0x50000000)
+#define	MAC2			(*(volatile uint32_t*)0x50000004)
+#define	IPGT			(*(volatile uint32_t*)0x50000008)
+#define	IPGR			(*(volatile uint32_t*)0x5000000C)
+#define	CLRT			(*(volatile uint32_t*)0x50000010)
+#define	MAXF			(*(volatile uint32_t*)0x50000014)
+#define	SUPP			(*(volatile uint32_t*)0x50000018)
+#define	TEST			(*(volatile uint32_t*)0x5000001C)
+#define	MCFG			(*(volatile uint32_t*)0x50000020)
+#define	MCMD			(*(volatile uint32_t*)0x50000024)
+#define	MADR			(*(volatile uint32_t*)0x50000028)
+#define	MWTD			(*(volatile uint32_t*)0x5000002C)
+#define	MRDD			(*(volatile uint32_t*)0x50000030)
+#define	MIND			(*(volatile uint32_t*)0x50000034)
+#define	SA0				(*(volatile uint32_t*)0x50000040)
+#define	SA1				(*(volatile uint32_t*)0x50000044)
+#define	SA2				(*(volatile uint32_t*)0x50000048)
+#define	Command			(*(volatile uint32_t*)0x50000100)
+#define	Status			(*(volatile uint32_t*)0x50000104)
+#define	RxDescriptor	(*(volatile uint32_t*)0x50000108)
+#define	RxStatus		(*(volatile uint32_t*)0x5000010C)
+#define	RxDescriptorNumber	(*(volatile uint32_t*)0x50000110)
+#define	RxProduceIndex	(*(volatile uint32_t*)0x50000114)
+#define	RxConsumeIndex	(*(volatile uint32_t*)0x50000118)
+#define	TxDescriptor	(*(volatile uint32_t*)0x5000011C)
+#define	TxStatus		(*(volatile uint32_t*)0x50000120)
+#define	TxDescriptorNumber	(*(volatile uint32_t*)0x50000124)
+#define	TxProduceIndex	(*(volatile uint32_t*)0x50000128)
+#define	TxConsumeIndex	(*(volatile uint32_t*)0x5000012C)
+#define	TSV0			(*(volatile uint32_t*)0x50000158)
+#define	TSV1			(*(volatile uint32_t*)0x5000015C)
+#define	RSV				(*(volatile uint32_t*)0x50000160)
+#define	FlowControlCounter	(*(volatile uint32_t*)0x50000170)
+#define	FlowControlStatus	(*(volatile uint32_t*)0x50000174)
+#define	RxFliterCtrl		(*(volatile uint32_t*)0x50000200)
+#define	RxFilterWoLStatus	(*(volatile uint32_t*)0x50000204)
+#define	RxFilterWoLClear	(*(volatile uint32_t*)0x50000208)
+#define	HashFilterL		(*(volatile uint32_t*)0x50000210)
+#define	HashFilterH		(*(volatile uint32_t*)0x50000214)
+#define	IntStatus		(*(volatile uint32_t*)0x50000FE0)
+#define	IntEnable		(*(volatile uint32_t*)0x50000FE4)
+#define	IntClear		(*(volatile uint32_t*)0x50000FE8)
+#define	IntSet			(*(volatile uint32_t*)0x50000FEC)
+#define	PowerDown		(*(volatile uint32_t*)0x50000FF4)
+
+/* USB Device */
+#define	USBClkCtrl		(*(volatile uint32_t*)0x5000CFF4)
+#define	USBClkSt		(*(volatile uint32_t*)0x5000CFF8)
+#define	USBIntSt		(*(volatile uint32_t*)0x400FC1C0)
+#define	USBDevIntSt		(*(volatile uint32_t*)0x5000C200)
+#define	USBDevIntEn		(*(volatile uint32_t*)0x5000C204)
+#define	USBDevIntClr	(*(volatile uint32_t*)0x5000C208)
+#define	USBDevIntSet	(*(volatile uint32_t*)0x5000C20C)
+#define	USBDevIntPri	(*(volatile uint32_t*)0x5000C22C)
+#define	USBEpIntSt		(*(volatile uint32_t*)0x5000C230)
+#define	USBEpIntEn		(*(volatile uint32_t*)0x5000C234)
+#define	USBEpIntClr		(*(volatile uint32_t*)0x5000C238)
+#define	USBEpIntSet		(*(volatile uint32_t*)0x5000C23C)
+#define	USBEpIntPri		(*(volatile uint32_t*)0x5000C240)
+#define	USBReEp			(*(volatile uint32_t*)0x5000C244)
+#define	USBEpIn			(*(volatile uint32_t*)0x5000C248)
+#define	USBMaxPSize		(*(volatile uint32_t*)0x5000C24C)
+#define	USBRxData		(*(volatile uint32_t*)0x5000C218)
+#define	USBRxPLen		(*(volatile uint32_t*)0x5000C220)
+#define	USBTxData		(*(volatile uint32_t*)0x5000C21C)
+#define	USBTxPLen		(*(volatile uint32_t*)0x5000C224)
+#define	USBCtrl			(*(volatile uint32_t*)0x5000C228)
+#define	USBCmdCode		(*(volatile uint32_t*)0x5000C210)
+#define	USBCmdData		(*(volatile uint32_t*)0x5000C214)
+#define	USBDMARSt		(*(volatile uint32_t*)0x5000C250)
+#define	USBDMARClr		(*(volatile uint32_t*)0x5000C254)
+#define	USBDMARSet		(*(volatile uint32_t*)0x5000C258)
+#define	USBUDCAH		(*(volatile uint32_t*)0x5000C280)
+#define	USBEpDMASt		(*(volatile uint32_t*)0x5000C284)
+#define	USBEpDMAEn		(*(volatile uint32_t*)0x5000C288)
+#define	USBEpDMADis		(*(volatile uint32_t*)0x5000C28C)
+#define	USBDMAIntSt		(*(volatile uint32_t*)0x5000C290)
+#define	USBDMAIntEn		(*(volatile uint32_t*)0x5000C294)
+#define	USBEoTIntSt		(*(volatile uint32_t*)0x5000C2A0)
+#define	USBEoTIntClr	(*(volatile uint32_t*)0x5000C2A4)
+#define	USBEoTIntSet	(*(volatile uint32_t*)0x5000C2A8)
+#define	USBNDDRIntSt	(*(volatile uint32_t*)0x5000C2AC)
+#define	USBNDDRIntClr	(*(volatile uint32_t*)0x5000C2B0)
+#define	USBNDDRIntSet	(*(volatile uint32_t*)0x5000C2B4)
+#define	USBSysErrIntSt	(*(volatile uint32_t*)0x5000C2B8)
+#define	USBSysErrIntClr	(*(volatile uint32_t*)0x5000C2BC)
+#define	USBSysErrIntSet	(*(volatile uint32_t*)0x5000C2C0)
+
+/* USB OTG */
+#define	USBIntSt	(*(volatile uint32_t*)0x400FC1C0)
+#define	OTGIntSt	(*(volatile uint32_t*)0x5000C100)
+#define	OTGIntEn	(*(volatile uint32_t*)0x5000C104)
+#define	OTGIntSet	(*(volatile uint32_t*)0x5000C108)
+#define	OTGIntClr	(*(volatile uint32_t*)0x5000C10C)
+#define	OTGStCtrl	(*(volatile uint32_t*)0x5000C110)
+#define	OTGTmr		(*(volatile uint32_t*)0x5000C114)
+#define	I2C_RX		(*(volatile uint32_t*)0x5000C300)
+#define	I2C_TX		(*(volatile uint32_t*)0x5000C300)
+#define	I2C_STS		(*(volatile uint32_t*)0x5000C304)
+#define	I2C_CTL		(*(volatile uint32_t*)0x5000C308)
+#define	I2C_CLKHI	(*(volatile uint32_t*)0x5000C30C)
+#define	I2C_CLKLO	(*(volatile uint32_t*)0x5000C310)
+#define	OTGClkCtrl	(*(volatile uint32_t*)0x5000CFF4)
+#define	OTGClkSt	(*(volatile uint32_t*)0x5000CFF8)
+
+/* UART0,UART1,UART2,UART3 */
+#define	U0RBR		(*(volatile uint32_t*)0x4000C000)
+#define	U0THR		(*(volatile uint32_t*)0x4000C000)
+#define	U0DLL		(*(volatile uint32_t*)0x4000C000)
+#define	U0DLM		(*(volatile uint32_t*)0x4000C004)
+#define	U0IER		(*(volatile uint32_t*)0x4000C004)
+#define	U0IIR		(*(volatile uint32_t*)0x4000C008)
+#define	U0FCR		(*(volatile uint32_t*)0x4000C008)
+#define	U0LCR		(*(volatile uint32_t*)0x4000C00C)
+#define	U0LSR		(*(volatile uint32_t*)0x4000C014)
+#define	U0SCR		(*(volatile uint32_t*)0x4000C01C)
+#define	U0ACR		(*(volatile uint32_t*)0x4000C020)
+#define	U0ICR		(*(volatile uint32_t*)0x4000C024)
+#define	U0FDR		(*(volatile uint32_t*)0x4000C028)
+#define	U0TER		(*(volatile uint32_t*)0x4000C030)
+
+#define	U1RBR		(*(volatile uint32_t*)0x40010000)
+#define	U1THR		(*(volatile uint32_t*)0x40010000)
+#define	U1DLL		(*(volatile uint32_t*)0x40010000)
+#define	U1DLM		(*(volatile uint32_t*)0x40010004)
+#define	U1IER		(*(volatile uint32_t*)0x40010004)
+#define	U1IIR		(*(volatile uint32_t*)0x40010008)
+#define	U1FCR		(*(volatile uint32_t*)0x40010008)
+#define	U1LCR		(*(volatile uint32_t*)0x4001000C)
+#define	U1MCR		(*(volatile uint32_t*)0x40010010)
+#define	U1LSR		(*(volatile uint32_t*)0x40010014)
+#define	U1MSR		(*(volatile uint32_t*)0x40010018)
+#define	U1SCR		(*(volatile uint32_t*)0x4001001C)
+#define	U1ACR		(*(volatile uint32_t*)0x40010020)
+#define	U1FDR		(*(volatile uint32_t*)0x40010028)
+#define	U1TER		(*(volatile uint32_t*)0x40010030)
+#define	U1RS485CTRL	(*(volatile uint32_t*)0x4001004C)
+#define	U1ADRMATCH	(*(volatile uint32_t*)0x40010050)
+#define	U1RS485DLY	(*(volatile uint32_t*)0x40010054)
+
+#define	U2RBR		(*(volatile uint32_t*)0x40098000)
+#define	U2THR		(*(volatile uint32_t*)0x40098000)
+#define	U2DLL		(*(volatile uint32_t*)0x40098000)
+#define	U2DLM		(*(volatile uint32_t*)0x40098004)
+#define	U2IER		(*(volatile uint32_t*)0x40098004)
+#define	U2IIR		(*(volatile uint32_t*)0x40098008)
+#define	U2FCR		(*(volatile uint32_t*)0x40098008)
+#define	U2LCR		(*(volatile uint32_t*)0x4009800C)
+#define	U2LSR		(*(volatile uint32_t*)0x40098014)
+#define	U2SCR		(*(volatile uint32_t*)0x4009801C)
+#define	U2ACR		(*(volatile uint32_t*)0x40098020)
+#define	U2ICR		(*(volatile uint32_t*)0x40098024)
+#define	U2FDR		(*(volatile uint32_t*)0x40098028)
+#define	U2TER		(*(volatile uint32_t*)0x40098030)
+
+#define	U3RBR		(*(volatile uint32_t*)0x4009C000)
+#define	U3THR		(*(volatile uint32_t*)0x4009C000)
+#define	U3DLL		(*(volatile uint32_t*)0x4009C000)
+#define	U3DLM		(*(volatile uint32_t*)0x4009C004)
+#define	U3IER		(*(volatile uint32_t*)0x4009C004)
+#define	U3IIR		(*(volatile uint32_t*)0x4009C008)
+#define	U3FCR		(*(volatile uint32_t*)0x4009C008)
+#define	U3LCR		(*(volatile uint32_t*)0x4009C00C)
+#define	U3LSR		(*(volatile uint32_t*)0x4009C014)
+#define	U3SCR		(*(volatile uint32_t*)0x4009C01C)
+#define	U3ACR		(*(volatile uint32_t*)0x4009C020)
+#define	U3ICR		(*(volatile uint32_t*)0x4009C024)
+#define	U3FDR		(*(volatile uint32_t*)0x4009C028)
+#define	U3TER		(*(volatile uint32_t*)0x4009C030)
+
+/* CAN1,CAN2 */
+#define	AFMR		(*(volatile uint32_t*)0x4003C000)
+#define	SFF_sa		(*(volatile uint32_t*)0x4003C004)
+#define	SFF_GRP_sa	(*(volatile uint32_t*)0x4003C008)
+#define	EFF_sa		(*(volatile uint32_t*)0x4003C00C)
+#define	EFF_GRP_sa	(*(volatile uint32_t*)0x4003C010)
+#define	ENDofTable	(*(volatile uint32_t*)0x4003C014)
+#define	LUTerrAd	(*(volatile uint32_t*)0x4003C018)
+#define	LUTerr		(*(volatile uint32_t*)0x4003C01C)
+
+#define	CANTxSR		(*(volatile uint32_t*)0x40040000)
+#define	CANRxSR		(*(volatile uint32_t*)0x40040004)
+#define	CANMSR		(*(volatile uint32_t*)0x40040008)
+#define	CAN1MOD		(*(volatile uint32_t*)0x40044000)
+#define	CAN1CMR		(*(volatile uint32_t*)0x40044004)
+#define	CAN1GSR		(*(volatile uint32_t*)0x40044008)
+#define	CAN1ICR		(*(volatile uint32_t*)0x4004400C)
+#define	CAN1IER		(*(volatile uint32_t*)0x40044010)
+#define	CAN1BTR		(*(volatile uint32_t*)0x40044014)
+#define	CAN1EWL		(*(volatile uint32_t*)0x40044018)
+#define	CAN1SR		(*(volatile uint32_t*)0x4004401C)
+#define	CAN1RFS		(*(volatile uint32_t*)0x40044020)
+#define	CAN1RID		(*(volatile uint32_t*)0x40044024)
+#define	CAN1RDA		(*(volatile uint32_t*)0x40044028)
+#define	CAN1RDB		(*(volatile uint32_t*)0x4004402C)
+#define	CAN1TFI1	(*(volatile uint32_t*)0x40044030)
+#define	CAN1TID1	(*(volatile uint32_t*)0x40044034)
+#define	CAN1TDA1	(*(volatile uint32_t*)0x40044038)
+#define	CAN1TDB1	(*(volatile uint32_t*)0x4004403C)
+#define	CAN1TFI2	(*(volatile uint32_t*)0x40044040)
+#define	CAN1TID2	(*(volatile uint32_t*)0x40044044)
+#define	CAN1TDA2	(*(volatile uint32_t*)0x40044048)
+#define	CAN1TDB2	(*(volatile uint32_t*)0x4004404C)
+#define	CAN1TFI3	(*(volatile uint32_t*)0x40044050)
+#define	CAN1TID3	(*(volatile uint32_t*)0x40044054)
+#define	CAN1TDA3	(*(volatile uint32_t*)0x40044058)
+#define	CAN1TDB3	(*(volatile uint32_t*)0x4004405C)
+
+#define	CAN2MOD		(*(volatile uint32_t*)0x40048000)
+#define	CAN2CMR		(*(volatile uint32_t*)0x40048004)
+#define	CAN2GSR		(*(volatile uint32_t*)0x40048008)
+#define	CAN2ICR		(*(volatile uint32_t*)0x4004800C)
+#define	CAN2IER		(*(volatile uint32_t*)0x40048010)
+#define	CAN2BTR		(*(volatile uint32_t*)0x40048014)
+#define	CAN2EWL		(*(volatile uint32_t*)0x40048018)
+#define	CAN2SR		(*(volatile uint32_t*)0x4004801C)
+#define	CAN2RFS		(*(volatile uint32_t*)0x40048020)
+#define	CAN2RID		(*(volatile uint32_t*)0x40048024)
+#define	CAN2RDA		(*(volatile uint32_t*)0x40048028)
+#define	CAN2RDB		(*(volatile uint32_t*)0x4004802C)
+#define	CAN2TFI1	(*(volatile uint32_t*)0x40048030)
+#define	CAN2TID1	(*(volatile uint32_t*)0x40048034)
+#define	CAN2TDA1	(*(volatile uint32_t*)0x40048038)
+#define	CAN2TDB1	(*(volatile uint32_t*)0x4004803C)
+#define	CAN2TFI2	(*(volatile uint32_t*)0x40048040)
+#define	CAN2TID2	(*(volatile uint32_t*)0x40048044)
+#define	CAN2TDA2	(*(volatile uint32_t*)0x40048048)
+#define	CAN2TDB2	(*(volatile uint32_t*)0x4004804C)
+#define	CAN2TFI3	(*(volatile uint32_t*)0x40048050)
+#define	CAN2TID3	(*(volatile uint32_t*)0x40048054)
+#define	CAN2TDA3	(*(volatile uint32_t*)0x40048058)
+#define	CAN2TDB3	(*(volatile uint32_t*)0x4004805C)
+
+/* SPI0 */
+#define	S0SPCR		(*(volatile uint32_t*)0x40020000)
+#define	S0SPSR		(*(volatile uint32_t*)0x40020004)
+#define	S0SPDR		(*(volatile uint32_t*)0x40020008)
+#define	S0SPCCR		(*(volatile uint32_t*)0x4002000C)
+#define	S0SPINT		(*(volatile uint32_t*)0x4002001C)
+
+/* SSP0,SSP1 */
+#define	SSP0CR0		(*(volatile uint32_t*)0x40088000)
+#define	SSP0CR1		(*(volatile uint32_t*)0x40088004)
+#define	SSP0DR		(*(volatile uint32_t*)0x40088008)
+#define	SSP0SR		(*(volatile uint32_t*)0x4008800C)
+#define	SSP0CPSR	(*(volatile uint32_t*)0x40088010)
+#define	SSP0IMSC	(*(volatile uint32_t*)0x40088014)
+#define	SSP0RIS		(*(volatile uint32_t*)0x40088018)
+#define	SSP0MIS		(*(volatile uint32_t*)0x4008801C)
+#define	SSP0ICR		(*(volatile uint32_t*)0x40088020)
+#define	SSP0DMACR	(*(volatile uint32_t*)0x40088024)
+#define	SSP1CR0		(*(volatile uint32_t*)0x40030000)
+#define	SSP1CR1		(*(volatile uint32_t*)0x40030004)
+#define	SSP1DR		(*(volatile uint32_t*)0x40030008)
+#define	SSP1SR		(*(volatile uint32_t*)0x4003000C)
+#define	SSP1CPSR	(*(volatile uint32_t*)0x40030010)
+#define	SSP1IMSC	(*(volatile uint32_t*)0x40030014)
+#define	SSP1RIS		(*(volatile uint32_t*)0x40030018)
+#define	SSP1MIS		(*(volatile uint32_t*)0x4003001C)
+#define	SSP1ICR		(*(volatile uint32_t*)0x40030020)
+#define	SSP1DMACR	(*(volatile uint32_t*)0x40030024)
+
+/* I2C0,I2C1,I2C2 */
+#define	I2C0CONSET		(*(volatile uint32_t*)0x4001C000)
+#define	I2C0STAT		(*(volatile uint32_t*)0x4001C004)
+#define	I2C0DAT			(*(volatile uint32_t*)0x4001C008)
+#define	I2C0ADR0		(*(volatile uint32_t*)0x4001C00C)
+#define	I2C0SCLH		(*(volatile uint32_t*)0x4001C010)
+#define	I2C0SCLL		(*(volatile uint32_t*)0x4001C014)
+#define	I2C0CONCLR		(*(volatile uint32_t*)0x4001C018)
+#define	I2C0MMCTRL		(*(volatile uint32_t*)0x4001C01C)
+#define	I2C0ADR1		(*(volatile uint32_t*)0x4001C020)
+#define	I2C0ADR2		(*(volatile uint32_t*)0x4001C024)
+#define	I2C0ADR3		(*(volatile uint32_t*)0x4001C028)
+#define	I2C0DATA_BUFFER	(*(volatile uint32_t*)0x4001C02C)
+#define	I2C0MASK		( (volatile uint32_t*)0x4001C030)
+#define	I2C0MASK0		(*(volatile uint32_t*)0x4001C030)
+#define	I2C0MASK1		(*(volatile uint32_t*)0x4001C034)
+#define	I2C0MASK2		(*(volatile uint32_t*)0x4001C038)
+#define	I2C0MASK3		(*(volatile uint32_t*)0x4001C03C)
+#define	I2C1CONSET		(*(volatile uint32_t*)0x4005C000)
+#define	I2C1STAT		(*(volatile uint32_t*)0x4005C004)
+#define	I2C1DAT			(*(volatile uint32_t*)0x4005C008)
+#define	I2C1ADR0		(*(volatile uint32_t*)0x4005C00C)
+#define	I2C1SCLH		(*(volatile uint32_t*)0x4005C010)
+#define	I2C1SCLL		(*(volatile uint32_t*)0x4005C014)
+#define	I2C1CONCLR		(*(volatile uint32_t*)0x4005C018)
+#define	I2C1MMCTRL		(*(volatile uint32_t*)0x4005C01C)
+#define	I2C1ADR1		(*(volatile uint32_t*)0x4005C020)
+#define	I2C1ADR2		(*(volatile uint32_t*)0x4005C024)
+#define	I2C1ADR3		(*(volatile uint32_t*)0x4005C028)
+#define	I2C1DATA_BUFFER	(*(volatile uint32_t*)0x4005C02C)
+#define	I2C1MASK		( (volatile uint32_t*)0x4005C030)
+#define	I2C1MASK0		(*(volatile uint32_t*)0x4005C030)
+#define	I2C1MASK1		(*(volatile uint32_t*)0x4005C034)
+#define	I2C1MASK2		(*(volatile uint32_t*)0x4005C038)
+#define	I2C1MASK3		(*(volatile uint32_t*)0x4005C03C)
+#define	I2C2CONSET		(*(volatile uint32_t*)0x400A0000)
+#define	I2C2STAT		(*(volatile uint32_t*)0x400A0004)
+#define	I2C2DAT			(*(volatile uint32_t*)0x400A0008)
+#define	I2C2ADR0		(*(volatile uint32_t*)0x400A000C)
+#define	I2C2SCLH		(*(volatile uint32_t*)0x400A0010)
+#define	I2C2SCLL		(*(volatile uint32_t*)0x400A0014)
+#define	I2C2CONCLR		(*(volatile uint32_t*)0x400A0018)
+#define	I2C2MMCTRL		(*(volatile uint32_t*)0x400A001C)
+#define	I2C2ADR1		(*(volatile uint32_t*)0x400A0020)
+#define	I2C2ADR2		(*(volatile uint32_t*)0x400A0024)
+#define	I2C2ADR3		(*(volatile uint32_t*)0x400A0028)
+#define	I2C2DATA_BUFFER	(*(volatile uint32_t*)0x400A002C)
+#define	I2C2MASK		( (volatile uint32_t*)0x400A0030)
+#define	I2C2MASK0		(*(volatile uint32_t*)0x400A0030)
+#define	I2C2MASK1		(*(volatile uint32_t*)0x400A0034)
+#define	I2C2MASK2		(*(volatile uint32_t*)0x400A0038)
+#define	I2C2MASK3		(*(volatile uint32_t*)0x400A003C)
+
+/* I2S */
+#define	I2SDAO		(*(volatile uint32_t*)0x400A8000)
+#define	I2SDAI		(*(volatile uint32_t*)0x400A8004)
+#define	I2STXFIFO	(*(volatile uint32_t*)0x400A8008)
+#define	I2SRXFIFO	(*(volatile uint32_t*)0x400A800C)
+#define	I2SSTATE	(*(volatile uint32_t*)0x400A8010)
+#define	I2SDMA1		(*(volatile uint32_t*)0x400A8014)
+#define	I2SDMA2		(*(volatile uint32_t*)0x400A8018)
+#define	I2SIRQ		(*(volatile uint32_t*)0x400A801C)
+#define	I2STXRATE	(*(volatile uint32_t*)0x400A8020)
+#define	I2SRXRATE	(*(volatile uint32_t*)0x400A8024)
+#define	I2STXBITRATE	(*(volatile uint32_t*)0x400A8028)
+#define	I2SRXBITRATE	(*(volatile uint32_t*)0x400A802C)
+#define	I2STXMODE	(*(volatile uint32_t*)0x400A8030)
+#define	I2SRXMODE	(*(volatile uint32_t*)0x400A8034)
+
+/* Timer0,Timer1,Timer2,Timer3 */
+#define	T0IR		(*(volatile uint32_t*)0x40004000)
+#define	T0TCR		(*(volatile uint32_t*)0x40004004)
+#define	T0TC		(*(volatile uint32_t*)0x40004008)
+#define	T0PR		(*(volatile uint32_t*)0x4000400C)
+#define	T0PC		(*(volatile uint32_t*)0x40004010)
+#define	T0MCR		(*(volatile uint32_t*)0x40004014)
+#define	T0MR		( (volatile uint32_t*)0x40004018)
+#define	T0MR0		(*(volatile uint32_t*)0x40004018)
+#define	T0MR1		(*(volatile uint32_t*)0x4000401C)
+#define	T0MR2		(*(volatile uint32_t*)0x40004020)
+#define	T0MR3		(*(volatile uint32_t*)0x40004024)
+#define	T0CCR		(*(volatile uint32_t*)0x40004028)
+#define	T0CR0		(*(volatile uint32_t*)0x4000402C)
+#define	T0CR1		(*(volatile uint32_t*)0x40004030)
+#define	T0EMR		(*(volatile uint32_t*)0x4000403C)
+#define	T0CTCR		(*(volatile uint32_t*)0x40004070)
+
+#define	T1IR		(*(volatile uint32_t*)0x40008000)
+#define	T1TCR		(*(volatile uint32_t*)0x40008004)
+#define	T1TC		(*(volatile uint32_t*)0x40008008)
+#define	T1PR		(*(volatile uint32_t*)0x4000800C)
+#define	T1PC		(*(volatile uint32_t*)0x40008010)
+#define	T1MCR		(*(volatile uint32_t*)0x40008014)
+#define	T1MR		( (volatile uint32_t*)0x40008018)
+#define	T1MR0		(*(volatile uint32_t*)0x40008018)
+#define	T1MR1		(*(volatile uint32_t*)0x4000801C)
+#define	T1MR2		(*(volatile uint32_t*)0x40008020)
+#define	T1MR3		(*(volatile uint32_t*)0x40008024)
+#define	T1CCR		(*(volatile uint32_t*)0x40008028)
+#define	T1CR0		(*(volatile uint32_t*)0x4000802C)
+#define	T1CR1		(*(volatile uint32_t*)0x40008030)
+#define	T1EMR		(*(volatile uint32_t*)0x4000803C)
+#define	T1CTCR		(*(volatile uint32_t*)0x40008070)
+
+#define	T2IR		(*(volatile uint32_t*)0x40090000)
+#define	T2TCR		(*(volatile uint32_t*)0x40090004)
+#define	T2TC		(*(volatile uint32_t*)0x40090008)
+#define	T2PR		(*(volatile uint32_t*)0x4009000C)
+#define	T2PC		(*(volatile uint32_t*)0x40090010)
+#define	T2MCR		(*(volatile uint32_t*)0x40090014)
+#define	T2MR		( (volatile uint32_t*)0x40090018)
+#define	T2MR0		(*(volatile uint32_t*)0x40090018)
+#define	T2MR1		(*(volatile uint32_t*)0x4009001C)
+#define	T2MR2		(*(volatile uint32_t*)0x40090020)
+#define	T2MR3		(*(volatile uint32_t*)0x40090024)
+#define	T2CCR		(*(volatile uint32_t*)0x40090028)
+#define	T2CR0		(*(volatile uint32_t*)0x4009002C)
+#define	T2CR1		(*(volatile uint32_t*)0x40090030)
+#define	T2EMR		(*(volatile uint32_t*)0x4009003C)
+#define	T2CTCR		(*(volatile uint32_t*)0x40090070)
+
+#define	T3IR		(*(volatile uint32_t*)0x40094000)
+#define	T3TCR		(*(volatile uint32_t*)0x40094004)
+#define	T3TC		(*(volatile uint32_t*)0x40094008)
+#define	T3PR		(*(volatile uint32_t*)0x4009400C)
+#define	T3PC		(*(volatile uint32_t*)0x40094010)
+#define	T3MCR		(*(volatile uint32_t*)0x40094014)
+#define	T3MR		( (volatile uint32_t*)0x40094018)
+#define	T3MR0		(*(volatile uint32_t*)0x40094018)
+#define	T3MR1		(*(volatile uint32_t*)0x4009401C)
+#define	T3MR2		(*(volatile uint32_t*)0x40094020)
+#define	T3MR3		(*(volatile uint32_t*)0x40094024)
+#define	T3CCR		(*(volatile uint32_t*)0x40094028)
+#define	T3CR0		(*(volatile uint32_t*)0x4009402C)
+#define	T3CR1		(*(volatile uint32_t*)0x40094030)
+#define	T3EMR		(*(volatile uint32_t*)0x4009403C)
+#define	T3CTCR		(*(volatile uint32_t*)0x40094070)
+
+/* Repeative Interrupt Timer */
+#define	RICOMPVAL	(*(volatile uint32_t*)0x400B0000)
+#define	RIMASK		(*(volatile uint32_t*)0x400B0004)
+#define	RICTRL		(*(volatile uint32_t*)0x400B0008)
+#define	RICOUNTER	(*(volatile uint32_t*)0x400B000C)
+
+/* PWM1 */
+#define	PWM1IR		(*(volatile uint32_t*)0x40018000)
+#define	PWM1TCR		(*(volatile uint32_t*)0x40018004)
+#define	PWM1TC		(*(volatile uint32_t*)0x40018008)
+#define	PWM1PR		(*(volatile uint32_t*)0x4001800C)
+#define	PWM1PC		(*(volatile uint32_t*)0x40018010)
+#define	PWM1MCR		(*(volatile uint32_t*)0x40018014)
+#define	PWM1MR0		(*(volatile uint32_t*)0x40018018)
+#define	PWM1MR1		(*(volatile uint32_t*)0x4001801C)
+#define	PWM1MR2		(*(volatile uint32_t*)0x40018020)
+#define	PWM1MR3		(*(volatile uint32_t*)0x40018024)
+#define	PWM1CCR		(*(volatile uint32_t*)0x40018028)
+#define	PWM1CR0		(*(volatile uint32_t*)0x4001802C)
+#define	PWM1CR1		(*(volatile uint32_t*)0x40018030)
+#define	PWM1CR2		(*(volatile uint32_t*)0x40018034)
+#define	PWM1CR3		(*(volatile uint32_t*)0x40018038)
+#define	PWM1MR4		(*(volatile uint32_t*)0x40018040)
+#define	PWM1MR5		(*(volatile uint32_t*)0x40018044)
+#define	PWM1MR6		(*(volatile uint32_t*)0x40018048)
+#define	PWM1PCR		(*(volatile uint32_t*)0x4001804C)
+#define	PWM1LER		(*(volatile uint32_t*)0x40018050)
+#define	PWM1CTCR	(*(volatile uint32_t*)0x40018070)
+
+/* Motor Control PWM */
+#define	MCCON		(*(volatile uint32_t*)0x400B8000)
+#define	MCCON_SET	(*(volatile uint32_t*)0x400B8004)
+#define	MCCON_CLR	(*(volatile uint32_t*)0x400B8008)
+#define	MCCAPCON	(*(volatile uint32_t*)0x400B800C)
+#define	MCCAPCON_SET	(*(volatile uint32_t*)0x400B8010)
+#define	MCCAPCON_CLR	(*(volatile uint32_t*)0x400B8014)
+#define	MCTC		( (volatile uint32_t*)0x400B8018)
+#define	MCTC0		(*(volatile uint32_t*)0x400B8018)
+#define	MCTC1		(*(volatile uint32_t*)0x400B801C)
+#define	MCTC2		(*(volatile uint32_t*)0x400B8020)
+#define	MCLIM		( (volatile uint32_t*)0x400B8024)
+#define	MCLIM0		(*(volatile uint32_t*)0x400B8024)
+#define	MCLIM1		(*(volatile uint32_t*)0x400B8028)
+#define	MCLIM2		(*(volatile uint32_t*)0x400B802C)
+#define	MCMAT		( (volatile uint32_t*)0x400B8030)
+#define	MCMAT0		(*(volatile uint32_t*)0x400B8030)
+#define	MCMAT1		(*(volatile uint32_t*)0x400B8034)
+#define	MCMAT2		(*(volatile uint32_t*)0x400B8038)
+#define	MCDT		(*(volatile uint32_t*)0x400B803C)
+#define	MCCP		(*(volatile uint32_t*)0x400B8040)
+#define	MCCAP		( (volatile uint32_t*)0x400B8044)
+#define	MCCAP0		(*(volatile uint32_t*)0x400B8044)
+#define	MCCAP1		(*(volatile uint32_t*)0x400B8048)
+#define	MCCAP2		(*(volatile uint32_t*)0x400B804C)
+#define	MCINTEN		(*(volatile uint32_t*)0x400B8050)
+#define	MCINTEN_SET	(*(volatile uint32_t*)0x400B8054)
+#define	MCINTEN_CLR	(*(volatile uint32_t*)0x400B8058)
+#define	MCCNTCON	(*(volatile uint32_t*)0x400B805C)
+#define	MCCNTCON_SET	(*(volatile uint32_t*)0x400B8060)
+#define	MCCNTCON_CLR	(*(volatile uint32_t*)0x400B8064)
+#define	MCINTF		(*(volatile uint32_t*)0x400B8068)
+#define	MCINTF_SET	(*(volatile uint32_t*)0x400B806C)
+#define	MCINTF_CLR	(*(volatile uint32_t*)0x400B8070)
+#define	MCCAP_CLR	(*(volatile uint32_t*)0x400B8074)
+
+/* Quadrature Encoder Interface */
+#define	QEICON		(*(volatile uint32_t*)0x400BC000)
+#define	QEICONF		(*(volatile uint32_t*)0x400BC008)
+#define	QEISTAT		(*(volatile uint32_t*)0x400BC004)
+#define	QEIPOS		(*(volatile uint32_t*)0x400BC00C)
+#define	QEIMAXPOS	(*(volatile uint32_t*)0x400BC010)
+#define	CMPOS0		(*(volatile uint32_t*)0x400BC014)
+#define	CMPOS1		(*(volatile uint32_t*)0x400BC018)
+#define	CMPOS2		(*(volatile uint32_t*)0x400BC01C)
+#define	INXCNT		(*(volatile uint32_t*)0x400BC020)
+#define	INXCMP		(*(volatile uint32_t*)0x400BC024)
+#define	QEILOAD		(*(volatile uint32_t*)0x400BC028)
+#define	QEITIME		(*(volatile uint32_t*)0x400BC02C)
+#define	QEIVEL		(*(volatile uint32_t*)0x400BC030)
+#define	QEICAP		(*(volatile uint32_t*)0x400BC034)
+#define	VELCOMP		(*(volatile uint32_t*)0x400BC038)
+#define	FILTER		(*(volatile uint32_t*)0x400BC03C)
+#define	QEIINTSTAT	(*(volatile uint32_t*)0x400BCFE0)
+#define	QEISET		(*(volatile uint32_t*)0x400BCFEC)
+#define	QEICLR		(*(volatile uint32_t*)0x400BCFE8)
+#define	QEIIE		(*(volatile uint32_t*)0x400BCFE4)
+#define	QEIIES		(*(volatile uint32_t*)0x400BCFDC)
+#define	QEIIEC		(*(volatile uint32_t*)0x400BCFD8)
+
+/* RTC */
+#define	RTC_ILR		(*(volatile uint32_t*)0x40024000)
+#define	RTC_CCR		(*(volatile uint32_t*)0x40024008)
+#define	RTC_CIIR	(*(volatile uint32_t*)0x4002400C)
+#define	RTC_AMR		(*(volatile uint32_t*)0x40024010)
+#define	RTC_AUX		(*(volatile uint32_t*)0x4002405C)
+#define	RTC_AUXEN	(*(volatile uint32_t*)0x40024058)
+#define	RTC_CTIME0	(*(volatile uint32_t*)0x40024014)
+#define	RTC_CTIME1	(*(volatile uint32_t*)0x40024018)
+#define	RTC_CTIME2	(*(volatile uint32_t*)0x4002401C)
+#define	RTC_SEC		(*(volatile uint32_t*)0x40024020)
+#define	RTC_MIN		(*(volatile uint32_t*)0x40024024)
+#define	RTC_HOUR	(*(volatile uint32_t*)0x40024028)
+#define	RTC_DOM		(*(volatile uint32_t*)0x4002402C)
+#define	RTC_DOW		(*(volatile uint32_t*)0x40024030)
+#define	RTC_DOY		(*(volatile uint32_t*)0x40024034)
+#define	RTC_MONTH	(*(volatile uint32_t*)0x40024038)
+#define	RTC_YEAR	(*(volatile uint32_t*)0x4002403C)
+#define	RTC_CALIBRATION	(*(volatile uint32_t*)0x40024040)
+#define	RTC_GPREG	( (volatile uint32_t*)0x40024044)
+#define	RTC_GPREG0	(*(volatile uint32_t*)0x40024044)
+#define	RTC_GPREG1	(*(volatile uint32_t*)0x40024048)
+#define	RTC_GPREG2	(*(volatile uint32_t*)0x4002404C)
+#define	RTC_GPREG3	(*(volatile uint32_t*)0x40024050)
+#define	RTC_GPREG4	(*(volatile uint32_t*)0x40024054)
+#define	RTC_ALSEC	(*(volatile uint32_t*)0x40024060)
+#define	RTC_ALMIN	(*(volatile uint32_t*)0x40024064)
+#define	RTC_ALHOUR	(*(volatile uint32_t*)0x40024068)
+#define	RTC_ALDOM	(*(volatile uint32_t*)0x4002406C)
+#define	RTC_ALDOW	(*(volatile uint32_t*)0x40024070)
+#define	RTC_ALDOY	(*(volatile uint32_t*)0x40024074)
+#define	RTC_ALMON	(*(volatile uint32_t*)0x40024078)
+#define	RTC_ALYEAR	(*(volatile uint32_t*)0x4002407C)
+
+/* WDT */
+#define	WDMOD		(*(volatile uint32_t*)0x40000000)
+#define	WDTC		(*(volatile uint32_t*)0x40000004)
+#define	WDFEED		(*(volatile uint32_t*)0x40000008)
+#define	WDTV		(*(volatile uint32_t*)0x4000000C)
+#define	WDCLKSEL	(*(volatile uint32_t*)0x40000010)
+
+/* ADC0 */
+#define	AD0CR		(*(volatile uint32_t*)0x40034000)
+#define	AD0GDR		(*(volatile uint32_t*)0x40034004)
+#define	AD0INTEN	(*(volatile uint32_t*)0x4003400C)
+#define	AD0DR		( (volatile uint32_t*)0x40034010)
+#define	AD0DR0		(*(volatile uint32_t*)0x40034010)
+#define	AD0DR1		(*(volatile uint32_t*)0x40034014)
+#define	AD0DR2		(*(volatile uint32_t*)0x40034018)
+#define	AD0DR3		(*(volatile uint32_t*)0x4003401C)
+#define	AD0DR4		(*(volatile uint32_t*)0x40034020)
+#define	AD0DR5		(*(volatile uint32_t*)0x40034024)
+#define	AD0DR6		(*(volatile uint32_t*)0x40034028)
+#define	AD0DR7		(*(volatile uint32_t*)0x4003402C)
+#define	AD0STAT		(*(volatile uint32_t*)0x40034030)
+#define	AD0TRM		(*(volatile uint32_t*)0x40034034)
+
+/* DAC */
+#define	DACR		(*(volatile uint32_t*)0x4008C000)
+#define	DACCTRL		(*(volatile uint32_t*)0x4008C004)
+#define	DACCNTVAL	(*(volatile uint32_t*)0x4008C008)
+
+/* GPDMA */
+#define	DMACIntStat		(*(volatile uint32_t*)0x50004000)
+#define	DMACIntTCStat	(*(volatile uint32_t*)0x50004004)
+#define	DMACIntTCClear	(*(volatile uint32_t*)0x50004008)
+#define	DMACIntErrStat	(*(volatile uint32_t*)0x5000400C)
+#define	DMACIntErrClr	(*(volatile uint32_t*)0x50004010)
+#define	DMACRawIntTCStat	(*(volatile uint32_t*)0x50004014)
+#define	DMACRawIntErrStat	(*(volatile uint32_t*)0x50004018)
+#define	DMACEnbldChns	(*(volatile uint32_t*)0x5000401C)
+#define	DMACSoftBReq	(*(volatile uint32_t*)0x50004020)
+#define	DMACSoftSReq	(*(volatile uint32_t*)0x50004024)
+#define	DMACSoftLBReq	(*(volatile uint32_t*)0x50004028)
+#define	DMACSoftLSReq	(*(volatile uint32_t*)0x5000402C)
+#define	DMACConfig		(*(volatile uint32_t*)0x50004030)
+#define	DMACSync		(*(volatile uint32_t*)0x50004034)
+#define	DMAREQSEL		(*(volatile uint32_t*)0x400FC1C4)
+#define	DMACC0SrcAddr	(*(volatile uint32_t*)0x50004100)
+#define	DMACC0DestAddr	(*(volatile uint32_t*)0x50004104)
+#define	DMACC0LLI		(*(volatile uint32_t*)0x50004108)
+#define	DMACC0Control	(*(volatile uint32_t*)0x5000410C)
+#define	DMACC0Config	(*(volatile uint32_t*)0x50004110)
+#define	DMACC1SrcAddr	(*(volatile uint32_t*)0x50004120)
+#define	DMACC1DestAddr	(*(volatile uint32_t*)0x50004124)
+#define	DMACC1LLI		(*(volatile uint32_t*)0x50004128)
+#define	DMACC1Control	(*(volatile uint32_t*)0x5000412C)
+#define	DMACC1Config	(*(volatile uint32_t*)0x50004130)
+#define	DMACC2SrcAddr	(*(volatile uint32_t*)0x50004140)
+#define	DMACC2DestAddr	(*(volatile uint32_t*)0x50004144)
+#define	DMACC2LLI		(*(volatile uint32_t*)0x50004148)
+#define	DMACC2Control	(*(volatile uint32_t*)0x5000414C)
+#define	DMACC2Config	(*(volatile uint32_t*)0x50004150)
+#define	DMACC3SrcAddr	(*(volatile uint32_t*)0x50004160)
+#define	DMACC3DestAddr	(*(volatile uint32_t*)0x50004164)
+#define	DMACC3LLI		(*(volatile uint32_t*)0x50004168)
+#define	DMACC3Control	(*(volatile uint32_t*)0x5000416C)
+#define	DMACC3Config	(*(volatile uint32_t*)0x50004170)
+#define	DMACC4SrcAddr	(*(volatile uint32_t*)0x50004180)
+#define	DMACC4DestAddr	(*(volatile uint32_t*)0x50004184)
+#define	DMACC4LLI		(*(volatile uint32_t*)0x50004188)
+#define	DMACC4Control	(*(volatile uint32_t*)0x5000418C)
+#define	DMACC4Config	(*(volatile uint32_t*)0x50004190)
+#define	DMACC5SrcAddr	(*(volatile uint32_t*)0x500041A0)
+#define	DMACC5DestAddr	(*(volatile uint32_t*)0x500041A4)
+#define	DMACC5LLI		(*(volatile uint32_t*)0x500041A8)
+#define	DMACC5Control	(*(volatile uint32_t*)0x500041AC)
+#define	DMACC5Config	(*(volatile uint32_t*)0x500041B0)
+#define	DMACC6SrcAddr	(*(volatile uint32_t*)0x500041C0)
+#define	DMACC6DestAddr	(*(volatile uint32_t*)0x500041C4)
+#define	DMACC6LLI		(*(volatile uint32_t*)0x500041C8)
+#define	DMACC6Control	(*(volatile uint32_t*)0x500041CC)
+#define	DMACC6Config	(*(volatile uint32_t*)0x500041D0)
+#define	DMACC7SrcAddr	(*(volatile uint32_t*)0x500041E0)
+#define	DMACC7DestAddr	(*(volatile uint32_t*)0x500041E4)
+#define	DMACC7LLI		(*(volatile uint32_t*)0x500041E8)
+#define	DMACC7Control	(*(volatile uint32_t*)0x500041EC)
+#define	DMACC7Config	(*(volatile uint32_t*)0x500041F0)
+
+
+/* Cortex-M3 System timer */
+#define	SYST_CSR	(*(volatile uint32_t*)0xE000E010)
+#define	SYST_RVR	(*(volatile uint32_t*)0xE000E014)
+#define	SYST_CVR	(*(volatile uint32_t*)0xE000E018)
+#define	SYST_CALIB	(*(volatile uint32_t*)0xE000E01C)
+
+/* Cortex-M3 NVIC */
+#define	ISER		( (volatile uint32_t*)0xE000E100)
+#define	ICER		( (volatile uint32_t*)0xE000E180)
+#define	ISPR		( (volatile uint32_t*)0xE000E200)
+#define	ICPR		( (volatile uint32_t*)0xE000E280)
+#define	IABR		( (volatile uint32_t*)0xE000E300)
+#define	IPR			( (volatile uint8_t *)0xE000E400)
+#define	STIR		(*(volatile uint32_t*)0xE000EF00)
+
+/* Cortex-M3 SCB */
+#define	ACTLR		(*(volatile uint32_t*)0xE000E008)
+#define	CPUID		(*(volatile uint32_t*)0xE000ED00)
+#define	ICSR		(*(volatile uint32_t*)0xE000ED04)
+#define	VTOR		(*(volatile uint32_t*)0xE000ED08)
+#define	AIRCR		(*(volatile uint32_t*)0xE000ED0C)
+#define	SCR			(*(volatile uint32_t*)0xE000ED10)
+#define	CCR			(*(volatile uint32_t*)0xE000ED14)
+#define	SHPR		( (volatile uint8_t *)0xE000ED14)
+#define	CFSR		(*(volatile uint32_t*)0xE000ED28)
+#define	MMSR		(*(volatile uint32_t*)0xE000ED28)
+#define	BFSR		(*(volatile uint32_t*)0xE000ED29)
+#define	UFSR		(*(volatile uint32_t*)0xE000ED2A)
+#define	HFSR		(*(volatile uint32_t*)0xE000ED2C)
+#define	MMFAR		(*(volatile uint32_t*)0xE000ED34)
+#define	BFAR		(*(volatile uint32_t*)0xE000ED38)
+
+
+
+/*--------------------------------------------------------------*/
+/* Cortex-M3 core/peripheral access macros                      */
+/*--------------------------------------------------------------*/
+
+/* These are for only privileged mode */
+#define __enable_irq() asm volatile ("CPSIE i\n")
+#define __disable_irq() asm volatile ("CPSID i\n")
+#define __enable_irqn(n) ISER[(n) / 32] = 1 << ((n) % 32)
+#define __disable_irqn(n) ICER[(n) / 32] = 1 << ((n) % 32)
+#define __test_irqn_enabled(n) (ISER[(n) / 32] & (1 << ((n) % 32)))
+#define __set_irqn(n) ISPR[(n) / 32] = 1 << ((n) % 32)
+#define __clear_irqn(n) ICPR[(n) / 32] = 1 << ((n) % 32)
+#define __test_irqn(n) (ICPR[(n) / 32] & (1 << ((n) % 32)))
+#define __test_irqn_active(n) (IABR[n / 32] & (1 << ((n) % 32)))
+#define __set_irqn_priority(n,v) IPR[n] = (v)
+#define __set_faultn_priority(n,v) SHPR[(n) + 16] = (v)
+#define __get_MSP()			({uint32_t __rv; asm ("MRS %0, MSP\n"		: "=r" (__rv)); __rv;})
+#define __get_PSP()			({uint32_t __rv; asm ("MRS %0, PSP\n"		: "=r" (__rv)); __rv;})
+#define __get_PRIMASK() 	({uint32_t __rv; asm ("MRS %0, PRIMASK\n"	: "=r" (__rv)); __rv;})
+#define __get_FAULTMASK()	({uint32_t __rv; asm ("MRS %0, FAULTMASK\n"	: "=r" (__rv)); __rv;})
+#define __get_BASEPRI() 	({uint32_t __rv; asm ("MRS %0, BASEPRI\n"	: "=r" (__rv)); __rv;})
+#define __get_CONTROL()		({uint32_t __rv; asm ("MRS %0, CONTROL\n"	: "=r" (__rv)); __rv;})
+#define __set_MSP(arg)		{uint32_t __v=arg; asm ("MSR MSP, %0\n" 		:: "r" (__v));}
+#define __set_PSP(arg)		{uint32_t __v=arg; asm ("MSR PSP, %0\n" 		:: "r" (__v));}
+#define __set_PRIMASK(arg)	{uint32_t __v=arg; asm ("MSR PRIMASK, %0\n" 	:: "r" (__v));}
+#define __set_FAULTMASK(arg) {uint32_t __v=arg; asm ("MSR FAULTMASK, %0\n" 	:: "r" (__v));}
+#define __set_BASEPRI(arg)	{uint32_t __v=arg; asm ("MSR BASEPRI, %0\n" 	:: "r" (__v));}
+#define __set_CONTORL(arg)	{uint32_t __v=arg; asm ("MSR CONTROL, %0\nISB\n" :: "r" (__v));}
+
+/* These functions and macros are alternative of above for user mode */
+#if USE_SV_SERVICE
+#define __enable_irq_user()		asm volatile ("SVC #0\n")	/* CPSIE i */
+#define __disable_irq_user()	asm volatile ("SVC #1\n")	/* CPSID i */
+#define __enable_irq_user()		asm volatile ("SVC #2\n")	/* CPSIE f */
+#define __disable_irq_user()	asm volatile ("SVC #3\n")	/* CPSID f */
+uint32_t __get_scs_reg (volatile uint32_t* reg);			/* Read a register in SCS */
+void __set_scs_reg (volatile uint32_t* reg, uint32_t val);	/* Write a register in SCS */
+#define __enable_irqn_user(n) __set_scs_reg(&ISER[((n) / 32)], 1 << ((n) % 32))
+#define __disable_irqn_user(n) __set_scs_reg(&ISCR[((n) / 32)], 1 << ((n) % 32))
+#define __test_irqn_enabled_user(n) (__get_scs_reg(&ISCR[(n) / 32]) & (1 << ((n) % 32)))
+#define __set_irqn_user(n) __set_scs_reg(&ISPR[((n) / 32)], 1 << ((n) % 32))
+#define __clear_irqn_user(n) __set_scs_reg(&ICPR[((n) / 32)], 1 << ((n) % 32))
+#define __test_irqn_user(n) (__get_scs_reg(&ICPR[(n) / 32]) & (1 << ((n) % 32)))
+#define __test_active_irqn_user(n) (__get_scs_reg(&IABR[(n) / 32]) & (1 << ((n) % 32)))
+#define __set_irqn_priority_user(n,v) __set_scs_reg(&IPR[n], (v))
+#define __set_faultn_priority_user(n,v) __set_scs_reg(&SHPR[(n) + 16], (v))
+#endif
+
+/* These functions/macros can be used at user/privileged mode */
+#define __REV(arg)		({uint32_t __r, __v=arg; asm ("REV %0,%1\n"   : "=r" (__r) : "r" (__v) ); __r;})
+#define __REV16(arg)	({uint32_t __r, __v=arg; asm ("REV16 %0,%1\n" : "=r" (__r) : "r" (__v) ); __r;})
+#define __REVSH(arg)	({uint32_t __r, __v=arg; asm ("REVSH %0,%1\n" : "=r" (__r) : "r" (__v) ); __r;})
+#define __RBIT(arg)		({uint32_t __r, __v=arg; asm ("RBIT %0,%1\n"  : "=r" (__r) : "r" (__v) ); __r;})
+#define __LDREXB(p)		({uint8_t __r;	asm ("LDREXB %0,[%1]\n" : "=r" (__r) : "r" (p)); __r;})
+#define __LDREXH(p)		({uint16_t __r;	asm ("LDREXH %0,[%1]\n" : "=r" (__r) : "r" (p)); __r;})
+#define __LDREXW(p)		({uint32_t __r;	asm ("LDREX  %0,[%1]\n" : "=r" (__r) : "r" (p)); __r;})
+#define __STREXB(d,p)	({register uint32_t __r asm("r2"); register uint8_t __d asm("r1") = d; register volatile uint8_t *__p asm("r0") = p; asm ("STREXB %0,%2,[%1]\n" : "=r" (__r) : "r" (__p), "r" (__d)); __r;})
+#define __STREXH(d,p)	({register uint32_t __r asm("r2"); register uint16_t __d asm("r1") = d; register volatile uint16_t *__p asm("r0") = p; asm ("STREXH %0,%2,[%1]\n" : "=r" (__r) : "r" (p), "r" (__d)); __r;})
+#define __STREXW(d,p)	({register uint32_t __r asm("r2"); register uint32_t __d asm("r1") = d; register volatile uint32_t *__p asm("r0") = p; asm ("STREX  %0,%2,[%1]\n" : "=r" (__r) : "r" (p), "r" (__d)); __r;})
+#define __CLREX() asm volatile ("CLREX\n")
+#define __SEV() asm volatile ("SEV\n")
+#define __WFE() asm volatile ("WFE\n")
+#define __WFI() asm volatile ("WFI\n")
+
+/* LPC176x IRQ number */
+#define	MemManage_IRQn	(-12)
+#define	BusFault_IRQn	(-11)
+#define	UsageFault_IRQn	(-10)
+#define	SVC_IRQn		(-5)
+#define	DebugMon_IRQn	(-4)
+#define	PendSV_IRQn		(-2)
+#define	SysTick_IRQn	(-1)
+#define	WDT_IRQn		0
+#define	TIMER0_IRQn		1
+#define	TIMER1_IRQn		2
+#define	TIMER2_IRQn		3
+#define	TIMER3_IRQn		4
+#define	UART0_IRQn		5
+#define	UART1_IRQn		6
+#define	UART2_IRQn		7
+#define	UART3_IRQn		8
+#define	PWM1_IRQn		9
+#define	I2C0_IRQn		10
+#define	I2C1_IRQn		11
+#define	I2C2_IRQn		12
+#define	SPI_IRQn		13
+#define	SSP0_IRQn		14
+#define	SSP1_IRQn		15
+#define	PLL0_IRQn		16
+#define	RTC_IRQn		17
+#define	EINT0_IRQn		18
+#define	EINT1_IRQn		19
+#define	EINT2_IRQn		20
+#define	EINT3_IRQn		21
+#define	ADC_IRQn		22
+#define	BOD_IRQn		23
+#define	USB_IRQn		24
+#define	CAN_IRQn		25
+#define	GPDMA_IRQn		26
+#define	I2S_IRQn		27
+#define	ETHER_IRQn		28
+#define	RIT_IRQn		29
+#define	MCPWM_IRQn		30
+#define	QEI_IRQn		31
+#define	PLL1_IRQn		32
+#define	USBACT_IRQn		33
+#define	CANACT_IRQn		34
+
+/* LPC176x Peripheral Divider */
+#define	__set_PCLKSEL(p,v)	PCLKSEL[(p) / 16] = (PCLKSEL[(p) / 16] & ~(3 << ((p) * 2 % 32))) | (v << ((p) * 2 % 32))
+#define PCLKDIV_4	0
+#define PCLKDIV_1	1
+#define PCLKDIV_2	2
+#define PCLKDIV_8	3
+#define	PCLK_WDT	0
+#define	PCLK_TIMER0	1
+#define	PCLK_TIMER1	2
+#define	PCLK_UART0	3
+#define	PCLK_UART1	4
+#define	PCLK_PWM1	6
+#define	PCLK_I2C0	7
+#define	PCLK_SPI	8
+#define	PCLK_SSP1	10
+#define	PCLK_DAC	11
+#define	PCLK_ADC	12
+#define	PCLK_CAN1	13
+#define	PCLK_CAN2	14
+#define	PCLK_ACF	15
+#define	PCLK_QEI	16
+#define	PCLK_GPIOINT	17
+#define	PCLK_PCB	18
+#define	PCLK_I2C1	19
+#define	PCLK_SSP0	21
+#define	PCLK_TIMER2	22
+#define	PCLK_TIMER3	23
+#define	PCLK_UART2	24
+#define	PCLK_UART3	25
+#define	PCLK_I2C2	26
+#define	PCLK_I2S	27
+#define	PCLK_RIT	28
+#define	PCLK_SYSCON	29
+#define	PCLK_MC		30
+
+/* LPC176x Pin Configuration */
+#define __set_PINSEL(p,b,v)		PINSEL[(p) * 2 + (b) / 16] = (PINSEL[(p) * 2 + (b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
+#define __set_PINMODE(p,b,v)	PINMODE[(p) * 2 + (b) / 16] = (PINMODE[(p) * 2 + (b) / 16] & ~(3 << ((b) * 2 % 32))) | (v << ((b) * 2 % 32))
+#define __set_PINOD(p,b,v)		PINOD[p] = (PINOD[p] & ~(1 << (b))) | ((v) << (b))
+
+/* LPC176x Power Control */
+#define	__set_PCONP(p,v)	PCONP = (PCONP & ~(1 << (p))) | (1 << (p))
+#define	PCTIM0	1
+#define	PCTIM1	2
+#define	PCUART0	3
+#define	PCUART1	4
+#define	PCPWM1	6
+#define	PCIIC0	7
+#define	PCSPI	8
+#define	PCRTC	9
+#define	PCSSP1	10
+#define	PCADC	12
+#define	PCCAN1	13
+#define	PCCAN2	14
+#define	PCGPIO	15
+#define	PCRIT	16
+#define	PCMCPWM	17
+#define	PCQEI	18
+#define	PCI2C1	19
+#define	PCSSP0	21
+#define	PCTIM2	22
+#define	PCTIM3	23
+#define	PCUART2	24
+#define	PCUART3	25
+#define	PCI2C2	26
+#define	PCI2S	27
+#define	PCGPDMA	29
+#define	PCENET	30
+#define	PCUSB	31
+
+
+/*--------------------------------------------------------------*/
+/* Misc Macros                                                  */
+/*--------------------------------------------------------------*/
+
+
+#define	_BV(bit) (1<<(bit))
+
+#define	IMPORT_BIN(sect, file, sym) asm (\
+		".section " #sect "\n"\
+		".balign 4\n"\
+		".global " #sym "\n"\
+		#sym ":\n"\
+		".incbin \"" file "\"\n"\
+		".global _sizeof_" #sym "\n"\
+		".set _sizeof_" #sym ", . - " #sym "\n"\
+		".balign 4\n"\
+		".section \".text\"\n")
+
+#define	IMPORT_BIN_PART(sect, file, ofs, siz, sym) asm (\
+		".section " #sect "\n"\
+		".balign 4\n"\
+		".global " #sym "\n"\
+		#sym ":\n"\
+		".incbin \"" file "\"," #ofs "," #siz "\n"\
+		".global _sizeof_" #sym "\n"\
+		".set _sizeof_" #sym ", . - " #sym "\n"\
+		".balign 4\n"\
+		".section \".text\"\n")
+
+/* Jump to secondary application */
+#define JUMP_APP(appvt) asm (\
+		"LDR SP, [%0]\n"          /* Initialize SP */\
+		"LDR PC, [%0, #4]\n"      /* Go to reset vector */\
+        : : "r" (appvt))
+
+
+#endif /* __LPC176x */

+ 109 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/chanfs/diskio.h

@@ -0,0 +1,109 @@
+/*-----------------------------------------------------------------------
+/  Low level disk interface modlue include file   (C)ChaN, 2015
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _DISKIO_WRITE	1	/* 1: Enable disk_write function */
+#define _DISKIO_IOCTL	1	/* 1: Enable disk_ioctl fucntion */
+#define _DISKIO_ISDIO	0	/* 1: Enable iSDIO control fucntion */
+
+#include "integer.h"
+
+
+/* Status of Disk Functions */
+typedef BYTE	DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+	RES_OK = 0,		/* 0: Successful */
+	RES_ERROR,		/* 1: R/W Error */
+	RES_WRPRT,		/* 2: Write Protected */
+	RES_NOTRDY,		/* 3: Not Ready */
+	RES_PARERR		/* 4: Invalid Parameter */
+} DRESULT;
+
+
+#if	_DISKIO_ISDIO
+/* Command structure for iSDIO ioctl command */
+typedef struct {
+	BYTE	func;	/* Function number: 0..7 */
+	WORD	ndata;	/* Number of bytes to transfer: 1..512, or mask + data */
+	DWORD	addr;	/* Register address: 0..0x1FFFF */
+	void*	data;	/* Pointer to the data (to be written | read buffer) */
+} SDIO_CMD;
+#endif
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+#if	_DISKIO_WRITE
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+#endif
+#if	_DISKIO_IOCTL
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+#endif
+
+
+/* Disk Status Bits (DSTATUS) */
+#define STA_NOINIT		0x01	/* Drive not initialized */
+#define STA_NODISK		0x02	/* No medium in the drive */
+#define STA_PROTECT		0x04	/* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (Used by FatFs) */
+#define CTRL_SYNC			0	/* Complete pending write process (needed at _FS_READONLY == 0) */
+#define GET_SECTOR_COUNT	1	/* Get media size (needed at _USE_MKFS == 1) */
+#define GET_SECTOR_SIZE		2	/* Get sector size (needed at _MAX_SS != _MIN_SS) */
+#define GET_BLOCK_SIZE		3	/* Get erase block size (needed at _USE_MKFS == 1) */
+#define CTRL_TRIM			4	/* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */
+
+/* Generic command (Not used by FatFs) */
+#define CTRL_FORMAT			5	/* Create physical format on the media */
+#define CTRL_POWER_IDLE		6	/* Put the device idle state */
+#define CTRL_POWER_OFF		7	/* Put the device off state */
+#define CTRL_LOCK			8	/* Lock media removal */
+#define CTRL_UNLOCK			9	/* Unlock media removal */
+#define CTRL_EJECT			10	/* Eject media */
+
+/* MMC/SDC specific ioctl command (Not used by FatFs) */
+#define MMC_GET_TYPE		50	/* Get card type */
+#define MMC_GET_CSD			51	/* Get CSD */
+#define MMC_GET_CID			52	/* Get CID */
+#define MMC_GET_OCR			53	/* Get OCR */
+#define MMC_GET_SDSTAT		54	/* Get SD status */
+#define ISDIO_READ			55	/* Read data form SD iSDIO register */
+#define ISDIO_WRITE			56	/* Write data to SD iSDIO register */
+#define ISDIO_MRITE			57	/* Masked write data to SD iSDIO register */
+
+/* ATA/CF specific ioctl command (Not used by FatFs) */
+#define ATA_GET_REV			60	/* Get F/W revision */
+#define ATA_GET_MODEL		61	/* Get model name */
+#define ATA_GET_SN			62	/* Get serial number */
+
+
+/* MMC card type flags (MMC_GET_TYPE) */
+#define CT_MMC		0x01		/* MMC ver 3 */
+#define CT_SD1		0x02		/* SD ver 1 */
+#define CT_SD2		0x04		/* SD ver 2 */
+#define CT_SDC		(CT_SD1|CT_SD2)	/* SD */
+#define CT_BLOCK	0x08		/* Block addressing */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 6204 - 0
Marlin/frameworks/CMSIS/LPC1768/lib/chanfs/ff.c

@@ -0,0 +1,6204 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - Generic FAT Filesystem Module  R0.13                               /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2017, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/
+/----------------------------------------------------------------------------*/
+
+
+#include "ff.h"			/* Declarations of FatFs API */
+#include "diskio.h"		/* Declarations of device I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if FF_DEFINED != 87030	/* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* ASCII code support macros */
+#define IsUpper(c)	((c) >= 'A' && (c) <= 'Z')
+#define IsLower(c)	((c) >= 'a' && (c) <= 'z')
+#define IsDigit(c)	((c) >= '0' && (c) <= '9')
+
+
+/* Additional file attribute bits for internal use */
+#define AM_VOL		0x08	/* Volume label */
+#define AM_LFN		0x0F	/* LFN entry */
+#define AM_MASK		0x3F	/* Mask of defined bits */
+
+
+/* Additional file access control and file status flags for internal use */
+#define FA_SEEKEND	0x20	/* Seek to end of the file on file open */
+#define FA_MODIFIED	0x40	/* File has been modified */
+#define FA_DIRTY	0x80	/* FIL.buf[] needs to be written-back */
+
+
+/* Name status flags in fn[11] */
+#define NSFLAG		11		/* Index of the 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 */
+#define NS_NOLFN	0x40	/* Do not find LFN */
+#define NS_NONAME	0x80	/* Not followed */
+
+
+/* Limits and boundaries */
+#define MAX_DIR		0x200000		/* Max size of FAT directory */
+#define MAX_DIR_EX	0x10000000		/* Max size of exFAT directory */
+#define MAX_FAT12	0xFF5			/* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */
+#define MAX_FAT16	0xFFF5			/* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */
+#define MAX_FAT32	0x0FFFFFF5		/* Max FAT32 clusters (not specified, practical limit) */
+#define MAX_EXFAT	0x7FFFFFFD		/* Max exFAT clusters (differs from specs, implementation limit) */
+
+
+/* FatFs refers the FAT structure as simple byte array instead of structure member
+/ because the C structure is not binary compatible between different platforms */
+
+#define BS_JmpBoot			0		/* x86 jump instruction (3-byte) */
+#define BS_OEMName			3		/* OEM name (8-byte) */
+#define BPB_BytsPerSec		11		/* Sector size [byte] (WORD) */
+#define BPB_SecPerClus		13		/* Cluster size [sector] (BYTE) */
+#define BPB_RsvdSecCnt		14		/* Size of reserved area [sector] (WORD) */
+#define BPB_NumFATs			16		/* Number of FATs (BYTE) */
+#define BPB_RootEntCnt		17		/* Size of root directory area for FAT [entry] (WORD) */
+#define BPB_TotSec16		19		/* Volume size (16-bit) [sector] (WORD) */
+#define BPB_Media			21		/* Media descriptor byte (BYTE) */
+#define BPB_FATSz16			22		/* FAT size (16-bit) [sector] (WORD) */
+#define BPB_SecPerTrk		24		/* Number of sectors per track for int13h [sector] (WORD) */
+#define BPB_NumHeads		26		/* Number of heads for int13h (WORD) */
+#define BPB_HiddSec			28		/* Volume offset from top of the drive (DWORD) */
+#define BPB_TotSec32		32		/* Volume size (32-bit) [sector] (DWORD) */
+#define BS_DrvNum			36		/* Physical drive number for int13h (BYTE) */
+#define BS_NTres			37		/* WindowsNT error flag (BYTE) */
+#define BS_BootSig			38		/* Extended boot signature (BYTE) */
+#define BS_VolID			39		/* Volume serial number (DWORD) */
+#define BS_VolLab			43		/* Volume label string (8-byte) */
+#define BS_FilSysType		54		/* Filesystem type string (8-byte) */
+#define BS_BootCode			62		/* Boot code (448-byte) */
+#define BS_55AA				510		/* Signature word (WORD) */
+
+#define BPB_FATSz32			36		/* FAT32: FAT size [sector] (DWORD) */
+#define BPB_ExtFlags32		40		/* FAT32: Extended flags (WORD) */
+#define BPB_FSVer32			42		/* FAT32: Filesystem version (WORD) */
+#define BPB_RootClus32		44		/* FAT32: Root directory cluster (DWORD) */
+#define BPB_FSInfo32		48		/* FAT32: Offset of FSINFO sector (WORD) */
+#define BPB_BkBootSec32		50		/* FAT32: Offset of backup boot sector (WORD) */
+#define BS_DrvNum32			64		/* FAT32: Physical drive number for int13h (BYTE) */
+#define BS_NTres32			65		/* FAT32: Error flag (BYTE) */
+#define BS_BootSig32		66		/* FAT32: Extended boot signature (BYTE) */
+#define BS_VolID32			67		/* FAT32: Volume serial number (DWORD) */
+#define BS_VolLab32			71		/* FAT32: Volume label string (8-byte) */
+#define BS_FilSysType32		82		/* FAT32: Filesystem type string (8-byte) */
+#define BS_BootCode32		90		/* FAT32: Boot code (420-byte) */
+
+#define BPB_ZeroedEx		11		/* exFAT: MBZ field (53-byte) */
+#define BPB_VolOfsEx		64		/* exFAT: Volume offset from top of the drive [sector] (QWORD) */
+#define BPB_TotSecEx		72		/* exFAT: Volume size [sector] (QWORD) */
+#define BPB_FatOfsEx		80		/* exFAT: FAT offset from top of the volume [sector] (DWORD) */
+#define BPB_FatSzEx			84		/* exFAT: FAT size [sector] (DWORD) */
+#define BPB_DataOfsEx		88		/* exFAT: Data offset from top of the volume [sector] (DWORD) */
+#define BPB_NumClusEx		92		/* exFAT: Number of clusters (DWORD) */
+#define BPB_RootClusEx		96		/* exFAT: Root directory start cluster (DWORD) */
+#define BPB_VolIDEx			100		/* exFAT: Volume serial number (DWORD) */
+#define BPB_FSVerEx			104		/* exFAT: Filesystem version (WORD) */
+#define BPB_VolFlagEx		106		/* exFAT: Volume flags (BYTE) */
+#define BPB_ActFatEx		107		/* exFAT: Active FAT flags (BYTE) */
+#define BPB_BytsPerSecEx	108		/* exFAT: Log2 of sector size in unit of byte (BYTE) */
+#define BPB_SecPerClusEx	109		/* exFAT: Log2 of cluster size in unit of sector (BYTE) */
+#define BPB_NumFATsEx		110		/* exFAT: Number of FATs (BYTE) */
+#define BPB_DrvNumEx		111		/* exFAT: Physical drive number for int13h (BYTE) */
+#define BPB_PercInUseEx		112		/* exFAT: Percent in use (BYTE) */
+#define BPB_RsvdEx			113		/* exFAT: Reserved (7-byte) */
+#define BS_BootCodeEx		120		/* exFAT: Boot code (390-byte) */
+
+#define DIR_Name			0		/* Short file name (11-byte) */
+#define DIR_Attr			11		/* Attribute (BYTE) */
+#define DIR_NTres			12		/* Lower case flag (BYTE) */
+#define DIR_CrtTime10		13		/* Created time sub-second (BYTE) */
+#define DIR_CrtTime			14		/* Created time (DWORD) */
+#define DIR_LstAccDate		18		/* Last accessed date (WORD) */
+#define DIR_FstClusHI		20		/* Higher 16-bit of first cluster (WORD) */
+#define DIR_ModTime			22		/* Modified time (DWORD) */
+#define DIR_FstClusLO		26		/* Lower 16-bit of first cluster (WORD) */
+#define DIR_FileSize		28		/* File size (DWORD) */
+#define LDIR_Ord			0		/* LFN: LFN order and LLE flag (BYTE) */
+#define LDIR_Attr			11		/* LFN: LFN attribute (BYTE) */
+#define LDIR_Type			12		/* LFN: Entry type (BYTE) */
+#define LDIR_Chksum			13		/* LFN: Checksum of the SFN (BYTE) */
+#define LDIR_FstClusLO		26		/* LFN: MBZ field (WORD) */
+#define XDIR_Type			0		/* exFAT: Type of exFAT directory entry (BYTE) */
+#define XDIR_NumLabel		1		/* exFAT: Number of volume label characters (BYTE) */
+#define XDIR_Label			2		/* exFAT: Volume label (11-WORD) */
+#define XDIR_CaseSum		4		/* exFAT: Sum of case conversion table (DWORD) */
+#define XDIR_NumSec			1		/* exFAT: Number of secondary entries (BYTE) */
+#define XDIR_SetSum			2		/* exFAT: Sum of the set of directory entries (WORD) */
+#define XDIR_Attr			4		/* exFAT: File attribute (WORD) */
+#define XDIR_CrtTime		8		/* exFAT: Created time (DWORD) */
+#define XDIR_ModTime		12		/* exFAT: Modified time (DWORD) */
+#define XDIR_AccTime		16		/* exFAT: Last accessed time (DWORD) */
+#define XDIR_CrtTime10		20		/* exFAT: Created time subsecond (BYTE) */
+#define XDIR_ModTime10		21		/* exFAT: Modified time subsecond (BYTE) */
+#define XDIR_CrtTZ			22		/* exFAT: Created timezone (BYTE) */
+#define XDIR_ModTZ			23		/* exFAT: Modified timezone (BYTE) */
+#define XDIR_AccTZ			24		/* exFAT: Last accessed timezone (BYTE) */
+#define XDIR_GenFlags		33		/* exFAT: General secondary flags (BYTE) */
+#define XDIR_NumName		35		/* exFAT: Number of file name characters (BYTE) */
+#define XDIR_NameHash		36		/* exFAT: Hash of file name (WORD) */
+#define XDIR_ValidFileSize	40		/* exFAT: Valid file size (QWORD) */
+#define XDIR_FstClus		52		/* exFAT: First cluster of the file data (DWORD) */
+#define XDIR_FileSize		56		/* exFAT: File/Directory size (QWORD) */
+
+#define SZDIRE				32		/* Size of a directory entry */
+#define DDEM				0xE5	/* Deleted directory entry mark set to DIR_Name[0] */
+#define RDDEM				0x05	/* Replacement of the character collides with DDEM */
+#define LLEF				0x40	/* Last long entry flag in LDIR_Ord */
+
+#define FSI_LeadSig			0		/* FAT32 FSI: Leading signature (DWORD) */
+#define FSI_StrucSig		484		/* FAT32 FSI: Structure signature (DWORD) */
+#define FSI_Free_Count		488		/* FAT32 FSI: Number of free clusters (DWORD) */
+#define FSI_Nxt_Free		492		/* FAT32 FSI: Last allocated cluster (DWORD) */
+
+#define MBR_Table			446		/* MBR: Offset of partition table in the MBR */
+#define SZ_PTE				16		/* MBR: Size of a partition table entry */
+#define PTE_Boot			0		/* MBR PTE: Boot indicator */
+#define PTE_StHead			1		/* MBR PTE: Start head */
+#define PTE_StSec			2		/* MBR PTE: Start sector */
+#define PTE_StCyl			3		/* MBR PTE: Start cylinder */
+#define PTE_System			4		/* MBR PTE: System ID */
+#define PTE_EdHead			5		/* MBR PTE: End head */
+#define PTE_EdSec			6		/* MBR PTE: End sector */
+#define PTE_EdCyl			7		/* MBR PTE: End cylinder */
+#define PTE_StLba			8		/* MBR PTE: Start in LBA */
+#define PTE_SizLba			12		/* MBR PTE: Size in LBA */
+
+
+/* Post process after fatal error on file operation */
+#define ABORT(fs, res)		{ fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Reentrancy related */
+#if FF_FS_REENTRANT
+#if FF_USE_LFN == 1
+#error Static LFN work area cannot be used at thread-safe configuration
+#endif
+#define LEAVE_FF(fs, res)	{ unlock_fs(fs, res); return res; }
+#else
+#define LEAVE_FF(fs, res)	return res
+#endif
+
+
+/* Definitions of volume - partition conversion */
+#if FF_MULTI_PARTITION
+#define LD2PD(vol) VolToPart[vol].pd	/* Get physical drive number */
+#define LD2PT(vol) VolToPart[vol].pt	/* Get partition index */
+#else
+#define LD2PD(vol) (BYTE)(vol)	/* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0			/* Find first valid partition or in SFD */
+#endif
+
+
+/* Definitions of sector size */
+#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
+#error Wrong sector size configuration
+#endif
+#if FF_MAX_SS == FF_MIN_SS
+#define SS(fs)	((UINT)FF_MAX_SS)	/* Fixed sector size */
+#else
+#define SS(fs)	((fs)->ssize)	/* Variable sector size */
+#endif
+
+
+/* Timestamp */
+#if FF_FS_NORTC == 1
+#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
+#error Invalid FF_FS_NORTC settings
+#endif
+#define GET_FATTIME()	((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
+#else
+#define GET_FATTIME()	get_fattime()
+#endif
+
+
+/* File lock controls */
+#if FF_FS_LOCK != 0
+#if FF_FS_READONLY
+#error FF_FS_LOCK must be 0 at read-only configuration
+#endif
+typedef struct {
+	FATFS *fs;		/* Object ID 1, volume (NULL:blank entry) */
+	DWORD clu;		/* Object ID 2, containing directory (0:root) */
+	DWORD ofs;		/* Object ID 3, offset in the directory */
+	WORD ctr;		/* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+/* SBCS up-case tables (\x80-\xFF) */
+#define TBL_CT437  {0x80,0x9A,0x45,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, \
+					0x41,0x49,0x4F,0x55,0xA5,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, \
+					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, \
+					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}
+#define TBL_CT720  {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, \
+					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, \
+					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, \
+					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}
+#define TBL_CT737  {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, \
+					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, \
+					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, \
+					0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+					0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT771  {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, \
+					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, \
+					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,0xDC,0xDE,0xDE, \
+					0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+					0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
+#define TBL_CT775  {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, \
+					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, \
+					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, \
+					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}
+#define TBL_CT850  {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
+					0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
+					0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+					0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+					0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+					0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
+					0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
+					0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT852  {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,0xAC, \
+					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, \
+					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, \
+					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}
+#define TBL_CT855  {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, \
+					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, \
+					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, \
+					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}
+#define TBL_CT857  {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
+					0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+					0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+					0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+					0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+					0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+					0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
+					0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT860  {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
+					0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+					0x86,0x8B,0x9F,0x96,0xA5,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, \
+					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, \
+					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}
+#define TBL_CT861  {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
+					0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+					0xA4,0xA5,0xA6,0xA7,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, \
+					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, \
+					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}
+#define TBL_CT862  {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, \
+					0x41,0x49,0x4F,0x55,0xA5,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, \
+					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, \
+					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}
+#define TBL_CT863  {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
+					0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
+					0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+					0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+					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, \
+					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}
+#define TBL_CT864  {0x80,0x9A,0x45,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, \
+					0x41,0x49,0x4F,0x55,0xA5,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, \
+					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, \
+					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}
+#define TBL_CT865  {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, \
+					0x41,0x49,0x4F,0x55,0xA5,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, \
+					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, \
+					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}
+#define TBL_CT866  {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, \
+					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, \
+					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, \
+					0x90,0x91,0x92,0x93,0x94,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}
+#define TBL_CT869  {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,0x86,0x9C,0x8D,0x8F,0x90, \
+					0x91,0x90,0x92,0x95,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, \
+					0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+					0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
+					0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
+					0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
+
+
+/* DBCS code range |----- 1st byte -----|  |----------- 2nd byte -----------| */
+#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
+#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
+#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
+#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
+
+
+/* Macros for table definitions */
+#define MERGE_2STR(a, b) a ## b
+#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Work Area
+
+---------------------------------------------------------------------------*/
+/* Remark: Variables defined here without initial value shall be guaranteed
+/  zero/null at start-up. If not, the linker option or start-up routine is
+/  not compliance with C standard. */
+
+/*--------------------------------*/
+/* File/Volume controls           */
+/*--------------------------------*/
+
+#if FF_VOLUMES < 1 || FF_VOLUMES > 10
+#error Wrong FF_VOLUMES setting
+#endif
+static FATFS *FatFs[FF_VOLUMES];	/* Pointer to the filesystem objects (logical drives) */
+static WORD Fsid;					/* File system mount ID */
+
+#if FF_FS_RPATH != 0 && FF_VOLUMES >= 2
+static BYTE CurrVol;				/* Current drive */
+#endif
+
+#if FF_FS_LOCK != 0
+static FILESEM Files[FF_FS_LOCK];	/* Open object lock semaphores */
+#endif
+
+
+/*--------------------------------*/
+/* LFN/Directory working buffer   */
+/*--------------------------------*/
+
+#if FF_USE_LFN == 0		/* Non-LFN configuration */
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+
+#else					/* LFN configurations */
+#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
+#error Wrong FF_MAX_LFN setting
+#endif
+static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};	/* FAT: Offset of LFN characters in the directory entry */
+#define MAXDIRB(nc)	((nc + 44U) / 15 * SZDIRE)	/* exFAT: Size of directory entry block scratchpad buffer needed for the max name length */
+
+#if FF_USE_LFN == 1		/* LFN enabled with static working buffer */
+#if FF_FS_EXFAT
+static BYTE	DirBuf[MAXDIRB(FF_MAX_LFN)];	/* Directory entry block scratchpad buffer */
+#endif
+static WCHAR LfnBuf[FF_MAX_LFN + 1];		/* LFN working buffer */
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+
+#elif FF_USE_LFN == 2 	/* LFN enabled with dynamic working buffer on the stack */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF		WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)];	/* LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs)	{ (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
+#define FREE_NAMBUF()
+#else
+#define DEF_NAMBUF		WCHAR lbuf[FF_MAX_LFN+1];	/* LFN working buffer */
+#define INIT_NAMBUF(fs)	{ (fs)->lfnbuf = lbuf; }
+#define FREE_NAMBUF()
+#endif
+
+#elif FF_USE_LFN == 3 	/* LFN enabled with dynamic working buffer on the heap */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF		WCHAR *lfn;	/* Pointer to LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs)	{ lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
+#define FREE_NAMBUF()	ff_memfree(lfn)
+#else
+#define DEF_NAMBUF		WCHAR *lfn;	/* Pointer to LFN working buffer */
+#define INIT_NAMBUF(fs)	{ lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
+#define FREE_NAMBUF()	ff_memfree(lfn)
+#endif
+
+#else
+#error Wrong FF_USE_LFN setting
+
+#endif
+#endif
+
+
+/*--------------------------------*/
+/* Code conversion tables         */
+/*--------------------------------*/
+
+#if FF_CODE_PAGE == 0		/* Run-time code page configuration */
+#define CODEPAGE CodePage
+static WORD CodePage;	/* Current code page */
+static const BYTE *ExCvt, *DbcTbl;	/* Pointer to current SBCS up-case table and DBCS code range table below */
+static const BYTE Ct437[] = TBL_CT437;
+static const BYTE Ct720[] = TBL_CT720;
+static const BYTE Ct737[] = TBL_CT737;
+static const BYTE Ct771[] = TBL_CT771;
+static const BYTE Ct775[] = TBL_CT775;
+static const BYTE Ct850[] = TBL_CT850;
+static const BYTE Ct852[] = TBL_CT852;
+static const BYTE Ct855[] = TBL_CT855;
+static const BYTE Ct857[] = TBL_CT857;
+static const BYTE Ct860[] = TBL_CT860;
+static const BYTE Ct861[] = TBL_CT861;
+static const BYTE Ct862[] = TBL_CT862;
+static const BYTE Ct863[] = TBL_CT863;
+static const BYTE Ct864[] = TBL_CT864;
+static const BYTE Ct865[] = TBL_CT865;
+static const BYTE Ct866[] = TBL_CT866;
+static const BYTE Ct869[] = TBL_CT869;
+static const BYTE Dc932[] = TBL_DC932;
+static const BYTE Dc936[] = TBL_DC936;
+static const BYTE Dc949[] = TBL_DC949;
+static const BYTE Dc950[] = TBL_DC950;
+
+#elif FF_CODE_PAGE < 900	/* Static code page configuration (SBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE);
+
+#else					/* Static code page configuration (DBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE);
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* Load/Store multi-byte word in the FAT structure                       */
+/*-----------------------------------------------------------------------*/
+
+static
+WORD ld_word (const BYTE* ptr)	/*	 Load a 2-byte little-endian word */
+{
+	WORD rv;
+
+	rv = ptr[1];
+	rv = rv << 8 | ptr[0];
+	return rv;
+}
+
+static
+DWORD ld_dword (const BYTE* ptr)	/* Load a 4-byte little-endian word */
+{
+	DWORD rv;
+
+	rv = ptr[3];
+	rv = rv << 8 | ptr[2];
+	rv = rv << 8 | ptr[1];
+	rv = rv << 8 | ptr[0];
+	return rv;
+}
+
+#if FF_FS_EXFAT
+static
+QWORD ld_qword (const BYTE* ptr)	/* Load an 8-byte little-endian word */
+{
+	QWORD rv;
+
+	rv = ptr[7];
+	rv = rv << 8 | ptr[6];
+	rv = rv << 8 | ptr[5];
+	rv = rv << 8 | ptr[4];
+	rv = rv << 8 | ptr[3];
+	rv = rv << 8 | ptr[2];
+	rv = rv << 8 | ptr[1];
+	rv = rv << 8 | ptr[0];
+	return rv;
+}
+#endif
+
+#if !FF_FS_READONLY
+static
+void st_word (BYTE* ptr, WORD val)	/* Store a 2-byte word in little-endian */
+{
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val;
+}
+
+static
+void st_dword (BYTE* ptr, DWORD val)	/* Store a 4-byte word in little-endian */
+{
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val;
+}
+
+#if FF_FS_EXFAT
+static
+void st_qword (BYTE* ptr, QWORD val)	/* Store an 8-byte word in little-endian */
+{
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val; val >>= 8;
+	*ptr++ = (BYTE)val;
+}
+#endif
+#endif	/* !FF_FS_READONLY */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, UINT cnt)
+{
+	BYTE *d = (BYTE*)dst;
+	const BYTE *s = (const BYTE*)src;
+
+	if (cnt != 0) {
+		do {
+			*d++ = *s++;
+		} while (--cnt);
+	}
+}
+
+
+/* Fill memory block */
+static
+void mem_set (void* dst, int val, UINT cnt)
+{
+	BYTE *d = (BYTE*)dst;
+
+	do {
+		*d++ = (BYTE)val;
+	} while (--cnt);
+}
+
+
+/* Compare memory block */
+static
+int mem_cmp (const void* dst, const void* src, UINT cnt)	/* ZR:same, NZ:different */
+{
+	const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+	int r = 0;
+
+	do {
+		r = *d++ - *s++;
+	} while (--cnt && r == 0);
+
+	return r;
+}
+
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr)	/* NZ:contained, ZR:not contained */
+{
+	while (*str && *str != chr) str++;
+	return *str;
+}
+
+
+/* Test if the character is DBC 1st byte */
+static
+int dbc_1st (BYTE c)
+{
+#if FF_CODE_PAGE == 0		/* Variable code page */
+	if (DbcTbl && c >= DbcTbl[0]) {
+		if (c <= DbcTbl[1]) return 1;					/* 1st byte range 1 */
+		if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;	/* 1st byte range 2 */
+	}
+#elif FF_CODE_PAGE >= 900	/* DBCS fixed code page */
+	if (c >= DbcTbl[0]) {
+		if (c <= DbcTbl[1]) return 1;
+		if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;
+	}
+#else					/* SBCS fixed code page */
+	if (c) return 0;	/* Always false */
+#endif
+	return 0;
+}
+
+
+/* Test if the character is DBC 2nd byte */
+static
+int dbc_2nd (BYTE c)
+{
+#if FF_CODE_PAGE == 0		/* Variable code page */
+	if (DbcTbl && c >= DbcTbl[4]) {
+		if (c <= DbcTbl[5]) return 1;					/* 2nd byte range 1 */
+		if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;	/* 2nd byte range 2 */
+		if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;	/* 2nd byte range 3 */
+	}
+#elif FF_CODE_PAGE >= 900	/* DBCD fixed code page */
+	if (c >= DbcTbl[4]) {
+		if (c <= DbcTbl[5]) return 1;
+		if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;
+		if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;
+	}
+#else					/* SBCS fixed code page */
+	if (c) return 0;	/* Always false */
+#endif
+	return 0;
+}
+
+
+
+#if FF_FS_REENTRANT
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+static
+int lock_fs (		/* 1:Ok, 0:timeout */
+	FATFS* fs		/* Filesystem object */
+)
+{
+	return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+	FATFS* fs,		/* Filesystem object */
+	FRESULT res		/* Result code to be returned */
+)
+{
+	if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
+		ff_rel_grant(fs->sobj);
+	}
+}
+
+#endif
+
+
+
+#if FF_FS_LOCK != 0
+/*-----------------------------------------------------------------------*/
+/* File lock control functions                                           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT chk_lock (	/* Check if the file can be accessed */
+	DIR* dp,		/* Directory object pointing the file to be checked */
+	int acc			/* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
+)
+{
+	UINT i, be;
+
+	/* Search open object table for the object */
+	be = 0;
+	for (i = 0; i < FF_FS_LOCK; i++) {
+		if (Files[i].fs) {	/* Existing entry */
+			if (Files[i].fs == dp->obj.fs &&	 	/* Check if the object matches with an open object */
+				Files[i].clu == dp->obj.sclust &&
+				Files[i].ofs == dp->dptr) break;
+		} else {			/* Blank entry */
+			be = 1;
+		}
+	}
+	if (i == FF_FS_LOCK) {	/* The object has not been opened */
+		return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK;	/* Is there a blank entry for new object? */
+	}
+
+	/* The object was opened. Reject any open against writing file and all write mode open */
+	return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock (void)	/* Check if an entry is available for a new object */
+{
+	UINT i;
+
+	for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+	return (i == FF_FS_LOCK) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock (	/* Increment object open counter and returns its index (0:Internal error) */
+	DIR* dp,	/* Directory object pointing the file to register or increment */
+	int acc		/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+	UINT i;
+
+
+	for (i = 0; i < FF_FS_LOCK; i++) {	/* Find the object */
+		if (Files[i].fs == dp->obj.fs &&
+			Files[i].clu == dp->obj.sclust &&
+			Files[i].ofs == dp->dptr) break;
+	}
+
+	if (i == FF_FS_LOCK) {				/* Not opened. Register it as new. */
+		for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+		if (i == FF_FS_LOCK) return 0;	/* No free entry to register (int err) */
+		Files[i].fs = dp->obj.fs;
+		Files[i].clu = dp->obj.sclust;
+		Files[i].ofs = dp->dptr;
+		Files[i].ctr = 0;
+	}
+
+	if (acc && Files[i].ctr) return 0;	/* Access violation (int err) */
+
+	Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;	/* Set semaphore value */
+
+	return i + 1;	/* Index number origin from 1 */
+}
+
+
+static
+FRESULT dec_lock (	/* Decrement object open counter */
+	UINT i			/* Semaphore index (1..) */
+)
+{
+	WORD n;
+	FRESULT res;
+
+
+	if (--i < FF_FS_LOCK) {	/* Index number origin from 0 */
+		n = Files[i].ctr;
+		if (n == 0x100) n = 0;		/* If write mode open, delete the entry */
+		if (n > 0) n--;				/* Decrement read mode open count */
+		Files[i].ctr = n;
+		if (n == 0) Files[i].fs = 0;	/* Delete the entry if open count gets zero */
+		res = FR_OK;
+	} else {
+		res = FR_INT_ERR;			/* Invalid index nunber */
+	}
+	return res;
+}
+
+
+static
+void clear_lock (	/* Clear lock entries of the volume */
+	FATFS *fs
+)
+{
+	UINT i;
+
+	for (i = 0; i < FF_FS_LOCK; i++) {
+		if (Files[i].fs == fs) Files[i].fs = 0;
+	}
+}
+
+#endif	/* FF_FS_LOCK != 0 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move/Flush disk access window in the filesystem object                */
+/*-----------------------------------------------------------------------*/
+#if !FF_FS_READONLY
+static
+FRESULT sync_window (	/* Returns FR_OK or FR_DISK_ERR */
+	FATFS* fs			/* Filesystem object */
+)
+{
+	FRESULT res = FR_OK;
+
+
+	if (fs->wflag) {	/* Is the disk access window dirty */
+		if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) {	/* Write back the window */
+			fs->wflag = 0;	/* Clear window dirty flag */
+			if (fs->winsect - fs->fatbase < fs->fsize) {	/* Is it in the 1st FAT? */
+				if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1);	/* Reflect it to 2nd FAT if needed */
+			}
+		} else {
+			res = FR_DISK_ERR;
+		}
+	}
+	return res;
+}
+#endif
+
+
+static
+FRESULT move_window (	/* Returns FR_OK or FR_DISK_ERR */
+	FATFS* fs,			/* Filesystem object */
+	DWORD sector		/* Sector number to make appearance in the fs->win[] */
+)
+{
+	FRESULT res = FR_OK;
+
+
+	if (sector != fs->winsect) {	/* Window offset changed? */
+#if !FF_FS_READONLY
+		res = sync_window(fs);		/* Write-back changes */
+#endif
+		if (res == FR_OK) {			/* Fill sector window with new data */
+			if (disk_read(fs->pdrv, fs->win, sector, 1) != RES_OK) {
+				sector = 0xFFFFFFFF;	/* Invalidate window if read data is not valid */
+				res = FR_DISK_ERR;
+			}
+			fs->winsect = sector;
+		}
+	}
+	return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Synchronize filesystem and data on the storage                        */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT sync_fs (	/* Returns FR_OK or FR_DISK_ERR */
+	FATFS* fs		/* Filesystem object */
+)
+{
+	FRESULT res;
+
+
+	res = sync_window(fs);
+	if (res == FR_OK) {
+		if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {	/* FAT32: Update FSInfo sector if needed */
+			/* Create FSInfo structure */
+			mem_set(fs->win, 0, SS(fs));
+			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_clst);
+			st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
+			/* Write it into the FSInfo sector */
+			fs->winsect = fs->volbase + 1;
+			disk_write(fs->pdrv, fs->win, fs->winsect, 1);
+			fs->fsi_flag = 0;
+		}
+		/* Make sure that no pending write process in the lower layer */
+		if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
+	}
+
+	return res;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get physical sector number from cluster number                        */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD clst2sect (	/* !=0:Sector number, 0:Failed (invalid cluster#) */
+	FATFS* fs,		/* Filesystem object */
+	DWORD clst		/* Cluster# to be converted */
+)
+{
+	clst -= 2;		/* Cluster number is origin from 2 */
+	if (clst >= fs->n_fatent - 2) return 0;		/* Is it invalid cluster number? */
+	return fs->database + fs->csize * clst;		/* Start sector number of the cluster */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD get_fat (		/* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
+	FFOBJID* obj,	/* Corresponding object */
+	DWORD clst		/* Cluster number to get the value */
+)
+{
+	UINT wc, bc;
+	DWORD val;
+	FATFS *fs = obj->fs;
+
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check if in valid range */
+		val = 1;	/* Internal error */
+
+	} else {
+		val = 0xFFFFFFFF;	/* Default value falls on disk error */
+
+		switch (fs->fs_type) {
+		case FS_FAT12 :
+			bc = (UINT)clst; bc += bc / 2;
+			if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+			wc = fs->win[bc++ % SS(fs)];		/* Get 1st byte of the entry */
+			if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+			wc |= fs->win[bc % SS(fs)] << 8;	/* Merge 2nd byte of the entry */
+			val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF);	/* Adjust bit position */
+			break;
+
+		case FS_FAT16 :
+			if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
+			val = ld_word(fs->win + clst * 2 % SS(fs));		/* Simple WORD array */
+			break;
+
+		case FS_FAT32 :
+			if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+			val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF;	/* Simple DWORD array but mask out upper 4 bits */
+			break;
+#if FF_FS_EXFAT
+		case FS_EXFAT :
+			if (obj->objsize != 0) {
+				DWORD cofs = clst - obj->sclust;	/* Offset from start cluster */
+				DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize;	/* Number of clusters - 1 */
+
+				if (obj->stat == 2 && cofs <= clen) {	/* Is it a contiguous chain? */
+					val = (cofs == clen) ? 0x7FFFFFFF : clst + 1;	/* No data on the FAT, generate the value */
+					break;
+				}
+				if (obj->stat == 3 && cofs < obj->n_cont) {	/* Is it in the 1st fragment? */
+					val = clst + 1; 	/* Generate the value */
+					break;
+				}
+				if (obj->stat != 2) {	/* Get value from FAT if FAT chain is valid */
+					if (obj->n_frag != 0) {	/* Is it on the growing edge? */
+						val = 0x7FFFFFFF;	/* Generate EOC */
+					} else {
+						if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+						val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;
+					}
+					break;
+				}
+			}
+			/* go to default */
+#endif
+		default:
+			val = 1;	/* Internal error */
+		}
+	}
+
+	return val;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT put_fat (	/* FR_OK(0):succeeded, !=0:error */
+	FATFS* fs,		/* Corresponding filesystem object */
+	DWORD clst,		/* FAT index number (cluster number) to be changed */
+	DWORD val		/* New value to be set to the entry */
+)
+{
+	UINT bc;
+	BYTE *p;
+	FRESULT res = FR_INT_ERR;
+
+
+	if (clst >= 2 && clst < fs->n_fatent) {	/* Check if in valid range */
+		switch (fs->fs_type) {
+		case FS_FAT12 :
+			bc = (UINT)clst; bc += bc / 2;	/* bc: byte offset of the entry */
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = fs->win + bc++ % SS(fs);
+			*p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;		/* Put 1st byte */
+			fs->wflag = 1;
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = fs->win + bc % SS(fs);
+			*p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));	/* Put 2nd byte */
+			fs->wflag = 1;
+			break;
+
+		case FS_FAT16 :
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+			if (res != FR_OK) break;
+			st_word(fs->win + clst * 2 % SS(fs), (WORD)val);	/* Simple WORD array */
+			fs->wflag = 1;
+			break;
+
+		case FS_FAT32 :
+#if FF_FS_EXFAT
+		case FS_EXFAT :
+#endif
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+			if (res != FR_OK) break;
+			if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+				val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000);
+			}
+			st_dword(fs->win + clst * 4 % SS(fs), val);
+			fs->wflag = 1;
+			break;
+		}
+	}
+	return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_FS_EXFAT && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* exFAT: Accessing FAT and Allocation Bitmap                            */
+/*-----------------------------------------------------------------------*/
+
+/*--------------------------------------*/
+/* Find a contiguous free cluster block */
+/*--------------------------------------*/
+
+static
+DWORD find_bitmap (	/* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
+	FATFS* fs,	/* Filesystem object */
+	DWORD clst,	/* Cluster number to scan from */
+	DWORD ncl	/* Number of contiguous clusters to find (1..) */
+)
+{
+	BYTE bm, bv;
+	UINT i;
+	DWORD val, scl, ctr;
+
+
+	clst -= 2;	/* The first bit in the bitmap corresponds to cluster #2 */
+	if (clst >= fs->n_fatent - 2) clst = 0;
+	scl = val = clst; ctr = 0;
+	for (;;) {
+		if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;	/* (assuming bitmap is located top of the cluster heap) */
+		i = val / 8 % SS(fs); bm = 1 << (val % 8);
+		do {
+			do {
+				bv = fs->win[i] & bm; bm <<= 1;		/* Get bit value */
+				if (++val >= fs->n_fatent - 2) {	/* Next cluster (with wrap-around) */
+					val = 0; bm = 0; i = SS(fs);
+				}
+				if (!bv) {	/* Is it a free cluster? */
+					if (++ctr == ncl) return scl + 2;	/* Check if run length is sufficient for required */
+				} else {
+					scl = val; ctr = 0;		/* Encountered a cluster in-use, restart to scan */
+				}
+				if (val == clst) return 0;	/* All cluster scanned? */
+			} while (bm);
+			bm = 1;
+		} while (++i < SS(fs));
+	}
+}
+
+
+/*----------------------------------------*/
+/* Set/Clear a block of allocation bitmap */
+/*----------------------------------------*/
+
+static
+FRESULT change_bitmap (
+	FATFS* fs,	/* Filesystem object */
+	DWORD clst,	/* Cluster number to change from */
+	DWORD ncl,	/* Number of clusters to be changed */
+	int bv		/* bit value to be set (0 or 1) */
+)
+{
+	BYTE bm;
+	UINT i;
+	DWORD sect;
+
+
+	clst -= 2;	/* The first bit corresponds to cluster #2 */
+	sect = fs->database + clst / 8 / SS(fs);	/* Sector address (assuming bitmap is located top of the cluster heap) */
+	i = clst / 8 % SS(fs);						/* Byte offset in the sector */
+	bm = 1 << (clst % 8);						/* Bit mask in the byte */
+	for (;;) {
+		if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;
+		do {
+			do {
+				if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR;	/* Is the bit expected value? */
+				fs->win[i] ^= bm;	/* Flip the bit */
+				fs->wflag = 1;
+				if (--ncl == 0) return FR_OK;	/* All bits processed? */
+			} while (bm <<= 1);		/* Next bit */
+			bm = 1;
+		} while (++i < SS(fs));		/* Next byte */
+		i = 0;
+	}
+}
+
+
+/*---------------------------------------------*/
+/* Fill the first fragment of the FAT chain    */
+/*---------------------------------------------*/
+
+static
+FRESULT fill_first_frag (
+	FFOBJID* obj	/* Pointer to the corresponding object */
+)
+{
+	FRESULT res;
+	DWORD cl, n;
+
+
+	if (obj->stat == 3) {	/* Has the object been changed 'fragmented' in this session? */
+		for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) {	/* Create cluster chain on the FAT */
+			res = put_fat(obj->fs, cl, cl + 1);
+			if (res != FR_OK) return res;
+		}
+		obj->stat = 0;	/* Change status 'FAT chain is valid' */
+	}
+	return FR_OK;
+}
+
+
+/*---------------------------------------------*/
+/* Fill the last fragment of the FAT chain     */
+/*---------------------------------------------*/
+
+static
+FRESULT fill_last_frag (
+	FFOBJID* obj,	/* Pointer to the corresponding object */
+	DWORD lcl,		/* Last cluster of the fragment */
+	DWORD term		/* Value to set the last FAT entry */
+)
+{
+	FRESULT res;
+
+
+	while (obj->n_frag > 0) {	/* Create the last chain on the FAT */
+		res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);
+		if (res != FR_OK) return res;
+		obj->n_frag--;
+	}
+	return FR_OK;
+}
+
+#endif	/* FF_FS_EXFAT && !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+static
+FRESULT remove_chain (	/* FR_OK(0):succeeded, !=0:error */
+	FFOBJID* obj,		/* Corresponding object */
+	DWORD clst,			/* Cluster to remove a chain from */
+	DWORD pclst			/* Previous cluster of clst (0:entire chain) */
+)
+{
+	FRESULT res = FR_OK;
+	DWORD nxt;
+	FATFS *fs = obj->fs;
+#if FF_FS_EXFAT || FF_USE_TRIM
+	DWORD scl = clst, ecl = clst;
+#endif
+#if FF_USE_TRIM
+	DWORD rt[2];
+#endif
+
+	if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;	/* Check if in valid range */
+
+	/* Mark the previous cluster 'EOC' on the FAT if it exists */
+	if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {
+		res = put_fat(fs, pclst, 0xFFFFFFFF);
+		if (res != FR_OK) return res;
+	}
+
+	/* Remove the chain */
+	do {
+		nxt = get_fat(obj, clst);			/* Get cluster status */
+		if (nxt == 0) break;				/* Empty cluster? */
+		if (nxt == 1) return FR_INT_ERR;	/* Internal error? */
+		if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error? */
+		if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+			res = put_fat(fs, clst, 0);		/* Mark the cluster 'free' on the FAT */
+			if (res != FR_OK) return res;
+		}
+		if (fs->free_clst < fs->n_fatent - 2) {	/* Update FSINFO */
+			fs->free_clst++;
+			fs->fsi_flag |= 1;
+		}
+#if FF_FS_EXFAT || FF_USE_TRIM
+		if (ecl + 1 == nxt) {	/* Is next cluster contiguous? */
+			ecl = nxt;
+		} else {				/* End of contiguous cluster block */
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {
+				res = change_bitmap(fs, scl, ecl - scl + 1, 0);	/* Mark the cluster block 'free' on the bitmap */
+				if (res != FR_OK) return res;
+			}
+#endif
+#if FF_USE_TRIM
+			rt[0] = clst2sect(fs, scl);					/* Start of data area freed */
+			rt[1] = clst2sect(fs, ecl) + fs->csize - 1;	/* End of data area freed */
+			disk_ioctl(fs->pdrv, CTRL_TRIM, rt);		/* Inform device the data in the block is no longer needed */
+#endif
+			scl = ecl = nxt;
+		}
+#endif
+		clst = nxt;					/* Next cluster */
+	} while (clst < fs->n_fatent);	/* Repeat while not the last link */
+
+#if FF_FS_EXFAT
+	/* Some post processes for chain status */
+	if (fs->fs_type == FS_EXFAT) {
+		if (pclst == 0) {	/* Has the entire chain been removed? */
+			obj->stat = 0;		/* Change the chain status 'initial' */
+		} else {
+			if (obj->stat == 0) {	/* Is it a fragmented chain from the beginning of this session? */
+				clst = obj->sclust;		/* Follow the chain to check if it gets contiguous */
+				while (clst != pclst) {
+					nxt = get_fat(obj, clst);
+					if (nxt < 2) return FR_INT_ERR;
+					if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;
+					if (nxt != clst + 1) break;	/* Not contiguous? */
+					clst++;
+				}
+				if (clst == pclst) {	/* Has the chain got contiguous again? */
+					obj->stat = 2;		/* Change the chain status 'contiguous' */
+				}
+			} else {
+				if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) {	/* Was the chain fragmented in this session and got contiguous again? */
+					obj->stat = 2;	/* Change the chain status 'contiguous' */
+				}
+			}
+		}
+	}
+#endif
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch a chain or Create a new chain                  */
+/*-----------------------------------------------------------------------*/
+static
+DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+	FFOBJID* obj,		/* Corresponding object */
+	DWORD clst			/* Cluster# to stretch, 0:Create a new chain */
+)
+{
+	DWORD cs, ncl, scl;
+	FRESULT res;
+	FATFS *fs = obj->fs;
+
+
+	if (clst == 0) {	/* Create a new chain */
+		scl = fs->last_clst;				/* Suggested cluster to start to find */
+		if (scl == 0 || scl >= fs->n_fatent) scl = 1;
+	}
+	else {				/* Stretch a chain */
+		cs = get_fat(obj, clst);			/* Check the cluster status */
+		if (cs < 2) return 1;				/* Test for insanity */
+		if (cs == 0xFFFFFFFF) return cs;	/* Test for disk error */
+		if (cs < fs->n_fatent) return cs;	/* It is already followed by next cluster */
+		scl = clst;							/* Cluster to start to find */
+	}
+	if (fs->free_clst == 0) return 0;		/* No free cluster */
+
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+		ncl = find_bitmap(fs, scl, 1);				/* Find a free cluster */
+		if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl;	/* No free cluster or hard error? */
+		res = change_bitmap(fs, ncl, 1, 1);			/* Mark the cluster 'in use' */
+		if (res == FR_INT_ERR) return 1;
+		if (res == FR_DISK_ERR) return 0xFFFFFFFF;
+		if (clst == 0) {							/* Is it a new chain? */
+			obj->stat = 2;							/* Set status 'contiguous' */
+		} else {									/* It is a stretched chain */
+			if (obj->stat == 2 && ncl != scl + 1) {	/* Is the chain got fragmented? */
+				obj->n_cont = scl - obj->sclust;	/* Set size of the contiguous part */
+				obj->stat = 3;						/* Change status 'just fragmented' */
+			}
+		}
+		if (obj->stat != 2) {	/* Is the file non-contiguous? */
+			if (ncl == clst + 1) {	/* Is the cluster next to previous one? */
+				obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2;	/* Increment size of last framgent */
+			} else {				/* New fragment */
+				if (obj->n_frag == 0) obj->n_frag = 1;
+				res = fill_last_frag(obj, clst, ncl);	/* Fill last fragment on the FAT and link it to new one */
+				if (res == FR_OK) obj->n_frag = 1;
+			}
+		}
+	} else
+#endif
+	{	/* On the FAT/FAT32 volume */
+		ncl = 0;
+		if (scl == clst) {						/* Stretching an existing chain? */
+			ncl = scl + 1;						/* Test if next cluster is free */
+			if (ncl >= fs->n_fatent) ncl = 2;
+			cs = get_fat(obj, ncl);				/* Get next cluster status */
+			if (cs == 1 || cs == 0xFFFFFFFF) return cs;	/* Test for error */
+			if (cs != 0) {						/* Not free? */
+				cs = fs->last_clst;				/* Start at suggested cluster if it is valid */
+				if (cs >= 2 && cs < fs->n_fatent) scl = cs;
+				ncl = 0;
+			}
+		}
+		if (ncl == 0) {	/* The new cluster cannot be contiguous and find another fragment */
+			ncl = scl;	/* Start cluster */
+			for (;;) {
+				ncl++;							/* Next cluster */
+				if (ncl >= fs->n_fatent) {		/* Check wrap-around */
+					ncl = 2;
+					if (ncl > scl) return 0;	/* No free cluster found? */
+				}
+				cs = get_fat(obj, ncl);			/* Get the cluster status */
+				if (cs == 0) break;				/* Found a free cluster? */
+				if (cs == 1 || cs == 0xFFFFFFFF) return cs;	/* Test for error */
+				if (ncl == scl) return 0;		/* No free cluster found? */
+			}
+		}
+		res = put_fat(fs, ncl, 0xFFFFFFFF);		/* Mark the new cluster 'EOC' */
+		if (res == FR_OK && clst != 0) {
+			res = put_fat(fs, clst, ncl);		/* Link it from the previous one if needed */
+		}
+	}
+
+	if (res == FR_OK) {			/* Update FSINFO if function succeeded. */
+		fs->last_clst = ncl;
+		if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;
+		fs->fsi_flag |= 1;
+	} else {
+		ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;	/* Failed. Generate error status */
+	}
+
+	return ncl;		/* Return new cluster number or error status */
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD clmt_clust (	/* <2:Error, >=2:Cluster number */
+	FIL* fp,		/* Pointer to the file object */
+	FSIZE_t ofs		/* File offset to be converted to cluster# */
+)
+{
+	DWORD cl, ncl, *tbl;
+	FATFS *fs = fp->obj.fs;
+
+
+	tbl = fp->cltbl + 1;	/* Top of CLMT */
+	cl = (DWORD)(ofs / SS(fs) / fs->csize);	/* Cluster order from top of the file */
+	for (;;) {
+		ncl = *tbl++;			/* Number of cluters in the fragment */
+		if (ncl == 0) return 0;	/* End of table? (error) */
+		if (cl < ncl) break;	/* In this fragment? */
+		cl -= ncl; tbl++;		/* Next fragment */
+	}
+	return cl + *tbl;	/* Return the cluster number */
+}
+
+#endif	/* FF_USE_FASTSEEK */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Fill a cluster with zeros                        */
+/*-----------------------------------------------------------------------*/
+
+#if !FF_FS_READONLY
+static
+FRESULT dir_clear (	/* Returns FR_OK or FR_DISK_ERR */
+	FATFS *fs,		/* Filesystem object */
+	DWORD clst		/* Directory table to clear */
+)
+{
+	DWORD sect;
+	UINT n, szb;
+	BYTE *ibuf;
+
+
+	if (sync_window(fs) != FR_OK) return FR_DISK_ERR;	/* Flush disk access window */
+	sect = clst2sect(fs, clst);		/* Top of the cluster */
+	fs->winsect = sect;				/* Set window to top of the cluster */
+	mem_set(fs->win, 0, SS(fs));	/* Clear window buffer */
+#if FF_USE_LFN == 3		/* Quick table clear by using multi-secter write */
+	/* Allocate a temporary buffer (32 KB max) */
+	for (szb = ((DWORD)fs->csize * SS(fs) >= 0x8000) ? 0x8000 : fs->csize * SS(fs); szb > SS(fs) && !(ibuf = ff_memalloc(szb)); szb /= 2) ;
+	if (szb > SS(fs)) {		/* Buffer allocated? */
+		mem_set(ibuf, 0, szb);
+		szb /= SS(fs);		/* Bytes -> Sectors */
+		for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ;	/* Fill the cluster with 0 */
+		ff_memfree(ibuf);
+	} else
+#endif
+	{
+		ibuf = fs->win; szb = 1;	/* Use window buffer (single-sector writes may take a time) */
+		for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ;	/* Fill the cluster with 0 */
+	}
+	return (n == fs->csize) ? FR_OK : FR_DISK_ERR;
+}
+#endif	/* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (	/* FR_OK(0):succeeded, !=0:error */
+	DIR* dp,		/* Pointer to directory object */
+	DWORD ofs		/* Offset of directory table */
+)
+{
+	DWORD csz, clst;
+	FATFS *fs = dp->obj.fs;
+
+
+	if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) {	/* Check range of offset and alignment */
+		return FR_INT_ERR;
+	}
+	dp->dptr = ofs;				/* Set current offset */
+	clst = dp->obj.sclust;		/* Table start cluster (0:root) */
+	if (clst == 0 && fs->fs_type >= FS_FAT32) {	/* Replace cluster# 0 with root cluster# */
+		clst = fs->dirbase;
+		if (FF_FS_EXFAT) dp->obj.stat = 0;	/* exFAT: Root dir has an FAT chain */
+	}
+
+	if (clst == 0) {	/* Static table (root-directory on the FAT volume) */
+		if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR;	/* Is index out of range? */
+		dp->sect = fs->dirbase;
+
+	} else {			/* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
+		csz = (DWORD)fs->csize * SS(fs);	/* Bytes per cluster */
+		while (ofs >= csz) {				/* Follow cluster chain */
+			clst = get_fat(&dp->obj, clst);				/* Get next cluster */
+			if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
+			if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;	/* Reached to end of table or internal error */
+			ofs -= csz;
+		}
+		dp->sect = clst2sect(fs, clst);
+	}
+	dp->clust = clst;					/* Current cluster# */
+	if (dp->sect == 0) return FR_INT_ERR;
+	dp->sect += ofs / SS(fs);			/* Sector# of the directory entry */
+	dp->dir = fs->win + (ofs % SS(fs));	/* Pointer to the entry in the win[] */
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (	/* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
+	DIR* dp,		/* Pointer to the directory object */
+	int stretch		/* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+	DWORD ofs, clst;
+	FATFS *fs = dp->obj.fs;
+
+
+	ofs = dp->dptr + SZDIRE;	/* Next entry */
+	if (dp->sect == 0 || ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE;	/* Report EOT when offset has reached max value */
+
+	if (ofs % SS(fs) == 0) {	/* Sector changed? */
+		dp->sect++;				/* Next sector */
+
+		if (dp->clust == 0) {	/* Static table */
+			if (ofs / SZDIRE >= fs->n_rootdir) {	/* Report EOT if it reached end of static table */
+				dp->sect = 0; return FR_NO_FILE;
+			}
+		}
+		else {					/* Dynamic table */
+			if ((ofs / SS(fs) & (fs->csize - 1)) == 0) {	/* Cluster changed? */
+				clst = get_fat(&dp->obj, dp->clust);		/* Get next cluster */
+				if (clst <= 1) return FR_INT_ERR;			/* Internal error */
+				if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
+				if (clst >= fs->n_fatent) {					/* It reached end of dynamic table */
+#if !FF_FS_READONLY
+					if (!stretch) {								/* If no stretch, report EOT */
+						dp->sect = 0; return FR_NO_FILE;
+					}
+					clst = create_chain(&dp->obj, dp->clust);	/* Allocate a cluster */
+					if (clst == 0) return FR_DENIED;			/* No free cluster */
+					if (clst == 1) return FR_INT_ERR;			/* Internal error */
+					if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
+					if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR;	/* Clean up the stretched table */
+					if (FF_FS_EXFAT) dp->obj.stat |= 4;			/* exFAT: The directory has been stretched */
+#else
+					if (!stretch) dp->sect = 0;					/* (this line is to suppress compiler warning) */
+					dp->sect = 0; return FR_NO_FILE;			/* Report EOT */
+#endif
+				}
+				dp->clust = clst;		/* Initialize data for new cluster */
+				dp->sect = clst2sect(fs, clst);
+			}
+		}
+	}
+	dp->dptr = ofs;						/* Current entry */
+	dp->dir = fs->win + ofs % SS(fs);	/* Pointer to the entry in the win[] */
+
+	return FR_OK;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Reserve a block of directory entries             */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_alloc (	/* FR_OK(0):succeeded, !=0:error */
+	DIR* dp,		/* Pointer to the directory object */
+	UINT nent		/* Number of contiguous entries to allocate */
+)
+{
+	FRESULT res;
+	UINT n;
+	FATFS *fs = dp->obj.fs;
+
+
+	res = dir_sdi(dp, 0);
+	if (res == FR_OK) {
+		n = 0;
+		do {
+			res = move_window(fs, dp->sect);
+			if (res != FR_OK) break;
+#if FF_FS_EXFAT
+			if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
+#else
+			if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
+#endif
+				if (++n == nent) break;	/* A block of contiguous free entries is found */
+			} else {
+				n = 0;					/* Not a blank entry. Restart to search */
+			}
+			res = dir_next(dp, 1);
+		} while (res == FR_OK);	/* Next entry with table stretch enabled */
+	}
+
+	if (res == FR_NO_FILE) res = FR_DENIED;	/* No directory entry to allocate */
+	return res;
+}
+
+#endif	/* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT: Directory handling - Load/Store start cluster number             */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD ld_clust (	/* Returns the top cluster value of the SFN entry */
+	FATFS* fs,		/* Pointer to the fs object */
+	const BYTE* dir	/* Pointer to the key entry */
+)
+{
+	DWORD cl;
+
+	cl = ld_word(dir + DIR_FstClusLO);
+	if (fs->fs_type == FS_FAT32) {
+		cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;
+	}
+
+	return cl;
+}
+
+
+#if !FF_FS_READONLY
+static
+void st_clust (
+	FATFS* fs,	/* Pointer to the fs object */
+	BYTE* dir,	/* Pointer to the key entry */
+	DWORD cl	/* Value to be set */
+)
+{
+	st_word(dir + DIR_FstClusLO, (WORD)cl);
+	if (fs->fs_type == FS_FAT32) {
+		st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));
+	}
+}
+#endif
+
+
+
+#if FF_USE_LFN
+/*--------------------------------------------------------*/
+/* FAT-LFN: Compare a part of file name with an LFN entry */
+/*--------------------------------------------------------*/
+static
+int cmp_lfn (				/* 1:matched, 0:not matched */
+	const WCHAR* lfnbuf,	/* Pointer to the LFN working buffer to be compared */
+	BYTE* dir				/* Pointer to the directory entry containing the part of LFN */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	if (ld_word(dir + LDIR_FstClusLO) != 0) return 0;	/* Check LDIR_FstClusLO */
+
+	i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;	/* Offset in the LFN buffer */
+
+	for (wc = 1, s = 0; s < 13; s++) {		/* Process all characters in the entry */
+		uc = ld_word(dir + LfnOfs[s]);		/* Pick an LFN character */
+		if (wc) {
+			if (i >= FF_MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) {	/* Compare it */
+				return 0;					/* Not matched */
+			}
+			wc = uc;
+		} else {
+			if (uc != 0xFFFF) return 0;		/* Check filler */
+		}
+	}
+
+	if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0;	/* Last segment matched but different length */
+
+	return 1;		/* The part of LFN matched */
+}
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------*/
+/* FAT-LFN: Pick a part of file name from an LFN entry */
+/*-----------------------------------------------------*/
+static
+int pick_lfn (			/* 1:succeeded, 0:buffer overflow or invalid LFN entry */
+	WCHAR* lfnbuf,		/* Pointer to the LFN working buffer */
+	BYTE* dir			/* Pointer to the LFN entry */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	if (ld_word(dir + LDIR_FstClusLO) != 0) return 0;	/* Check LDIR_FstClusLO is 0 */
+
+	i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13;	/* Offset in the LFN buffer */
+
+	for (wc = 1, s = 0; s < 13; s++) {		/* Process all characters in the entry */
+		uc = ld_word(dir + LfnOfs[s]);		/* Pick an LFN character */
+		if (wc) {
+			if (i >= FF_MAX_LFN) return 0;	/* Buffer overflow? */
+			lfnbuf[i++] = wc = uc;			/* Store it */
+		} else {
+			if (uc != 0xFFFF) return 0;		/* Check filler */
+		}
+	}
+
+	if (dir[LDIR_Ord] & LLEF) {				/* Put terminator if it is the last LFN part */
+		if (i >= FF_MAX_LFN) return 0;		/* Buffer overflow? */
+		lfnbuf[i] = 0;
+	}
+
+	return 1;		/* The part of LFN is valid */
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------*/
+/* FAT-LFN: Create an entry of LFN entries */
+/*-----------------------------------------*/
+static
+void put_lfn (
+	const WCHAR* lfn,	/* Pointer to the LFN */
+	BYTE* dir,			/* Pointer to the LFN entry to be created */
+	BYTE ord,			/* LFN order (1-20) */
+	BYTE sum			/* Checksum of the corresponding SFN */
+)
+{
+	UINT i, s;
+	WCHAR wc;
+
+
+	dir[LDIR_Chksum] = sum;			/* Set checksum */
+	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 working buffer */
+	s = wc = 0;
+	do {
+		if (wc != 0xFFFF) wc = lfn[i++];	/* Get an effective character */
+		st_word(dir + LfnOfs[s], wc);		/* Put it */
+		if (wc == 0) wc = 0xFFFF;		/* Padding characters for left locations */
+	} while (++s < 13);
+	if (wc == 0xFFFF || !lfn[i]) ord |= LLEF;	/* Last LFN part is the start of LFN sequence */
+	dir[LDIR_Ord] = ord;			/* Set the LFN order */
+}
+
+#endif	/* !FF_FS_READONLY */
+#endif	/* FF_USE_LFN */
+
+
+
+#if FF_USE_LFN && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Create a Numbered SFN                                        */
+/*-----------------------------------------------------------------------*/
+
+static
+void gen_numname (
+	BYTE* dst,			/* Pointer to the buffer to store numbered SFN */
+	const BYTE* src,	/* Pointer to SFN */
+	const WCHAR* lfn,	/* Pointer to LFN */
+	UINT seq			/* Sequence number */
+)
+{
+	BYTE ns[8], c;
+	UINT i, j;
+	WCHAR wc;
+	DWORD sr;
+
+
+	mem_cpy(dst, src, 11);
+
+	if (seq > 5) {	/* In case of many collisions, generate a hash number instead of sequential number */
+		sr = seq;
+		while (*lfn) {	/* Create a CRC */
+			wc = *lfn++;
+			for (i = 0; i < 16; i++) {
+				sr = (sr << 1) + (wc & 1);
+				wc >>= 1;
+				if (sr & 0x10000) sr ^= 0x11021;
+			}
+		}
+		seq = (UINT)sr;
+	}
+
+	/* itoa (hexdecimal) */
+	i = 7;
+	do {
+		c = (BYTE)((seq % 16) + '0');
+		if (c > '9') c += 7;
+		ns[i--] = c;
+		seq /= 16;
+	} while (seq);
+	ns[i] = '~';
+
+	/* Append the number to the SFN body */
+	for (j = 0; j < i && dst[j] != ' '; j++) {
+		if (dbc_1st(dst[j])) {
+			if (j == i - 1) break;
+			j++;
+		}
+	}
+	do {
+		dst[j++] = (i < 8) ? ns[i++] : ' ';
+	} while (j < 8);
+}
+#endif	/* FF_USE_LFN && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LFN
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Calculate checksum of an SFN entry                           */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE sum_sfn (
+	const BYTE* dir		/* Pointer to the SFN entry */
+)
+{
+	BYTE sum = 0;
+	UINT n = 11;
+
+	do {
+		sum = (sum >> 1) + (sum << 7) + *dir++;
+	} while (--n);
+	return sum;
+}
+
+#endif	/* FF_USE_LFN */
+
+
+
+#if FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* exFAT: Checksum                                                       */
+/*-----------------------------------------------------------------------*/
+
+static
+WORD xdir_sum (			/* Get checksum of the directoly entry block */
+	const BYTE* dir		/* Directory entry block to be calculated */
+)
+{
+	UINT i, szblk;
+	WORD sum;
+
+
+	szblk = (dir[XDIR_NumSec] + 1) * SZDIRE;
+	for (i = sum = 0; i < szblk; i++) {
+		if (i == XDIR_SetSum) {	/* Skip sum field */
+			i++;
+		} else {
+			sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];
+		}
+	}
+	return sum;
+}
+
+
+
+static
+WORD xname_sum (		/* Get check sum (to be used as hash) of the name */
+	const WCHAR* name	/* File name to be calculated */
+)
+{
+	WCHAR chr;
+	WORD sum = 0;
+
+
+	while ((chr = *name++) != 0) {
+		chr = ff_wtoupper(chr);		/* File name needs to be upper-case converted */
+		sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);
+		sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);
+	}
+	return sum;
+}
+
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+static
+DWORD xsum32 (
+	BYTE  dat,	/* Byte to be calculated */
+	DWORD sum	/* Previous sum */
+)
+{
+	sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;
+	return sum;
+}
+#endif
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*------------------------------------------------------*/
+/* exFAT: Get object information from a directory block */
+/*------------------------------------------------------*/
+
+static
+void get_xdir_info (
+	BYTE* dirb,			/* Pointer to the direcotry entry block 85+C0+C1s */
+	FILINFO* fno		/* Buffer to store the extracted file information */
+)
+{
+	WCHAR w;
+	UINT di, si, nc;
+
+	/* Get file name */
+	for (si = SZDIRE * 2, nc = di = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) {
+		if ((si % SZDIRE) == 0) si += 2;		/* Skip entry type field */
+		w = ld_word(dirb + si);					/* Get a character */
+#if !FF_LFN_UNICODE		/* ANSI/OEM API */
+		w = ff_uni2oem(w, CODEPAGE);			/* Convert it to OEM code */
+		if (w >= 0x100) {						/* Is it a double byte char? */
+			fno->fname[di++] = (char)(w >> 8);	/* Store 1st byte of the DBC */
+		}
+#endif
+		if (w == 0 || di >= FF_MAX_LFN) { di = 0; break; }	/* Invalid char or buffer overflow --> inaccessible object name */
+		fno->fname[di++] = (TCHAR)w;			/* Store the character */
+	}
+	if (di == 0) fno->fname[di++] = '?';	/* Inaccessible object name? */
+	fno->fname[di] = 0;						/* Terminate file name */
+
+	fno->altname[0] = 0;					/* No SFN */
+	fno->fattrib = dirb[XDIR_Attr];			/* Attribute */
+	fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize);	/* Size */
+	fno->ftime = ld_word(dirb + XDIR_ModTime + 0);	/* Time */
+	fno->fdate = ld_word(dirb + XDIR_ModTime + 2);	/* Date */
+}
+
+#endif	/* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+/*-----------------------------------*/
+/* exFAT: Get a directry entry block */
+/*-----------------------------------*/
+
+static
+FRESULT load_xdir (	/* FR_INT_ERR: invalid entry block */
+	DIR* dp			/* Reading direcotry object pointing top of the entry block to load */
+)
+{
+	FRESULT res;
+	UINT i, sz_ent;
+	BYTE* dirb = dp->obj.fs->dirbuf;	/* Pointer to the on-memory direcotry entry block 85+C0+C1s */
+
+
+	/* Load 85 entry */
+	res = move_window(dp->obj.fs, dp->sect);
+	if (res != FR_OK) return res;
+	if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR;	/* Invalid order */
+	mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE);
+	sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE;
+	if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR;
+
+	/* Load C0 entry */
+	res = dir_next(dp, 0);
+	if (res == FR_NO_FILE) res = FR_INT_ERR;	/* It cannot be */
+	if (res != FR_OK) return res;
+	res = move_window(dp->obj.fs, dp->sect);
+	if (res != FR_OK) return res;
+	if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR;	/* Invalid order */
+	mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE);
+	if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR;
+
+	/* Load C1 entries */
+	i = 2 * SZDIRE;	/* C1 offset to load */
+	do {
+		res = dir_next(dp, 0);
+		if (res == FR_NO_FILE) res = FR_INT_ERR;	/* It cannot be */
+		if (res != FR_OK) return res;
+		res = move_window(dp->obj.fs, dp->sect);
+		if (res != FR_OK) return res;
+		if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR;	/* Invalid order */
+		if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE);
+	} while ((i += SZDIRE) < sz_ent);
+
+	/* Sanity check (do it for only accessible object) */
+	if (i <= MAXDIRB(FF_MAX_LFN)) {
+		if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;
+	}
+	return FR_OK;
+}
+
+
+#if !FF_FS_READONLY || FF_FS_RPATH != 0
+/*------------------------------------------------*/
+/* exFAT: Load the object's directory entry block */
+/*------------------------------------------------*/
+static
+FRESULT load_obj_xdir (	
+	DIR* dp,			/* Blank directory object to be used to access containing direcotry */
+	const FFOBJID* obj	/* Object with its containing directory information */
+)
+{
+	FRESULT res;
+
+	/* Open object containing directory */
+	dp->obj.fs = obj->fs;
+	dp->obj.sclust = obj->c_scl;
+	dp->obj.stat = (BYTE)obj->c_size;
+	dp->obj.objsize = obj->c_size & 0xFFFFFF00;
+	dp->obj.n_frag = 0;
+	dp->blk_ofs = obj->c_ofs;
+
+	res = dir_sdi(dp, dp->blk_ofs);	/* Goto object's entry block */
+	if (res == FR_OK) {
+		res = load_xdir(dp);		/* Load the object's entry block */
+	}
+	return res;
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*----------------------------------------*/
+/* exFAT: Store the directory entry block */
+/*----------------------------------------*/
+static
+FRESULT store_xdir (
+	DIR* dp				/* Pointer to the direcotry object */
+)
+{
+	FRESULT res;
+	UINT nent;
+	BYTE* dirb = dp->obj.fs->dirbuf;	/* Pointer to the direcotry entry block 85+C0+C1s */
+
+	/* Create set sum */
+	st_word(dirb + XDIR_SetSum, xdir_sum(dirb));
+	nent = dirb[XDIR_NumSec] + 1;
+
+	/* Store the direcotry entry block to the directory */
+	res = dir_sdi(dp, dp->blk_ofs);
+	while (res == FR_OK) {
+		res = move_window(dp->obj.fs, dp->sect);
+		if (res != FR_OK) break;
+		mem_cpy(dp->dir, dirb, SZDIRE);
+		dp->obj.fs->wflag = 1;
+		if (--nent == 0) break;
+		dirb += SZDIRE;
+		res = dir_next(dp, 0);
+	}
+	return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;
+}
+
+
+
+/*-------------------------------------------*/
+/* exFAT: Create a new directory enrty block */
+/*-------------------------------------------*/
+
+static
+void create_xdir (
+	BYTE* dirb,			/* Pointer to the direcotry entry block buffer */
+	const WCHAR* lfn	/* Pointer to the object name */
+)
+{
+	UINT i;
+	BYTE nc1, nlen;
+	WCHAR chr;
+
+
+	/* Create 85+C0 entry */
+	mem_set(dirb, 0, 2 * SZDIRE);
+	dirb[0 * SZDIRE + XDIR_Type] = 0x85;
+	dirb[1 * SZDIRE + XDIR_Type] = 0xC0;
+
+	/* Create C1 entries */
+	nlen = nc1 = 0; chr = 1; i = SZDIRE * 2;
+	do {
+		dirb[i++] = 0xC1; dirb[i++] = 0;	/* Entry type C1 */
+		do {	/* Fill name field */
+			if (chr && (chr = lfn[nlen]) != 0) nlen++;	/* Get a character if exist */
+			st_word(dirb + i, chr); 		/* Store it */
+			i += 2;
+		} while (i % SZDIRE != 0);
+		nc1++;
+	} while (lfn[nlen]);	/* Fill next entry if any char follows */
+
+	dirb[XDIR_NumName] = nlen;		/* Set name length */
+	dirb[XDIR_NumSec] = 1 + nc1;	/* Set secondary count */
+	st_word(dirb + XDIR_NameHash, xname_sum(lfn));	/* Set name hash */
+}
+
+#endif	/* !FF_FS_READONLY */
+#endif	/* FF_FS_EXFAT */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_read (
+	DIR* dp,		/* Pointer to the directory object */
+	int vol			/* Filtered by 0:file/directory or 1:volume label */
+)
+{
+	FRESULT res = FR_NO_FILE;
+	FATFS *fs = dp->obj.fs;
+	BYTE a, c;
+#if FF_USE_LFN
+	BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+	while (dp->sect) {
+		res = move_window(fs, dp->sect);
+		if (res != FR_OK) break;
+		c = dp->dir[DIR_Name];	/* Test for the entry type */
+		if (c == 0) {
+			res = FR_NO_FILE; break; /* Reached to end of the directory */
+		}
+#if FF_FS_EXFAT
+		if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+			if (FF_USE_LABEL && vol) {
+				if (c == 0x83) break;	/* Volume label entry? */
+			} else {
+				if (c == 0x85) {		/* Start of the file entry block? */
+					dp->blk_ofs = dp->dptr;	/* Get location of the block */
+					res = load_xdir(dp);	/* Load the entry block */
+					if (res == FR_OK) {
+						dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK;	/* Get attribute */
+					}
+					break;
+				}
+			}
+		} else
+#endif
+		{	/* On the FAT/FAT32 volume */
+			dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;	/* Get attribute */
+#if FF_USE_LFN		/* LFN configuration */
+			if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) {	/* An entry without valid data */
+				ord = 0xFF;
+			} else {
+				if (a == AM_LFN) {			/* An LFN entry is found */
+					if (c & LLEF) {			/* Is it start of an LFN sequence? */
+						sum = dp->dir[LDIR_Chksum];
+						c &= (BYTE)~LLEF; ord = c;
+						dp->blk_ofs = dp->dptr;
+					}
+					/* Check LFN validity and capture it */
+					ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+				} else {					/* An SFN entry is found */
+					if (ord || sum != sum_sfn(dp->dir)) {	/* Is there a valid LFN? */
+						dp->blk_ofs = 0xFFFFFFFF;			/* It has no LFN. */
+					}
+					break;
+				}
+			}
+#else		/* Non LFN configuration */
+			if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) {	/* Is it a valid entry? */
+				break;
+			}
+#endif
+		}
+		res = dir_next(dp, 0);		/* Next entry */
+		if (res != FR_OK) break;
+	}
+
+	if (res != FR_OK) dp->sect = 0;		/* Terminate the read operation on error or EOT */
+	return res;
+}
+
+#endif	/* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (	/* FR_OK(0):succeeded, !=0:error */
+	DIR* dp			/* Pointer to the directory object with the file name */
+)
+{
+	FRESULT res;
+	FATFS *fs = dp->obj.fs;
+	BYTE c;
+#if FF_USE_LFN
+	BYTE a, ord, sum;
+#endif
+
+	res = dir_sdi(dp, 0);			/* Rewind directory object */
+	if (res != FR_OK) return res;
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+		BYTE nc;
+		UINT di, ni;
+		WORD hash = xname_sum(fs->lfnbuf);		/* Hash value of the name to find */
+
+		while ((res = dir_read(dp, 0)) == FR_OK) {	/* Read an item */
+#if FF_MAX_LFN < 255
+			if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue;			/* Skip comparison if inaccessible object name */
+#endif
+			if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue;	/* Skip comparison if hash mismatched */
+			for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) {	/* Compare the name */
+				if ((di % SZDIRE) == 0) di += 2;
+				if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;
+			}
+			if (nc == 0 && !fs->lfnbuf[ni]) break;	/* Name matched? */
+		}
+		return res;
+	}
+#endif
+	/* On the FAT/FAT32 volume */
+#if FF_USE_LFN
+	ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF;	/* Reset LFN sequence */
+#endif
+	do {
+		res = move_window(fs, dp->sect);
+		if (res != FR_OK) break;
+		c = dp->dir[DIR_Name];
+		if (c == 0) { res = FR_NO_FILE; break; }	/* Reached to end of table */
+#if FF_USE_LFN		/* LFN configuration */
+		dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;
+		if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) {	/* An entry without valid data */
+			ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;	/* Reset LFN sequence */
+		} else {
+			if (a == AM_LFN) {			/* An LFN entry is found */
+				if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
+					if (c & LLEF) {		/* Is it start of LFN sequence? */
+						sum = dp->dir[LDIR_Chksum];
+						c &= (BYTE)~LLEF; ord = c;	/* LFN start order */
+						dp->blk_ofs = dp->dptr;	/* Start offset of LFN */
+					}
+					/* Check validity of the LFN entry and compare it with given name */
+					ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+				}
+			} else {					/* An SFN entry is found */
+				if (!ord && sum == sum_sfn(dp->dir)) break;	/* LFN matched? */
+				if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break;	/* SFN matched? */
+				ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;	/* Reset LFN sequence */
+			}
+		}
+#else		/* Non LFN configuration */
+		dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
+		if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break;	/* Is it a valid entry? */
+#endif
+		res = dir_next(dp, 0);	/* Next entry */
+	} while (res == FR_OK);
+
+	return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_register (	/* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
+	DIR* dp				/* Target directory with object name to be created */
+)
+{
+	FRESULT res;
+	FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN		/* LFN configuration */
+	UINT n, nlen, nent;
+	BYTE sn[12], sum;
+
+
+	if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME;	/* Check name validity */
+	for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ;	/* Get lfn length */
+
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+		nent = (nlen + 14) / 15 + 2;	/* Number of entries to allocate (85+C0+C1s) */
+		res = dir_alloc(dp, nent);		/* Allocate entries */
+		if (res != FR_OK) return res;
+		dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1);	/* Set the allocated entry block offset */
+
+		if (dp->obj.stat & 4) {			/* Has the directory been stretched? */
+			dp->obj.stat &= ~4;
+			res = fill_first_frag(&dp->obj);	/* Fill the first fragment on the FAT if needed */
+			if (res != FR_OK) return res;
+			res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF);	/* Fill the last fragment on the FAT if needed */
+			if (res != FR_OK) return res;
+			if (dp->obj.sclust != 0) {		/* Is it a sub directory? */
+				DIR dj;
+
+				res = load_obj_xdir(&dj, &dp->obj);	/* Load the object status */
+				if (res != FR_OK) return res;
+				dp->obj.objsize += (DWORD)fs->csize * SS(fs);			/* Increase the directory size by cluster size */
+				st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize);	/* Update the allocation status */
+				st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
+				fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;
+				res = store_xdir(&dj);				/* Store the object status */
+				if (res != FR_OK) return res;
+			}
+		}
+
+		create_xdir(fs->dirbuf, fs->lfnbuf);	/* Create on-memory directory block to be written later */
+		return FR_OK;
+	}
+#endif
+	/* On the FAT/FAT32 volume */
+	mem_cpy(sn, dp->fn, 12);
+	if (sn[NSFLAG] & NS_LOSS) {			/* When LFN is out of 8.3 format, generate a numbered name */
+		dp->fn[NSFLAG] = NS_NOLFN;		/* Find only SFN */
+		for (n = 1; n < 100; n++) {
+			gen_numname(dp->fn, sn, fs->lfnbuf, n);	/* Generate a numbered name */
+			res = dir_find(dp);				/* 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' */
+		dp->fn[NSFLAG] = sn[NSFLAG];
+	}
+
+	/* Create an SFN with/without LFNs. */
+	nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1;	/* Number of entries to allocate */
+	res = dir_alloc(dp, nent);		/* Allocate entries */
+	if (res == FR_OK && --nent) {	/* Set LFN entry if needed */
+		res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
+		if (res == FR_OK) {
+			sum = sum_sfn(dp->fn);	/* Checksum value of the SFN tied to the LFN */
+			do {					/* Store LFN entries in bottom first */
+				res = move_window(fs, dp->sect);
+				if (res != FR_OK) break;
+				put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
+				fs->wflag = 1;
+				res = dir_next(dp, 0);	/* Next entry */
+			} while (res == FR_OK && --nent);
+		}
+	}
+
+#else	/* Non LFN configuration */
+	res = dir_alloc(dp, 1);		/* Allocate an entry for SFN */
+
+#endif
+
+	/* Set SFN entry */
+	if (res == FR_OK) {
+		res = move_window(fs, dp->sect);
+		if (res == FR_OK) {
+			mem_set(dp->dir, 0, SZDIRE);	/* Clean the entry */
+			mem_cpy(dp->dir + DIR_Name, dp->fn, 11);	/* Put SFN */
+#if FF_USE_LFN
+			dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT);	/* Put NT flag */
+#endif
+			fs->wflag = 1;
+		}
+	}
+
+	return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_remove (	/* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
+	DIR* dp				/* Directory object pointing the entry to be removed */
+)
+{
+	FRESULT res;
+	FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN		/* LFN configuration */
+	DWORD last = dp->dptr;
+
+	res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs);	/* Goto top of the entry block if LFN is exist */
+	if (res == FR_OK) {
+		do {
+			res = move_window(fs, dp->sect);
+			if (res != FR_OK) break;
+			/* Mark an entry 'deleted' */
+			if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+				dp->dir[XDIR_Type] &= 0x7F;
+			} else {									/* On the FAT/FAT32 volume */
+				dp->dir[DIR_Name] = DDEM;
+			}
+			fs->wflag = 1;
+			if (dp->dptr >= last) break;	/* If reached last entry then all entries of the object has been deleted. */
+			res = dir_next(dp, 0);	/* Next entry */
+		} while (res == FR_OK);
+		if (res == FR_NO_FILE) res = FR_INT_ERR;
+	}
+#else			/* Non LFN configuration */
+
+	res = move_window(fs, dp->sect);
+	if (res == FR_OK) {
+		dp->dir[DIR_Name] = DDEM;
+		fs->wflag = 1;
+	}
+#endif
+
+	return res;
+}
+
+#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+
+static
+void get_fileinfo (		/* No return code */
+	DIR* dp,			/* Pointer to the directory object */
+	FILINFO* fno	 	/* Pointer to the file information to be filled */
+)
+{
+	UINT i, j;
+	TCHAR c;
+	DWORD tm;
+#if FF_USE_LFN
+	WCHAR w, lfv;
+	FATFS *fs = dp->obj.fs;
+#endif
+
+
+	fno->fname[0] = 0;			/* Invaidate file info */
+	if (dp->sect == 0) return;	/* Exit if read pointer has reached end of directory */
+
+#if FF_USE_LFN		/* LFN configuration */
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+		get_xdir_info(fs->dirbuf, fno);
+		return;
+	} else
+#endif
+	{	/* On the FAT/FAT32 volume */
+		if (dp->blk_ofs != 0xFFFFFFFF) {	/* Get LFN if available */
+			i = j = 0;
+			while ((w = fs->lfnbuf[j++]) != 0) {	/* Get an LFN character */
+#if !FF_LFN_UNICODE	/* ANSI/OEM API */
+				w = ff_uni2oem(w, CODEPAGE);	/* Unicode -> OEM */
+				if (w == 0) { i = 0; break; }	/* No LFN if it could not be converted */
+				if (w >= 0x100) {				/* Put 1st byte if it is a DBC */
+					fno->fname[i++] = (char)(w >> 8);
+				}
+#endif
+				if (i >= FF_MAX_LFN) { i = 0; break; }	/* No LFN if buffer overflow */
+				fno->fname[i++] = (TCHAR)w;
+			}
+			fno->fname[i] = 0;	/* Terminate the LFN */
+		}
+	}
+
+	i = j = 0;
+	lfv = fno->fname[i];	/* LFN is exist if non-zero */
+	while (i < 11) {		/* Copy name body and extension */
+		c = (TCHAR)dp->dir[i++];
+		if (c == ' ') continue;				/* Skip padding spaces */
+		if (c == RDDEM) c = (TCHAR)DDEM;	/* Restore replaced DDEM character */
+		if (i == 9) {						/* Insert a . if extension is exist */
+			if (!lfv) fno->fname[j] = '.';
+			fno->altname[j++] = '.';
+		}
+#if FF_LFN_UNICODE	/* Unicode API */
+		if (dbc_1st((BYTE)c) && i != 8 && i != 11 && dbc_2nd(dp->dir[i])) {
+			c = c << 8 | dp->dir[i++];
+		}
+		c = ff_oem2uni(c, CODEPAGE);	/* OEM -> Unicode */
+		if (!c) c = '?';
+#endif
+		fno->altname[j] = c;
+		if (!lfv) {
+			if (IsUpper(c) && (dp->dir[DIR_NTres] & ((i >= 9) ? NS_EXT : NS_BODY))) {
+				c += 0x20;			/* To lower */
+			}
+			fno->fname[j] = c;
+		}
+		j++;
+	}
+	if (!lfv) {
+		fno->fname[j] = 0;
+		if (!dp->dir[DIR_NTres]) j = 0;	/* Altname is no longer needed if neither LFN nor case info is exist. */
+	}
+	fno->altname[j] = 0;	/* Terminate the SFN */
+
+#else	/* Non-LFN configuration */
+	i = j = 0;
+	while (i < 11) {		/* Copy name body and extension */
+		c = (TCHAR)dp->dir[i++];
+		if (c == ' ') continue;				/* Skip padding spaces */
+		if (c == RDDEM) c = (TCHAR)DDEM;	/* Restore replaced DDEM character */
+		if (i == 9) fno->fname[j++] = '.';	/* Insert a . if extension is exist */
+		fno->fname[j++] = c;
+	}
+	fno->fname[j] = 0;
+#endif
+
+	fno->fattrib = dp->dir[DIR_Attr];				/* Attribute */
+	fno->fsize = ld_dword(dp->dir + DIR_FileSize);	/* Size */
+	tm = ld_dword(dp->dir + DIR_ModTime);			/* Timestamp */
+	fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16);
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+
+#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Pattern matching                                                      */
+/*-----------------------------------------------------------------------*/
+
+static
+WCHAR get_achar (		/* Get a character and advances ptr 1 or 2 */
+	const TCHAR** ptr	/* Pointer to pointer to the SBCS/DBCS/Unicode string */
+)
+{
+	WCHAR chr;
+
+#if FF_LFN_UNICODE && FF_USE_LFN	/* Unicode API */
+	chr = ff_wtoupper(*(*ptr)++);			/* Get a Unicode char and to upper */
+#else								/* ANSI/OEM API */
+	chr = (BYTE)*(*ptr)++;					/* Get a byte */
+	if (IsLower(chr)) chr -= 0x20;			/* To upper ASCII char */
+#if FF_CODE_PAGE == 0
+	if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80];	/* To upper SBCS extended char */
+#elif FF_CODE_PAGE < 900
+	if (chr >= 0x80) chr = ExCvt[chr - 0x80];	/* To upper SBCS extended char */
+#endif
+#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
+	if (dbc_1st((BYTE)chr) && dbc_2nd((BYTE)**ptr)) {	/* Get DBC 2nd byte if needed */
+		chr = chr << 8 | (BYTE)*(*ptr)++;
+	}
+#endif
+#endif
+	return chr;
+}
+
+
+static
+int pattern_matching (	/* 0:not matched, 1:matched */
+	const TCHAR* pat,	/* Matching pattern */
+	const TCHAR* nam,	/* String to be tested */
+	int skip,			/* Number of pre-skip chars (number of ?s) */
+	int inf				/* Infinite search (* specified) */
+)
+{
+	const TCHAR *pp, *np;
+	WCHAR pc, nc;
+	int nm, nx;
+
+
+	while (skip--) {				/* Pre-skip name chars */
+		if (!get_achar(&nam)) return 0;	/* Branch mismatched if less name chars */
+	}
+	if (!*pat && inf) return 1;		/* (short circuit) */
+
+	do {
+		pp = pat; np = nam;			/* Top of pattern and name to match */
+		for (;;) {
+			if (*pp == '?' || *pp == '*') {	/* Wildcard? */
+				nm = nx = 0;
+				do {				/* Analyze the wildcard chars */
+					if (*pp++ == '?') nm++; else nx = 1;
+				} while (*pp == '?' || *pp == '*');
+				if (pattern_matching(pp, np, nm, nx)) return 1;	/* Test new branch (recurs upto number of wildcard blocks in the pattern) */
+				nc = *np; break;	/* Branch mismatched */
+			}
+			pc = get_achar(&pp);	/* Get a pattern char */
+			nc = get_achar(&np);	/* Get a name char */
+			if (pc != nc) break;	/* Branch mismatched? */
+			if (pc == 0) return 1;	/* Branch matched? (matched at end of both strings) */
+		}
+		get_achar(&nam);			/* nam++ */
+	} while (inf && nc);			/* Retry until end of name if infinite search is specified */
+
+	return 0;
+}
+
+#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a top segment and create the object name in directory form       */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (	/* FR_OK: successful, FR_INVALID_NAME: could not create */
+	DIR* dp,			/* Pointer to the directory object */
+	const TCHAR** path	/* Pointer to pointer to the segment in the path string */
+)
+{
+#if FF_USE_LFN		/* LFN configuration */
+	BYTE b, cf;
+	WCHAR w, *lfn;
+	UINT i, ni, si, di;
+	const TCHAR *p;
+
+
+	/* Create LFN in Unicode */
+	p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0;
+	for (;;) {
+		w = p[si++];					/* Get a character */
+		if (w < ' ') break;				/* Break if end of the path name */
+		if (w == '/' || w == '\\') {	/* Break if a separator is found */
+			while (p[si] == '/' || p[si] == '\\') si++;	/* Skip duplicated separator if exist */
+			break;
+		}
+		if (di >= FF_MAX_LFN) return FR_INVALID_NAME;	/* Reject too long name */
+#if !FF_LFN_UNICODE		/* ANSI/OEM API */
+		w &= 0xFF;
+		if (dbc_1st((BYTE)w)) {			/* Check if it is a DBC 1st byte */
+			b = (BYTE)p[si++];			/* Get 2nd byte */
+			w = (w << 8) + b;			/* Create a DBC */
+			if (!dbc_2nd(b)) return FR_INVALID_NAME;	/* Reject invalid sequence */
+		}
+		w = ff_oem2uni(w, CODEPAGE);	/* Convert ANSI/OEM to Unicode */
+		if (!w) return FR_INVALID_NAME;	/* Reject invalid code */
+#endif
+		if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME;	/* Reject illegal characters for LFN */
+		lfn[di++] = w;					/* Store the Unicode character */
+	}
+	*path = &p[si];						/* Return pointer to the next segment */
+	cf = (w < ' ') ? NS_LAST : 0;		/* Set last segment flag if end of the path */
+#if FF_FS_RPATH != 0
+	if ((di == 1 && lfn[di - 1] == '.') ||
+		(di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {	/* Is this segment a dot name? */
+		lfn[di] = 0;
+		for (i = 0; i < 11; i++)		/* Create dot name for SFN entry */
+			dp->fn[i] = (i < di) ? '.' : ' ';
+		dp->fn[i] = cf | NS_DOT;		/* This is a dot entry */
+		return FR_OK;
+	}
+#endif
+	while (di) {						/* Snip off trailing spaces and dots if exist */
+		w = lfn[di - 1];
+		if (w != ' ' && w != '.') break;
+		di--;
+	}
+	lfn[di] = 0;						/* LFN is created */
+	if (di == 0) return FR_INVALID_NAME;	/* Reject nul name */
+
+	/* Create SFN in directory form */
+	mem_set(dp->fn, ' ', 11);
+	for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;	/* Strip leading spaces and dots */
+	if (si > 0) cf |= NS_LOSS | NS_LFN;
+	while (di > 0 && lfn[di - 1] != '.') di--;	/* Find extension (di<=si: no extension) */
+
+	i = b = 0; ni = 8;
+	for (;;) {
+		w = lfn[si++];					/* Get an LFN character */
+		if (!w) break;					/* Break on end of the LFN */
+		if (w == ' ' || (w == '.' && si != di)) {	/* Remove spaces and dots */
+			cf |= NS_LOSS | NS_LFN;
+			continue;
+		}
+
+		if (i >= ni || si == di) {		/* Entered extension or end of SFN */
+			if (ni == 11) {				/* Extension fileld overflow? */
+				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 fileld */
+			b <<= 2; continue;
+		}
+
+		if (w >= 0x80) {				/* Is this a non-ASCII character? */
+			cf |= NS_LFN;				/* Force to create LFN entry */
+#if FF_CODE_PAGE == 0
+			if (ExCvt) {	/* In SBCS */
+				w = ff_uni2oem(w, CODEPAGE);	/* Unicode -> OEM code */
+				if (w & 0x80) w = ExCvt[w & 0x7F];	/* Convert extended character to upper (SBCS) */
+			} else {		/* In DBCS */
+				w = ff_uni2oem(ff_wtoupper(w), CODEPAGE);	/* Upper converted Unicode -> OEM code */
+			}
+#elif FF_CODE_PAGE < 900	/* SBCS cfg */
+			w = ff_uni2oem(w, CODEPAGE);	/* Unicode -> OEM code */
+			if (w & 0x80) w = ExCvt[w & 0x7F];	/* Convert extended character to upper (SBCS) */
+#else					/* DBCS cfg */
+			w = ff_uni2oem(ff_wtoupper(w), CODEPAGE);	/* Upper converted Unicode -> OEM code */
+#endif
+		}
+
+		if (w >= 0x100) {				/* Is this a DBC? */
+			if (i >= ni - 1) {			/* Field overflow? */
+				cf |= NS_LOSS | NS_LFN;
+				i = ni; continue;		/* Next field */
+			}
+			dp->fn[i++] = (BYTE)(w >> 8);	/* Put 1st byte */
+		} else {						/* SBC */
+			if (!w || chk_chr("+,;=[]", w)) {	/* Replace illegal characters 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;
+					}
+				}
+			}
+		}
+		dp->fn[i++] = (BYTE)w;
+	}
+
+	if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM;	/* If the first character collides with DDEM, replace it with RDDEM */
+
+	if (ni == 8) b <<= 2;
+	if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN;	/* Create LFN entry when there are composite capitals */
+	if (!(cf & NS_LFN)) {						/* When LFN is in 8.3 format without extended character, 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) */
+	}
+
+	dp->fn[NSFLAG] = cf;	/* SFN is created */
+
+	return FR_OK;
+
+
+#else	/* FF_USE_LFN : Non-LFN configuration */
+	BYTE c, d, *sfn;
+	UINT ni, si, i;
+	const char *p;
+
+	/* Create file name in directory form */
+	p = *path; sfn = dp->fn;
+	mem_set(sfn, ' ', 11);
+	si = i = 0; ni = 8;
+#if FF_FS_RPATH != 0
+	if (p[si] == '.') { /* Is this a dot entry? */
+		for (;;) {
+			c = (BYTE)p[si++];
+			if (c != '.' || si >= 3) break;
+			sfn[i++] = c;
+		}
+		if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+		*path = p + si;								/* Return pointer to the next segment */
+		sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;	/* Set last segment flag if end of the path */
+		return FR_OK;
+	}
+#endif
+	for (;;) {
+		c = (BYTE)p[si++];
+		if (c <= ' ') break; 			/* Break if end of the path name */
+		if (c == '/' || c == '\\') {	/* Break if a separator is found */
+			while (p[si] == '/' || p[si] == '\\') si++;	/* Skip duplicated separator if exist */
+			break;
+		}
+		if (c == '.' || i >= ni) {		/* End of body or over size? */
+			if (ni == 11 || c != '.') return FR_INVALID_NAME;	/* Over size or invalid dot */
+			i = 8; ni = 11;				/* Goto extension */
+			continue;
+		}
+#if FF_CODE_PAGE == 0
+		if (ExCvt && c >= 0x80) {		/* Is SBC extended character? */
+			c = ExCvt[c - 0x80];		/* To upper SBC extended character */
+		}
+#elif FF_CODE_PAGE < 900
+		if (c >= 0x80) {				/* Is SBC extended character? */
+			c = ExCvt[c - 0x80];		/* To upper SBC extended character */
+		}
+#endif
+		if (dbc_1st(c)) {				/* Check if it is a DBC 1st byte */
+			d = (BYTE)p[si++];			/* Get 2nd byte */
+			if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME;	/* Reject invalid DBC */
+			sfn[i++] = c;
+			sfn[i++] = d;
+		} else {						/* SBC */
+			if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME;	/* Reject illegal chrs for SFN */
+			if (IsLower(c)) c -= 0x20;	/* To upper */
+			sfn[i++] = c;
+		}
+	}
+	*path = p + si;						/* Return pointer to the next segment */
+	if (i == 0) return FR_INVALID_NAME;	/* Reject nul string */
+
+	if (sfn[0] == DDEM) sfn[0] = RDDEM;	/* If the first character collides with DDEM, replace it with RDDEM */
+	sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0;		/* Set last segment flag if end of the path */
+
+	return FR_OK;
+#endif /* FF_USE_LFN */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (	/* FR_OK(0): successful, !=0: error code */
+	DIR* dp,			/* Directory object to return last directory and found object */
+	const TCHAR* path	/* Full-path string to find a file or directory */
+)
+{
+	FRESULT res;
+	BYTE ns;
+	FATFS *fs = dp->obj.fs;
+
+
+#if FF_FS_RPATH != 0
+	if (*path != '/' && *path != '\\') {	/* Without heading separator */
+		dp->obj.sclust = fs->cdir;				/* Start from current directory */
+	} else
+#endif
+	{										/* With heading separator */
+		while (*path == '/' || *path == '\\') path++;	/* Strip heading separator */
+		dp->obj.sclust = 0;					/* Start from root directory */
+	}
+#if FF_FS_EXFAT
+	dp->obj.n_frag = 0;	/* Invalidate last fragment counter of the object */
+#if FF_FS_RPATH != 0
+	if (fs->fs_type == FS_EXFAT && dp->obj.sclust) {	/* exFAT: Retrieve the sub-directory's status */
+		DIR dj;
+
+		dp->obj.c_scl = fs->cdc_scl;
+		dp->obj.c_size = fs->cdc_size;
+		dp->obj.c_ofs = fs->cdc_ofs;
+		res = load_obj_xdir(&dj, &dp->obj);
+		if (res != FR_OK) return res;
+		dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize);
+		dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+	}
+#endif
+#endif
+
+	if ((UINT)*path < ' ') {				/* Null path name is the origin directory itself */
+		dp->fn[NSFLAG] = NS_NONAME;
+		res = dir_sdi(dp, 0);
+
+	} else {								/* Follow path */
+		for (;;) {
+			res = create_name(dp, &path);	/* Get a segment name of the path */
+			if (res != FR_OK) break;
+			res = dir_find(dp);				/* Find an object with the segment name */
+			ns = dp->fn[NSFLAG];
+			if (res != FR_OK) {				/* Failed to find the object */
+				if (res == FR_NO_FILE) {	/* Object is not found */
+					if (FF_FS_RPATH && (ns & NS_DOT)) {	/* If dot entry is not exist, stay there */
+						if (!(ns & NS_LAST)) continue;	/* Continue to follow if not last segment */
+						dp->fn[NSFLAG] = NS_NONAME;
+						res = FR_OK;
+					} else {							/* Could not find the object */
+						if (!(ns & NS_LAST)) res = FR_NO_PATH;	/* Adjust error code if not last segment */
+					}
+				}
+				break;
+			}
+			if (ns & NS_LAST) break;			/* Last segment matched. Function completed. */
+			/* Get into the sub-directory */
+			if (!(dp->obj.attr & AM_DIR)) {		/* It is not a sub-directory and cannot follow */
+				res = FR_NO_PATH; break;
+			}
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {		/* Save containing directory information for next dir */
+				dp->obj.c_scl = dp->obj.sclust;
+				dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+				dp->obj.c_ofs = dp->blk_ofs;
+				dp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);	/* Open next directory */
+				dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+				dp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);
+			} else
+#endif
+			{
+				dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs));	/* Open next directory */
+			}
+		}
+	}
+
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get logical drive number from path name                               */
+/*-----------------------------------------------------------------------*/
+
+static
+int get_ldnumber (		/* Returns logical drive number (-1:invalid drive) */
+	const TCHAR** path	/* Pointer to pointer to the path name */
+)
+{
+	const TCHAR *tp, *tt;
+	UINT i;
+	int vol = -1;
+#if FF_STR_VOLUME_ID		/* Find string drive id */
+	static const char* const volid[] = {FF_VOLUME_STRS};
+	const char *sp;
+	char c;
+	TCHAR tc;
+#endif
+
+
+	if (*path) {	/* If the pointer is not a null */
+		for (tt = *path; (UINT)*tt >= (FF_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;	/* Find a colon in the path */
+		if (*tt == ':') {	/* If a colon is exist in the path name */
+			tp = *path;
+			i = *tp++;
+			if (IsDigit(i) && tp == tt) {	/* Is there a numeric drive id + colon? */
+				if ((i -= '0') < FF_VOLUMES) {	/* If drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = ++tt;
+				}
+			}
+#if FF_STR_VOLUME_ID
+			 else {	/* No numeric drive number, find string drive id */
+				i = 0; tt++;
+				do {
+					sp = volid[i]; tp = *path;
+					do {	/* Compare a string drive id with path name */
+						c = *sp++; tc = *tp++;
+						if (IsLower(tc)) tc -= 0x20;
+					} while (c && (TCHAR)c == tc);
+				} while ((c || tp != tt) && ++i < FF_VOLUMES);	/* Repeat for each id until pattern match */
+				if (i < FF_VOLUMES) {	/* If a drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = tt;
+				}
+			}
+#endif
+		} else {	/* No volume id and use default drive */
+#if FF_FS_RPATH != 0 && FF_VOLUMES >= 2
+			vol = CurrVol;	/* Current drive */
+#else
+			vol = 0;		/* Drive 0 */
+#endif
+		}
+	}
+	return vol;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT VBR                           */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (	/* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */
+	FATFS* fs,	/* Filesystem object */
+	DWORD sect	/* Sector# (lba) to load and check if it is an FAT-VBR or not */
+)
+{
+	fs->wflag = 0; fs->winsect = 0xFFFFFFFF;		/* Invaidate window */
+	if (move_window(fs, sect) != FR_OK) return 4;	/* Load boot record */
+
+	if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3;	/* Check boot record signature (always placed here even if the sector size is >512) */
+
+#if FF_FS_EXFAT
+	if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT   ", 11)) return 1;	/* Check if exFAT VBR */
+#endif
+	if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) {	/* Valid JumpBoot code? */
+		if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0;		/* Is it an FAT VBR? */
+		if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0;	/* Is it an FAT32 VBR? */
+	}
+	return 2;	/* Valid BS but not FAT */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find logical drive and check if the volume is mounted                 */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT find_volume (	/* FR_OK(0): successful, !=0: any error occurred */
+	const TCHAR** path,	/* Pointer to pointer to the path name (drive number) */
+	FATFS** rfs,		/* Pointer to pointer to the found filesystem object */
+	BYTE mode			/* !=0: Check write protection for write access */
+)
+{
+	BYTE fmt, *pt;
+	int vol;
+	DSTATUS stat;
+	DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4];
+	WORD nrsv;
+	FATFS *fs;
+	UINT i;
+
+
+	/* Get logical drive number */
+	*rfs = 0;
+	vol = get_ldnumber(path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	/* Check if the filesystem object is valid or not */
+	fs = FatFs[vol];					/* Get pointer to the filesystem object */
+	if (!fs) return FR_NOT_ENABLED;		/* Is the filesystem object available? */
+#if FF_FS_REENTRANT
+	if (!lock_fs(fs)) return FR_TIMEOUT;	/* Lock the volume */
+#endif
+	*rfs = fs;							/* Return pointer to the filesystem object */
+
+	mode &= (BYTE)~FA_READ;				/* Desired access mode, write access or not */
+	if (fs->fs_type != 0) {				/* If the volume has been mounted */
+		stat = disk_status(fs->pdrv);
+		if (!(stat & STA_NOINIT)) {		/* and the physical drive is kept initialized */
+			if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) {	/* Check write protection if needed */
+				return FR_WRITE_PROTECTED;
+			}
+			return FR_OK;				/* The filesystem object is valid */
+		}
+	}
+
+	/* The filesystem object is not valid. */
+	/* Following code attempts to mount the volume. (analyze BPB and initialize the filesystem object) */
+
+	fs->fs_type = 0;					/* Clear the filesystem object */
+	fs->pdrv = LD2PD(vol);				/* Bind the logical drive and a physical drive */
+	stat = disk_initialize(fs->pdrv);	/* Initialize the physical drive */
+	if (stat & STA_NOINIT) { 			/* Check if the initialization succeeded */
+		return FR_NOT_READY;			/* Failed to initialize due to no medium or hard error */
+	}
+	if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
+		return FR_WRITE_PROTECTED;
+	}
+#if FF_MAX_SS != FF_MIN_SS				/* Get sector size (multiple sector size cfg only) */
+	if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
+	if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
+#endif
+
+	/* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */
+	bsect = 0;
+	fmt = check_fs(fs, bsect);			/* Load sector 0 and check if it is an FAT-VBR as SFD */
+	if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) {	/* Not an FAT-VBR or forced partition number */
+		for (i = 0; i < 4; i++) {		/* Get partition offset */
+			pt = fs->win + (MBR_Table + i * SZ_PTE);
+			br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0;
+		}
+		i = LD2PT(vol);					/* Partition number: 0:auto, 1-4:forced */
+		if (i != 0) i--;
+		do {							/* Find an FAT volume */
+			bsect = br[i];
+			fmt = bsect ? check_fs(fs, bsect) : 3;	/* Check the partition */
+		} while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);
+	}
+	if (fmt == 4) return FR_DISK_ERR;		/* An error occured in the disk I/O layer */
+	if (fmt >= 2) return FR_NO_FILESYSTEM;	/* No FAT volume is found */
+
+	/* An FAT volume is found (bsect). Following code initializes the filesystem object */
+
+#if FF_FS_EXFAT
+	if (fmt == 1) {
+		QWORD maxlba;
+
+		for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ;	/* Check zero filler */
+		if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
+
+		if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM;	/* Check exFAT version (must be version 1.0) */
+
+		if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) {	/* (BPB_BytsPerSecEx must be equal to the physical sector size) */
+			return FR_NO_FILESYSTEM;
+		}
+
+		maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect;	/* Last LBA + 1 of the volume */
+		if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM;	/* (It cannot be handled in 32-bit LBA) */
+
+		fs->fsize = ld_dword(fs->win + BPB_FatSzEx);	/* Number of sectors per FAT */
+
+		fs->n_fats = fs->win[BPB_NumFATsEx];			/* Number of FATs */
+		if (fs->n_fats != 1) return FR_NO_FILESYSTEM;	/* (Supports only 1 FAT) */
+
+		fs->csize = 1 << fs->win[BPB_SecPerClusEx];		/* Cluster size */
+		if (fs->csize == 0)	return FR_NO_FILESYSTEM;	/* (Must be 1..32768) */
+
+		nclst = ld_dword(fs->win + BPB_NumClusEx);		/* Number of clusters */
+		if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM;	/* (Too many clusters) */
+		fs->n_fatent = nclst + 2;
+
+		/* Boundaries and Limits */
+		fs->volbase = bsect;
+		fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
+		fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
+		if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM;	/* (Volume size must not be smaller than the size requiered) */
+		fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
+
+		/* Check if bitmap location is in assumption (at the first cluster) */
+		if (move_window(fs, clst2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR;
+		for (i = 0; i < SS(fs); i += SZDIRE) {
+			if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break;	/* 81 entry with cluster #2? */
+		}
+		if (i == SS(fs)) return FR_NO_FILESYSTEM;
+#if !FF_FS_READONLY
+		fs->last_clst = fs->free_clst = 0xFFFFFFFF;		/* Initialize cluster allocation information */
+#endif
+		fmt = FS_EXFAT;			/* FAT sub-type */
+	} else
+#endif	/* FF_FS_EXFAT */
+	{
+		if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM;	/* (BPB_BytsPerSec must be equal to the physical sector size) */
+
+		fasize = ld_word(fs->win + BPB_FATSz16);		/* Number of sectors per FAT */
+		if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
+		fs->fsize = fasize;
+
+		fs->n_fats = fs->win[BPB_NumFATs];				/* Number of FATs */
+		if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM;	/* (Must be 1 or 2) */
+		fasize *= fs->n_fats;							/* Number of sectors for FAT area */
+
+		fs->csize = fs->win[BPB_SecPerClus];			/* Cluster size */
+		if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM;	/* (Must be power of 2) */
+
+		fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt);	/* Number of root directory entries */
+		if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM;	/* (Must be sector aligned) */
+
+		tsect = ld_word(fs->win + BPB_TotSec16);		/* Number of sectors on the volume */
+		if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
+
+		nrsv = ld_word(fs->win + BPB_RsvdSecCnt);		/* Number of reserved sectors */
+		if (nrsv == 0) return FR_NO_FILESYSTEM;			/* (Must not be 0) */
+
+		/* Determine the FAT sub type */
+		sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE);	/* RSV + FAT + DIR */
+		if (tsect < sysect) return FR_NO_FILESYSTEM;	/* (Invalid volume size) */
+		nclst = (tsect - sysect) / fs->csize;			/* Number of clusters */
+		if (nclst == 0) return FR_NO_FILESYSTEM;		/* (Invalid volume size) */
+		fmt = 0;
+		if (nclst <= MAX_FAT32) fmt = FS_FAT32;
+		if (nclst <= MAX_FAT16) fmt = FS_FAT16;
+		if (nclst <= MAX_FAT12) fmt = FS_FAT12;
+		if (fmt == 0) return FR_NO_FILESYSTEM;
+
+		/* Boundaries and Limits */
+		fs->n_fatent = nclst + 2;						/* Number of FAT entries */
+		fs->volbase = bsect;							/* Volume start sector */
+		fs->fatbase = bsect + nrsv; 					/* FAT start sector */
+		fs->database = bsect + sysect;					/* Data start sector */
+		if (fmt == FS_FAT32) {
+			if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM;	/* (Must be FAT32 revision 0.0) */
+			if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM;	/* (BPB_RootEntCnt must be 0) */
+			fs->dirbase = ld_dword(fs->win + BPB_RootClus32);	/* Root directory start cluster */
+			szbfat = fs->n_fatent * 4;					/* (Needed FAT size) */
+		} else {
+			if (fs->n_rootdir == 0)	return FR_NO_FILESYSTEM;	/* (BPB_RootEntCnt must not be 0) */
+			fs->dirbase = fs->fatbase + fasize;			/* Root directory start sector */
+			szbfat = (fmt == FS_FAT16) ?				/* (Needed FAT size) */
+				fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+		}
+		if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM;	/* (BPB_FATSz must not be less than the size needed) */
+
+#if !FF_FS_READONLY
+		/* Get FSInfo if available */
+		fs->last_clst = fs->free_clst = 0xFFFFFFFF;		/* Initialize cluster allocation information */
+		fs->fsi_flag = 0x80;
+#if (FF_FS_NOFSINFO & 3) != 3
+		if (fmt == FS_FAT32				/* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
+			&& ld_word(fs->win + BPB_FSInfo32) == 1
+			&& move_window(fs, bsect + 1) == FR_OK)
+		{
+			fs->fsi_flag = 0;
+			if (ld_word(fs->win + BS_55AA) == 0xAA55	/* Load FSInfo data if available */
+				&& ld_dword(fs->win + FSI_LeadSig) == 0x41615252
+				&& ld_dword(fs->win + FSI_StrucSig) == 0x61417272)
+			{
+#if (FF_FS_NOFSINFO & 1) == 0
+				fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
+#endif
+#if (FF_FS_NOFSINFO & 2) == 0
+				fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
+#endif
+			}
+		}
+#endif	/* (FF_FS_NOFSINFO & 3) != 3 */
+#endif	/* !FF_FS_READONLY */
+	}
+
+	fs->fs_type = fmt;		/* FAT sub-type */
+	fs->id = ++Fsid;		/* Volume mount ID */
+#if FF_USE_LFN == 1
+	fs->lfnbuf = LfnBuf;	/* Static LFN working buffer */
+#if FF_FS_EXFAT
+	fs->dirbuf = DirBuf;	/* Static directory block scratchpad buuffer */
+#endif
+#endif
+#if FF_FS_RPATH != 0
+	fs->cdir = 0;			/* Initialize current directory */
+#endif
+#if FF_FS_LOCK != 0			/* Clear file lock semaphores */
+	clear_lock(fs);
+#endif
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/directory object is valid or not                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (	/* Returns FR_OK or FR_INVALID_OBJECT */
+	FFOBJID* obj,	/* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
+	FATFS** rfs		/* Pointer to pointer to the owner filesystem object to return */
+)
+{
+	FRESULT res = FR_INVALID_OBJECT;
+
+
+	if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) {	/* Test if the object is valid */
+#if FF_FS_REENTRANT
+		if (lock_fs(obj->fs)) {	/* Obtain the filesystem object */
+			if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+				res = FR_OK;
+			} else {
+				unlock_fs(obj->fs, FR_OK);
+			}
+		} else {
+			res = FR_TIMEOUT;
+		}
+#else
+		if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+			res = FR_OK;
+		}
+#endif
+	}
+	*rfs = (res == FR_OK) ? obj->fs : 0;	/* Corresponding filesystem object */
+	return res;
+}
+
+
+
+
+/*---------------------------------------------------------------------------
+
+   Public Functions (FatFs API)
+
+----------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+	FATFS* fs,			/* Pointer to the filesystem object (NULL:unmount)*/
+	const TCHAR* path,	/* Logical drive number to be mounted/unmounted */
+	BYTE opt			/* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
+)
+{
+	FATFS *cfs;
+	int vol;
+	FRESULT res;
+	const TCHAR *rp = path;
+
+
+	/* Get logical drive number */
+	vol = get_ldnumber(&rp);
+	if (vol < 0) return FR_INVALID_DRIVE;
+	cfs = FatFs[vol];					/* Pointer to fs object */
+
+	if (cfs) {
+#if FF_FS_LOCK != 0
+		clear_lock(cfs);
+#endif
+#if FF_FS_REENTRANT						/* Discard sync object of the current volume */
+		if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
+#endif
+		cfs->fs_type = 0;				/* Clear old fs object */
+	}
+
+	if (fs) {
+		fs->fs_type = 0;				/* Clear new fs object */
+#if FF_FS_REENTRANT						/* Create sync object for the new volume */
+		if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+	}
+	FatFs[vol] = fs;					/* Register new fs object */
+
+	if (opt == 0) return FR_OK;			/* Do not mount now, it will be mounted later */
+
+	res = find_volume(&path, &fs, 0);	/* Force mounted the volume */
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+	FIL* fp,			/* Pointer to the blank file object */
+	const TCHAR* path,	/* Pointer to the file name */
+	BYTE mode			/* Access mode and file open mode flags */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+#if !FF_FS_READONLY
+	DWORD dw, cl, bcs, clst, sc;
+	FSIZE_t ofs;
+#endif
+	DEF_NAMBUF
+
+
+	if (!fp) return FR_INVALID_OBJECT;
+
+	/* Get logical drive */
+	mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;
+	res = find_volume(&path, &fs, mode);
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);	/* Follow the file path */
+#if !FF_FS_READONLY	/* Read/Write configuration */
+		if (res == FR_OK) {
+			if (dj.fn[NSFLAG] & NS_NONAME) {	/* Origin directory itself? */
+				res = FR_INVALID_NAME;
+			}
+#if FF_FS_LOCK != 0
+			else {
+				res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);		/* Check if the file can be used */
+			}
+#endif
+		}
+		/* Create or Open a file */
+		if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+			if (res != FR_OK) {					/* No file, create new */
+				if (res == FR_NO_FILE) {		/* There is no file to open, create a new entry */
+#if FF_FS_LOCK != 0
+					res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+					res = dir_register(&dj);
+#endif
+				}
+				mode |= FA_CREATE_ALWAYS;		/* File is created */
+			}
+			else {								/* Any object with the same name is already existing */
+				if (dj.obj.attr & (AM_RDO | AM_DIR)) {	/* Cannot overwrite it (R/O or DIR) */
+					res = FR_DENIED;
+				} else {
+					if (mode & FA_CREATE_NEW) res = FR_EXIST;	/* Cannot create as new file */
+				}
+			}
+			if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {	/* Truncate the file if overwrite mode */
+#if FF_FS_EXFAT
+				if (fs->fs_type == FS_EXFAT) {
+					/* Get current allocation info */
+					fp->obj.fs = fs;
+					fp->obj.sclust = cl = ld_dword(fs->dirbuf + XDIR_FstClus);
+					fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);
+					fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+					fp->obj.n_frag = 0;
+					/* Set directory entry block initial state */
+					mem_set(fs->dirbuf + 2, 0, 30);		/* Clear 85 entry except for NumSec */
+					mem_set(fs->dirbuf + 38, 0, 26);	/* Clear C0 entry except for NumName and NameHash */
+					fs->dirbuf[XDIR_Attr] = AM_ARC;
+					st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME());
+					fs->dirbuf[XDIR_GenFlags] = 1;
+					res = store_xdir(&dj);
+					if (res == FR_OK && cl != 0) {		/* Remove the cluster chain if exist */
+						res = remove_chain(&fp->obj, cl, 0);
+						fs->last_clst = cl - 1;			/* Reuse the cluster hole */
+					}
+				} else
+#endif
+				{
+					/* Set directory entry initial state */
+					cl = ld_clust(fs, dj.dir);			/* Get current cluster chain */
+					st_dword(dj.dir + DIR_CrtTime, GET_FATTIME());	/* Set created time */
+					dj.dir[DIR_Attr] = AM_ARC;			/* Reset attribute */
+					st_clust(fs, dj.dir, 0);			/* Reset file allocation info */
+					st_dword(dj.dir + DIR_FileSize, 0);
+					fs->wflag = 1;
+					if (cl != 0) {						/* Remove the cluster chain if exist */
+						dw = fs->winsect;
+						res = remove_chain(&dj.obj, cl, 0);
+						if (res == FR_OK) {
+							res = move_window(fs, dw);
+							fs->last_clst = cl - 1;		/* Reuse the cluster hole */
+						}
+					}
+				}
+			}
+		}
+		else {	/* Open an existing file */
+			if (res == FR_OK) {					/* Is the object exsiting? */
+				if (dj.obj.attr & AM_DIR) {		/* File open against a directory */
+					res = FR_NO_FILE;
+				} else {
+					if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */
+						res = FR_DENIED;
+					}
+				}
+			}
+		}
+		if (res == FR_OK) {
+			if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED;	/* Set file change flag if created or overwritten */
+			fp->dir_sect = fs->winsect;			/* Pointer to the directory entry */
+			fp->dir_ptr = dj.dir;
+#if FF_FS_LOCK != 0
+			fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);	/* Lock the file for this session */
+			if (!fp->obj.lockid) res = FR_INT_ERR;
+#endif
+		}
+#else		/* R/O configuration */
+		if (res == FR_OK) {
+			if (dj.fn[NSFLAG] & NS_NONAME) {	/* Is it origin directory itself? */
+				res = FR_INVALID_NAME;
+			} else {
+				if (dj.obj.attr & AM_DIR) {		/* Is it a directory? */
+					res = FR_NO_FILE;
+				}
+			}
+		}
+#endif
+
+		if (res == FR_OK) {
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {
+				fp->obj.c_scl = dj.obj.sclust;							/* Get containing directory info */
+				fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+				fp->obj.c_ofs = dj.blk_ofs;
+				fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);	/* Get object allocation info */
+				fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);
+				fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+				fp->obj.n_frag = 0;
+			} else
+#endif
+			{
+				fp->obj.sclust = ld_clust(fs, dj.dir);					/* Get object allocation info */
+				fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);
+			}
+#if FF_USE_FASTSEEK
+			fp->cltbl = 0;			/* Disable fast seek mode */
+#endif
+			fp->obj.fs = fs;	 	/* Validate the file object */
+			fp->obj.id = fs->id;
+			fp->flag = mode;		/* Set file access mode */
+			fp->err = 0;			/* Clear error flag */
+			fp->sect = 0;			/* Invalidate current data sector */
+			fp->fptr = 0;			/* Set file pointer top of the file */
+#if !FF_FS_READONLY
+#if !FF_FS_TINY
+			mem_set(fp->buf, 0, FF_MAX_SS);	/* Clear sector buffer */
+#endif
+			if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) {	/* Seek to end of file if FA_OPEN_APPEND is specified */
+				fp->fptr = fp->obj.objsize;			/* Offset to seek */
+				bcs = (DWORD)fs->csize * SS(fs);	/* Cluster size in byte */
+				clst = fp->obj.sclust;				/* Follow the cluster chain */
+				for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {
+					clst = get_fat(&fp->obj, clst);
+					if (clst <= 1) res = FR_INT_ERR;
+					if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;
+				}
+				fp->clust = clst;
+				if (res == FR_OK && ofs % SS(fs)) {	/* Fill sector buffer if not on the sector boundary */
+					if ((sc = clst2sect(fs, clst)) == 0) {
+						res = FR_INT_ERR;
+					} else {
+						fp->sect = sc + (DWORD)(ofs / SS(fs));
+#if !FF_FS_TINY
+						if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;
+#endif
+					}
+				}
+			}
+#endif
+		}
+
+		FREE_NAMBUF();
+	}
+
+	if (res != FR_OK) fp->obj.fs = 0;	/* Invalidate file object on error */
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* 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;
+	FATFS *fs;
+	DWORD clst, sect;
+	FSIZE_t remain;
+	UINT rcnt, cc, csect;
+	BYTE *rbuff = (BYTE*)buff;
+
+
+	*br = 0;	/* Clear read byte counter */
+	res = validate(&fp->obj, &fs);				/* Check validity of the file object */
+	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);	/* Check validity */
+	if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+	remain = fp->obj.objsize - fp->fptr;
+	if (btr > remain) btr = (UINT)remain;		/* Truncate btr by remaining bytes */
+
+	for ( ;  btr;								/* Repeat until all data read */
+		btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) {
+		if (fp->fptr % SS(fs) == 0) {			/* On the sector boundary? */
+			csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));	/* Sector offset in the cluster */
+			if (csect == 0) {					/* On the cluster boundary? */
+				if (fp->fptr == 0) {			/* On the top of the file? */
+					clst = fp->obj.sclust;		/* Follow cluster chain from the origin */
+				} else {						/* Middle or end of the file */
+#if FF_USE_FASTSEEK
+					if (fp->cltbl) {
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					} else
+#endif
+					{
+						clst = get_fat(&fp->obj, fp->clust);	/* Follow cluster chain on the FAT */
+					}
+				}
+				if (clst < 2) ABORT(fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+				fp->clust = clst;				/* Update current cluster */
+			}
+			sect = clst2sect(fs, fp->clust);	/* Get current sector */
+			if (sect == 0) ABORT(fs, FR_INT_ERR);
+			sect += csect;
+			cc = btr / SS(fs);					/* When remaining bytes >= sector size, */
+			if (cc > 0) {						/* Read maximum contiguous sectors directly */
+				if (csect + cc > fs->csize) {	/* Clip at cluster boundary */
+					cc = fs->csize - csect;
+				}
+				if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
+#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2		/* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if FF_FS_TINY
+				if (fs->wflag && fs->winsect - sect < cc) {
+					mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
+				}
+#else
+				if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
+					mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
+				}
+#endif
+#endif
+				rcnt = SS(fs) * cc;				/* Number of bytes transferred */
+				continue;
+			}
+#if !FF_FS_TINY
+			if (fp->sect != sect) {			/* Load data sector if not in cache */
+#if !FF_FS_READONLY
+				if (fp->flag & FA_DIRTY) {		/* Write-back dirty sector cache */
+					if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+					fp->flag &= (BYTE)~FA_DIRTY;
+				}
+#endif
+				if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK)	ABORT(fs, FR_DISK_ERR);	/* Fill sector cache */
+			}
+#endif
+			fp->sect = sect;
+		}
+		rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);	/* Number of bytes left in the sector */
+		if (rcnt > btr) rcnt = btr;					/* Clip it by btr if needed */
+#if FF_FS_TINY
+		if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR);	/* Move sector window */
+		mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt);	/* Extract partial sector */
+#else
+		mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt);	/* Extract partial sector */
+#endif
+	}
+
+	LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+#if !FF_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;
+	FATFS *fs;
+	DWORD clst, sect;
+	UINT wcnt, cc, csect;
+	const BYTE *wbuff = (const BYTE*)buff;
+
+
+	*bw = 0;	/* Clear write byte counter */
+	res = validate(&fp->obj, &fs);			/* Check validity of the file object */
+	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);	/* Check validity */
+	if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);	/* Check access mode */
+
+	/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
+	if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
+		btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
+	}
+
+	for ( ;  btw;							/* Repeat until all data written */
+		btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) {
+		if (fp->fptr % SS(fs) == 0) {		/* On the sector boundary? */
+			csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1);	/* Sector offset in the cluster */
+			if (csect == 0) {				/* On the cluster boundary? */
+				if (fp->fptr == 0) {		/* On the top of the file? */
+					clst = fp->obj.sclust;	/* Follow from the origin */
+					if (clst == 0) {		/* If no cluster is allocated, */
+						clst = create_chain(&fp->obj, 0);	/* create a new cluster chain */
+					}
+				} else {					/* On the middle or end of the file */
+#if FF_USE_FASTSEEK
+					if (fp->cltbl) {
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					} else
+#endif
+					{
+						clst = create_chain(&fp->obj, fp->clust);	/* Follow or stretch cluster chain on the FAT */
+					}
+				}
+				if (clst == 0) break;		/* Could not allocate a new cluster (disk full) */
+				if (clst == 1) ABORT(fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+				fp->clust = clst;			/* Update current cluster */
+				if (fp->obj.sclust == 0) fp->obj.sclust = clst;	/* Set start cluster if the first write */
+			}
+#if FF_FS_TINY
+			if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);	/* Write-back sector cache */
+#else
+			if (fp->flag & FA_DIRTY) {		/* Write-back sector cache */
+				if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+				fp->flag &= (BYTE)~FA_DIRTY;
+			}
+#endif
+			sect = clst2sect(fs, fp->clust);	/* Get current sector */
+			if (sect == 0) ABORT(fs, FR_INT_ERR);
+			sect += csect;
+			cc = btw / SS(fs);				/* When remaining bytes >= sector size, */
+			if (cc > 0) {					/* Write maximum contiguous sectors directly */
+				if (csect + cc > fs->csize) {	/* Clip at cluster boundary */
+					cc = fs->csize - csect;
+				}
+				if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
+#if FF_FS_MINIMIZE <= 2
+#if FF_FS_TINY
+				if (fs->winsect - sect < cc) {	/* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
+					fs->wflag = 0;
+				}
+#else
+				if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
+					fp->flag &= (BYTE)~FA_DIRTY;
+				}
+#endif
+#endif
+				wcnt = SS(fs) * cc;		/* Number of bytes transferred */
+				continue;
+			}
+#if FF_FS_TINY
+			if (fp->fptr >= fp->obj.objsize) {	/* Avoid silly cache filling on the growing edge */
+				if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
+				fs->winsect = sect;
+			}
+#else
+			if (fp->sect != sect && 		/* Fill sector cache with file data */
+				fp->fptr < fp->obj.objsize &&
+				disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
+					ABORT(fs, FR_DISK_ERR);
+			}
+#endif
+			fp->sect = sect;
+		}
+		wcnt = SS(fs) - (UINT)fp->fptr % SS(fs);	/* Number of bytes left in the sector */
+		if (wcnt > btw) wcnt = btw;					/* Clip it by btw if needed */
+#if FF_FS_TINY
+		if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR);	/* Move sector window */
+		mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt);	/* Fit data to the sector */
+		fs->wflag = 1;
+#else
+		mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt);	/* Fit data to the sector */
+		fp->flag |= FA_DIRTY;
+#endif
+	}
+
+	fp->flag |= FA_MODIFIED;				/* Set file change flag */
+
+	LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File                                                  */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD tm;
+	BYTE *dir;
+
+
+	res = validate(&fp->obj, &fs);	/* Check validity of the file object */
+	if (res == FR_OK) {
+		if (fp->flag & FA_MODIFIED) {	/* Is there any change to the file? */
+#if !FF_FS_TINY
+			if (fp->flag & FA_DIRTY) {	/* Write-back cached data if needed */
+				if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
+				fp->flag &= (BYTE)~FA_DIRTY;
+			}
+#endif
+			/* Update the directory entry */
+			tm = GET_FATTIME();				/* Modified time */
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {
+				res = fill_first_frag(&fp->obj);	/* Fill first fragment on the FAT if needed */
+				if (res == FR_OK) {
+					res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);	/* Fill last fragment on the FAT if needed */
+				}
+				if (res == FR_OK) {
+					DIR dj;
+					DEF_NAMBUF
+
+					INIT_NAMBUF(fs);
+					res = load_obj_xdir(&dj, &fp->obj);	/* Load directory entry block */
+					if (res == FR_OK) {
+						fs->dirbuf[XDIR_Attr] |= AM_ARC;				/* Set archive attribute to indicate that the file has been changed */
+						fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1;	/* Update file allocation information */
+						st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);
+						st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);
+						st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);
+						st_dword(fs->dirbuf + XDIR_ModTime, tm);		/* Update modified time */
+						fs->dirbuf[XDIR_ModTime10] = 0;
+						st_dword(fs->dirbuf + XDIR_AccTime, 0);
+						res = store_xdir(&dj);	/* Restore it to the directory */
+						if (res == FR_OK) {
+							res = sync_fs(fs);
+							fp->flag &= (BYTE)~FA_MODIFIED;
+						}
+					}
+					FREE_NAMBUF();
+				}
+			} else
+#endif
+			{
+				res = move_window(fs, fp->dir_sect);
+				if (res == FR_OK) {
+					dir = fp->dir_ptr;
+					dir[DIR_Attr] |= AM_ARC;						/* Set archive attribute to indicate that the file has been changed */
+					st_clust(fp->obj.fs, dir, fp->obj.sclust);		/* Update file allocation information  */
+					st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize);	/* Update file size */
+					st_dword(dir + DIR_ModTime, tm);				/* Update modified time */
+					st_word(dir + DIR_LstAccDate, 0);
+					fs->wflag = 1;
+					res = sync_fs(fs);					/* Restore it to the directory */
+					fp->flag &= (BYTE)~FA_MODIFIED;
+				}
+			}
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+	FIL* fp		/* Pointer to the file object to be closed */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+
+#if !FF_FS_READONLY
+	res = f_sync(fp);					/* Flush cached data */
+	if (res == FR_OK)
+#endif
+	{
+		res = validate(&fp->obj, &fs);	/* Lock volume */
+		if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+			res = dec_lock(fp->obj.lockid);		/* Decrement file open counter */
+			if (res == FR_OK) fp->obj.fs = 0;	/* Invalidate file object */
+#else
+			fp->obj.fs = 0;	/* Invalidate file object */
+#endif
+#if FF_FS_REENTRANT
+			unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+		}
+	}
+	return res;
+}
+
+
+
+
+#if FF_FS_RPATH >= 1
+/*-----------------------------------------------------------------------*/
+/* Change Current Directory or Current Drive, Get Current Directory      */
+/*-----------------------------------------------------------------------*/
+
+#if FF_VOLUMES >= 2
+FRESULT f_chdrive (
+	const TCHAR* path		/* Drive number */
+)
+{
+	int vol;
+
+
+	/* Get logical drive number */
+	vol = get_ldnumber(&path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	CurrVol = (BYTE)vol;	/* Set it as current volume */
+
+	return FR_OK;
+}
+#endif
+
+
+FRESULT f_chdir (
+	const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	DEF_NAMBUF
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, 0);
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);		/* Follow the path */
+		if (res == FR_OK) {					/* Follow completed */
+			if (dj.fn[NSFLAG] & NS_NONAME) {
+				fs->cdir = dj.obj.sclust;	/* It is the start directory itself */
+#if FF_FS_EXFAT
+				if (fs->fs_type == FS_EXFAT) {
+					fs->cdc_scl = dj.obj.c_scl;
+					fs->cdc_size = dj.obj.c_size;
+					fs->cdc_ofs = dj.obj.c_ofs;
+				}
+#endif
+			} else {
+				if (dj.obj.attr & AM_DIR) {	/* It is a sub-directory */
+#if FF_FS_EXFAT
+					if (fs->fs_type == FS_EXFAT) {
+						fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus);		/* Sub-directory cluster */
+						fs->cdc_scl = dj.obj.sclust;						/* Save containing directory information */
+						fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+						fs->cdc_ofs = dj.blk_ofs;
+					} else
+#endif
+					{
+						fs->cdir = ld_clust(fs, dj.dir);					/* Sub-directory cluster */
+					}
+				} else {
+					res = FR_NO_PATH;		/* Reached but a file */
+				}
+			}
+		}
+		FREE_NAMBUF();
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+#if FF_FS_RPATH >= 2
+FRESULT f_getcwd (
+	TCHAR* buff,	/* Pointer to the directory path */
+	UINT len		/* Size of path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	UINT i, n;
+	DWORD ccl;
+	TCHAR *tp;
+	FILINFO fno;
+	DEF_NAMBUF
+
+
+	*buff = 0;
+	/* Get logical drive */
+	res = find_volume((const TCHAR**)&buff, &fs, 0);	/* Get current volume */
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		i = len;			/* Bottom of buffer (directory stack base) */
+		if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {	/* (Cannot do getcwd on exFAT and returns root path) */
+			dj.obj.sclust = fs->cdir;				/* Start to follow upper directory from current directory */
+			while ((ccl = dj.obj.sclust) != 0) {	/* Repeat while current directory is a sub-directory */
+				res = dir_sdi(&dj, 1 * SZDIRE);	/* Get parent directory */
+				if (res != FR_OK) break;
+				res = move_window(fs, dj.sect);
+				if (res != FR_OK) break;
+				dj.obj.sclust = ld_clust(fs, dj.dir);	/* Goto parent directory */
+				res = dir_sdi(&dj, 0);
+				if (res != FR_OK) break;
+				do {							/* Find the entry links to the child directory */
+					res = dir_read(&dj, 0);
+					if (res != FR_OK) break;
+					if (ccl == ld_clust(fs, dj.dir)) break;	/* Found the entry */
+					res = dir_next(&dj, 0);
+				} while (res == FR_OK);
+				if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+				if (res != FR_OK) break;
+				get_fileinfo(&dj, &fno);		/* Get the directory name and push it to the buffer */
+				for (n = 0; fno.fname[n]; n++) ;
+				if (i < n + 3) {
+					res = FR_NOT_ENOUGH_CORE; break;
+				}
+				while (n) buff[--i] = fno.fname[--n];
+				buff[--i] = '/';
+			}
+		}
+		tp = buff;
+		if (res == FR_OK) {
+#if FF_VOLUMES >= 2
+			*tp++ = '0' + CurrVol;			/* Put drive number */
+			*tp++ = ':';
+#endif
+			if (i == len) {					/* Root-directory */
+				*tp++ = '/';
+			} else {						/* Sub-directroy */
+				do		/* Add stacked path str */
+					*tp++ = buff[i++];
+				while (i < len);
+			}
+		}
+		*tp = 0;
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif /* FF_FS_RPATH >= 2 */
+#endif /* FF_FS_RPATH >= 1 */
+
+
+
+#if FF_FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File Read/Write Pointer                                          */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+	FIL* fp,		/* Pointer to the file object */
+	FSIZE_t ofs		/* File pointer from top of file */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD clst, bcs, nsect;
+	FSIZE_t ifptr;
+#if FF_USE_FASTSEEK
+	DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+#endif
+
+	res = validate(&fp->obj, &fs);		/* Check validity of the file object */
+	if (res == FR_OK) res = (FRESULT)fp->err;
+#if FF_FS_EXFAT && !FF_FS_READONLY
+	if (res == FR_OK && fs->fs_type == FS_EXFAT) {
+		res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);	/* Fill last fragment on the FAT if needed */
+	}
+#endif
+	if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_USE_FASTSEEK
+	if (fp->cltbl) {	/* Fast seek */
+		if (ofs == CREATE_LINKMAP) {	/* Create CLMT */
+			tbl = fp->cltbl;
+			tlen = *tbl++; ulen = 2;	/* Given table size and required table size */
+			cl = fp->obj.sclust;		/* Origin of the chain */
+			if (cl != 0) {
+				do {
+					/* Get a fragment */
+					tcl = cl; ncl = 0; ulen += 2;	/* Top, length and used items */
+					do {
+						pcl = cl; ncl++;
+						cl = get_fat(&fp->obj, cl);
+						if (cl <= 1) ABORT(fs, FR_INT_ERR);
+						if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+					} while (cl == pcl + 1);
+					if (ulen <= tlen) {		/* Store the length and top of the fragment */
+						*tbl++ = ncl; *tbl++ = tcl;
+					}
+				} while (cl < fs->n_fatent);	/* Repeat until end of chain */
+			}
+			*fp->cltbl = ulen;	/* Number of items used */
+			if (ulen <= tlen) {
+				*tbl = 0;		/* Terminate table */
+			} else {
+				res = FR_NOT_ENOUGH_CORE;	/* Given table size is smaller than required */
+			}
+		} else {						/* Fast seek */
+			if (ofs > fp->obj.objsize) ofs = fp->obj.objsize;	/* Clip offset at the file size */
+			fp->fptr = ofs;				/* Set file pointer */
+			if (ofs > 0) {
+				fp->clust = clmt_clust(fp, ofs - 1);
+				dsc = clst2sect(fs, fp->clust);
+				if (dsc == 0) ABORT(fs, FR_INT_ERR);
+				dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);
+				if (fp->fptr % SS(fs) && dsc != fp->sect) {	/* Refill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+					if (fp->flag & FA_DIRTY) {		/* Write-back dirty sector cache */
+						if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+						fp->flag &= (BYTE)~FA_DIRTY;
+					}
+#endif
+					if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);	/* Load current sector */
+#endif
+					fp->sect = dsc;
+				}
+			}
+		}
+	} else
+#endif
+
+	/* Normal Seek */
+	{
+#if FF_FS_EXFAT
+		if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF;	/* Clip at 4 GiB - 1 if at FATxx */
+#endif
+		if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) {	/* In read-only mode, clip offset with the file size */
+			ofs = fp->obj.objsize;
+		}
+		ifptr = fp->fptr;
+		fp->fptr = nsect = 0;
+		if (ofs > 0) {
+			bcs = (DWORD)fs->csize * SS(fs);	/* Cluster size (byte) */
+			if (ifptr > 0 &&
+				(ofs - 1) / bcs >= (ifptr - 1) / bcs) {	/* When seek to same or following cluster, */
+				fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1);	/* start from the current cluster */
+				ofs -= fp->fptr;
+				clst = fp->clust;
+			} else {									/* When seek to back cluster, */
+				clst = fp->obj.sclust;					/* start from the first cluster */
+#if !FF_FS_READONLY
+				if (clst == 0) {						/* If no cluster chain, create a new chain */
+					clst = create_chain(&fp->obj, 0);
+					if (clst == 1) ABORT(fs, FR_INT_ERR);
+					if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+					fp->obj.sclust = clst;
+				}
+#endif
+				fp->clust = clst;
+			}
+			if (clst != 0) {
+				while (ofs > bcs) {						/* Cluster following loop */
+					ofs -= bcs; fp->fptr += bcs;
+#if !FF_FS_READONLY
+					if (fp->flag & FA_WRITE) {			/* Check if in write mode or not */
+						if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) {	/* No FAT chain object needs correct objsize to generate FAT value */
+							fp->obj.objsize = fp->fptr;
+							fp->flag |= FA_MODIFIED;
+						}
+						clst = create_chain(&fp->obj, clst);	/* Follow chain with forceed stretch */
+						if (clst == 0) {				/* Clip file size in case of disk full */
+							ofs = 0; break;
+						}
+					} else
+#endif
+					{
+						clst = get_fat(&fp->obj, clst);	/* Follow cluster chain if not in write mode */
+					}
+					if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+					if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
+					fp->clust = clst;
+				}
+				fp->fptr += ofs;
+				if (ofs % SS(fs)) {
+					nsect = clst2sect(fs, clst);	/* Current sector */
+					if (nsect == 0) ABORT(fs, FR_INT_ERR);
+					nsect += (DWORD)(ofs / SS(fs));
+				}
+			}
+		}
+		if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) {	/* Set file change flag if the file size is extended */
+			fp->obj.objsize = fp->fptr;
+			fp->flag |= FA_MODIFIED;
+		}
+		if (fp->fptr % SS(fs) && nsect != fp->sect) {	/* Fill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+			if (fp->flag & FA_DIRTY) {			/* Write-back dirty sector cache */
+				if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+				fp->flag &= (BYTE)~FA_DIRTY;
+			}
+#endif
+			if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);	/* Fill sector cache */
+#endif
+			fp->sect = nsect;
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+	DIR* dp,			/* Pointer to directory object to create */
+	const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DEF_NAMBUF
+
+
+	if (!dp) return FR_INVALID_OBJECT;
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, 0);
+	if (res == FR_OK) {
+		dp->obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(dp, path);			/* Follow the path to the directory */
+		if (res == FR_OK) {						/* Follow completed */
+			if (!(dp->fn[NSFLAG] & NS_NONAME)) {	/* It is not the origin directory itself */
+				if (dp->obj.attr & AM_DIR) {		/* This object is a sub-directory */
+#if FF_FS_EXFAT
+					if (fs->fs_type == FS_EXFAT) {
+						dp->obj.c_scl = dp->obj.sclust;							/* Get containing directory inforamation */
+						dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+						dp->obj.c_ofs = dp->blk_ofs;
+						dp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus);	/* Get object allocation info */
+						dp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);
+						dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+					} else
+#endif
+					{
+						dp->obj.sclust = ld_clust(fs, dp->dir);	/* Get object allocation info */
+					}
+				} else {						/* This object is a file */
+					res = FR_NO_PATH;
+				}
+			}
+			if (res == FR_OK) {
+				dp->obj.id = fs->id;
+				res = dir_sdi(dp, 0);			/* Rewind directory */
+#if FF_FS_LOCK != 0
+				if (res == FR_OK) {
+					if (dp->obj.sclust != 0) {
+						dp->obj.lockid = inc_lock(dp, 0);	/* Lock the sub directory */
+						if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES;
+					} else {
+						dp->obj.lockid = 0;	/* Root directory need not to be locked */
+					}
+				}
+#endif
+			}
+		}
+		FREE_NAMBUF();
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+	if (res != FR_OK) dp->obj.fs = 0;		/* Invalidate the directory object if function faild */
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close Directory                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_closedir (
+	DIR *dp		/* Pointer to the directory object to be closed */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+
+
+	res = validate(&dp->obj, &fs);	/* Check validity of the file object */
+	if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+		if (dp->obj.lockid) res = dec_lock(dp->obj.lockid);	/* Decrement sub-directory open counter */
+		if (res == FR_OK) dp->obj.fs = 0;	/* Invalidate directory object */
+#else
+		dp->obj.fs = 0;	/* Invalidate directory object */
+#endif
+#if FF_FS_REENTRANT
+		unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entries in Sequence                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+	DIR* dp,			/* Pointer to the open directory object */
+	FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DEF_NAMBUF
+
+
+	res = validate(&dp->obj, &fs);	/* Check validity of the directory object */
+	if (res == FR_OK) {
+		if (!fno) {
+			res = dir_sdi(dp, 0);			/* Rewind the directory object */
+		} else {
+			INIT_NAMBUF(fs);
+			res = dir_read(dp, 0);			/* Read an item */
+			if (res == FR_NO_FILE) res = FR_OK;	/* Ignore end of directory */
+			if (res == FR_OK) {				/* A valid entry is found */
+				get_fileinfo(dp, fno);		/* Get the object information */
+				res = dir_next(dp, 0);		/* Increment index for next */
+				if (res == FR_NO_FILE) res = FR_OK;	/* Ignore end of directory now */
+			}
+			FREE_NAMBUF();
+		}
+	}
+	LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_USE_FIND
+/*-----------------------------------------------------------------------*/
+/* Find Next File                                                        */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findnext (
+	DIR* dp,		/* Pointer to the open directory object */
+	FILINFO* fno	/* Pointer to the file information structure */
+)
+{
+	FRESULT res;
+
+
+	for (;;) {
+		res = f_readdir(dp, fno);		/* Get a directory item */
+		if (res != FR_OK || !fno || !fno->fname[0]) break;	/* Terminate if any error or end of directory */
+		if (pattern_matching(dp->pat, fno->fname, 0, 0)) break;		/* Test for the file name */
+#if FF_USE_LFN && FF_USE_FIND == 2
+		if (pattern_matching(dp->pat, fno->altname, 0, 0)) break;	/* Test for alternative name if exist */
+#endif
+	}
+	return res;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find First File                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findfirst (
+	DIR* dp,				/* Pointer to the blank directory object */
+	FILINFO* fno,			/* Pointer to the file information structure */
+	const TCHAR* path,		/* Pointer to the directory to open */
+	const TCHAR* pattern	/* Pointer to the matching pattern */
+)
+{
+	FRESULT res;
+
+
+	dp->pat = pattern;		/* Save pointer to pattern string */
+	res = f_opendir(dp, path);		/* Open the target directory */
+	if (res == FR_OK) {
+		res = f_findnext(dp, fno);	/* Find the first item */
+	}
+	return res;
+}
+
+#endif	/* FF_USE_FIND */
+
+
+
+#if FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+	const TCHAR* path,	/* Pointer to the file path */
+	FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	DIR dj;
+	DEF_NAMBUF
+
+
+	/* Get logical drive */
+	res = find_volume(&path, &dj.obj.fs, 0);
+	if (res == FR_OK) {
+		INIT_NAMBUF(dj.obj.fs);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		if (res == FR_OK) {				/* Follow completed */
+			if (dj.fn[NSFLAG] & NS_NONAME) {	/* It is origin directory */
+				res = FR_INVALID_NAME;
+			} else {							/* Found an object */
+				if (fno) get_fileinfo(&dj, fno);
+			}
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(dj.obj.fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+	const TCHAR* path,	/* Path name of the logical drive number */
+	DWORD* nclst,		/* Pointer to a variable to return number of free clusters */
+	FATFS** fatfs		/* Pointer to return pointer to corresponding filesystem object */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD nfree, clst, sect, stat;
+	UINT i;
+	FFOBJID obj;
+
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, 0);
+	if (res == FR_OK) {
+		*fatfs = fs;				/* Return ptr to the fs object */
+		/* If free_clst is valid, return it without full FAT scan */
+		if (fs->free_clst <= fs->n_fatent - 2) {
+			*nclst = fs->free_clst;
+		} else {
+			/* Scan FAT to obtain number of free clusters */
+			nfree = 0;
+			if (fs->fs_type == FS_FAT12) {	/* FAT12: Scan bit field FAT entries */
+				clst = 2; obj.fs = fs;
+				do {
+					stat = get_fat(&obj, clst);
+					if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+					if (stat == 1) { res = FR_INT_ERR; break; }
+					if (stat == 0) nfree++;
+				} while (++clst < fs->n_fatent);
+			} else {
+#if FF_FS_EXFAT
+				if (fs->fs_type == FS_EXFAT) {	/* exFAT: Scan allocation bitmap */
+					BYTE bm;
+					UINT b;
+
+					clst = fs->n_fatent - 2;	/* Number of clusters */
+					sect = fs->database;		/* Assuming bitmap starts at cluster 2 */
+					i = 0;						/* Offset in the sector */
+					do {	/* Counts numbuer of bits with zero in the bitmap */
+						if (i == 0) {
+							res = move_window(fs, sect++);
+							if (res != FR_OK) break;
+						}
+						for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {
+							if (!(bm & 1)) nfree++;
+							bm >>= 1;
+						}
+						i = (i + 1) % SS(fs);
+					} while (clst);
+				} else
+#endif
+				{	/* FAT16/32: Scan WORD/DWORD FAT entries */
+					clst = fs->n_fatent;	/* Number of entries */
+					sect = fs->fatbase;		/* Top of the FAT */
+					i = 0;					/* Offset in the sector */
+					do {	/* Counts numbuer of entries with zero in the FAT */
+						if (i == 0) {
+							res = move_window(fs, sect++);
+							if (res != FR_OK) break;
+						}
+						if (fs->fs_type == FS_FAT16) {
+							if (ld_word(fs->win + i) == 0) nfree++;
+							i += 2;
+						} else {
+							if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++;
+							i += 4;
+						}
+						i %= SS(fs);
+					} while (--clst);
+				}
+			}
+			*nclst = nfree;			/* Return the free clusters */
+			fs->free_clst = nfree;	/* Now free_clst is valid */
+			fs->fsi_flag |= 1;		/* FAT32: FSInfo is to be updated */
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD ncl;
+
+
+	res = validate(&fp->obj, &fs);	/* Check validity of the file object */
+	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+	if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);	/* Check access mode */
+
+	if (fp->fptr < fp->obj.objsize) {	/* Process when fptr is not on the eof */
+		if (fp->fptr == 0) {	/* When set file size to zero, remove entire cluster chain */
+			res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+			fp->obj.sclust = 0;
+		} else {				/* When truncate a part of the file, remove remaining clusters */
+			ncl = get_fat(&fp->obj, fp->clust);
+			res = FR_OK;
+			if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+			if (ncl == 1) res = FR_INT_ERR;
+			if (res == FR_OK && ncl < fs->n_fatent) {
+				res = remove_chain(&fp->obj, ncl, fp->clust);
+			}
+		}
+		fp->obj.objsize = fp->fptr;	/* Set file size to current read/write point */
+		fp->flag |= FA_MODIFIED;
+#if !FF_FS_TINY
+		if (res == FR_OK && (fp->flag & FA_DIRTY)) {
+			if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
+				res = FR_DISK_ERR;
+			} else {
+				fp->flag &= (BYTE)~FA_DIRTY;
+			}
+		}
+#endif
+		if (res != FR_OK) ABORT(fs, res);
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File/Directory                                               */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+	const TCHAR* path		/* Pointer to the file or directory path */
+)
+{
+	FRESULT res;
+	DIR dj, sdj;
+	DWORD dclst = 0;
+	FATFS *fs;
+#if FF_FS_EXFAT
+	FFOBJID obj;
+#endif
+	DEF_NAMBUF
+
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, FA_WRITE);
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);		/* Follow the file path */
+		if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {
+			res = FR_INVALID_NAME;			/* Cannot remove dot entry */
+		}
+#if FF_FS_LOCK != 0
+		if (res == FR_OK) res = chk_lock(&dj, 2);	/* Check if it is an open object */
+#endif
+		if (res == FR_OK) {					/* The object is accessible */
+			if (dj.fn[NSFLAG] & NS_NONAME) {
+				res = FR_INVALID_NAME;		/* Cannot remove the origin directory */
+			} else {
+				if (dj.obj.attr & AM_RDO) {
+					res = FR_DENIED;		/* Cannot remove R/O object */
+				}
+			}
+			if (res == FR_OK) {
+#if FF_FS_EXFAT
+				obj.fs = fs;
+				if (fs->fs_type == FS_EXFAT) {
+					obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus);
+					obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize);
+					obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+				} else
+#endif
+				{
+					dclst = ld_clust(fs, dj.dir);
+				}
+				if (dj.obj.attr & AM_DIR) {			/* Is it a sub-directory? */
+#if FF_FS_RPATH != 0
+					if (dclst == fs->cdir) {		 	/* Is it the current directory? */
+						res = FR_DENIED;
+					} else
+#endif
+					{
+						sdj.obj.fs = fs;				/* Open the sub-directory */
+						sdj.obj.sclust = dclst;
+#if FF_FS_EXFAT
+						if (fs->fs_type == FS_EXFAT) {
+							sdj.obj.objsize = obj.objsize;
+							sdj.obj.stat = obj.stat;
+						}
+#endif
+						res = dir_sdi(&sdj, 0);
+						if (res == FR_OK) {
+							res = dir_read(&sdj, 0);			/* Read an item */
+							if (res == FR_OK) res = FR_DENIED;	/* Not empty? */
+							if (res == FR_NO_FILE) res = FR_OK;	/* Empty? */
+						}
+					}
+				}
+			}
+			if (res == FR_OK) {
+				res = dir_remove(&dj);			/* Remove the directory entry */
+				if (res == FR_OK && dclst) {	/* Remove the cluster chain if exist */
+#if FF_FS_EXFAT
+					res = remove_chain(&obj, dclst, 0);
+#else
+					res = remove_chain(&dj.obj, dclst, 0);
+#endif
+				}
+				if (res == FR_OK) res = sync_fs(fs);
+			}
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+	const TCHAR* path		/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	BYTE *dir;
+	DWORD dcl, pcl, tm;
+	DEF_NAMBUF
+
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, FA_WRITE);
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);			/* Follow the file path */
+		if (res == FR_OK) res = FR_EXIST;		/* Any object with same name is already existing */
+		if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) {
+			res = FR_INVALID_NAME;
+		}
+		if (res == FR_NO_FILE) {				/* Can create a new directory */
+			dcl = create_chain(&dj.obj, 0);		/* Allocate a cluster for the new directory table */
+			dj.obj.objsize = (DWORD)fs->csize * SS(fs);
+			res = FR_OK;
+			if (dcl == 0) res = FR_DENIED;		/* No space to allocate a new cluster */
+			if (dcl == 1) res = FR_INT_ERR;
+			if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+			if (res == FR_OK) res = sync_window(fs);	/* Flush FAT */
+			tm = GET_FATTIME();
+			if (res == FR_OK) {					/* Initialize the new directory table */
+				res = dir_clear(fs, dcl);		/* Clean up the new table */
+				if (res == FR_OK && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT)) {	/* Create dot entries (FAT only) */
+					dir = fs->win;
+					mem_set(dir + DIR_Name, ' ', 11);	/* Create "." entry */
+					dir[DIR_Name] = '.';
+					dir[DIR_Attr] = AM_DIR;
+					st_dword(dir + DIR_ModTime, tm);
+					st_clust(fs, dir, dcl);
+					mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */
+					dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
+					st_clust(fs, dir + SZDIRE, pcl);
+					fs->wflag = 1;
+				}
+			}
+			if (res == FR_OK) {
+				res = dir_register(&dj);	/* Register the object to the directoy */
+			}
+			if (res == FR_OK) {
+#if FF_FS_EXFAT
+				if (fs->fs_type == FS_EXFAT) {	/* Initialize directory entry block */
+					st_dword(fs->dirbuf + XDIR_ModTime, tm);	/* Created time */
+					st_dword(fs->dirbuf + XDIR_FstClus, dcl);	/* Table start cluster */
+					st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize);	/* File size needs to be valid */
+					st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize);
+					fs->dirbuf[XDIR_GenFlags] = 3;				/* Initialize the object flag */
+					fs->dirbuf[XDIR_Attr] = AM_DIR;				/* Attribute */
+					res = store_xdir(&dj);
+				} else
+#endif
+				{
+					dir = dj.dir;
+					st_dword(dir + DIR_ModTime, tm);	/* Created time */
+					st_clust(fs, dir, dcl);				/* Table start cluster */
+					dir[DIR_Attr] = AM_DIR;				/* Attribute */
+					fs->wflag = 1;
+				}
+				if (res == FR_OK) {
+					res = sync_fs(fs);
+				}
+			} else {
+				remove_chain(&dj.obj, dcl, 0);		/* Could not register, remove cluster chain */
+			}
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename a File/Directory                                               */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+	const TCHAR* path_old,	/* Pointer to the object name to be renamed */
+	const TCHAR* path_new	/* Pointer to the new name */
+)
+{
+	FRESULT res;
+	DIR djo, djn;
+	FATFS *fs;
+	BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
+	DWORD dw;
+	DEF_NAMBUF
+
+
+	get_ldnumber(&path_new);						/* Snip the drive number of new name off */
+	res = find_volume(&path_old, &fs, FA_WRITE);	/* Get logical drive of the old object */
+	if (res == FR_OK) {
+		djo.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&djo, path_old);		/* Check old object */
+		if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;	/* Check validity of name */
+#if FF_FS_LOCK != 0
+		if (res == FR_OK) {
+			res = chk_lock(&djo, 2);
+		}
+#endif
+		if (res == FR_OK) {						/* Object to be renamed is found */
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {	/* At exFAT volume */
+				BYTE nf, nn;
+				WORD nh;
+
+				mem_cpy(buf, fs->dirbuf, SZDIRE * 2);	/* Save 85+C0 entry of old object */
+				mem_cpy(&djn, &djo, sizeof djo);
+				res = follow_path(&djn, path_new);		/* Make sure if new object name is not in use */
+				if (res == FR_OK) {						/* Is new name already in use by any other object? */
+					res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+				}
+				if (res == FR_NO_FILE) { 				/* It is a valid path and no name collision */
+					res = dir_register(&djn);			/* Register the new entry */
+					if (res == FR_OK) {
+						nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];
+						nh = ld_word(fs->dirbuf + XDIR_NameHash);
+						mem_cpy(fs->dirbuf, buf, SZDIRE * 2);	/* Restore 85+C0 entry */
+						fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;
+						st_word(fs->dirbuf + XDIR_NameHash, nh);
+						if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC;	/* Set archive attribute if it is a file */
+/* Start of critical section where an interruption can cause a cross-link */
+						res = store_xdir(&djn);
+					}
+				}
+			} else
+#endif
+			{	/* At FAT/FAT32 volume */
+				mem_cpy(buf, djo.dir, SZDIRE);			/* Save directory entry of the object */
+				mem_cpy(&djn, &djo, sizeof (DIR));		/* Duplicate the directory object */
+				res = follow_path(&djn, path_new);		/* Make sure if new object name is not in use */
+				if (res == FR_OK) {						/* Is new name already in use by any other object? */
+					res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+				}
+				if (res == FR_NO_FILE) { 				/* It is a valid path and no name collision */
+					res = dir_register(&djn);			/* Register the new entry */
+					if (res == FR_OK) {
+						dir = djn.dir;					/* Copy directory entry of the object except name */
+						mem_cpy(dir + 13, buf + 13, SZDIRE - 13);
+						dir[DIR_Attr] = buf[DIR_Attr];
+						if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC;	/* Set archive attribute if it is a file */
+						fs->wflag = 1;
+						if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) {	/* Update .. entry in the sub-directory if needed */
+							dw = clst2sect(fs, ld_clust(fs, dir));
+							if (dw == 0) {
+								res = FR_INT_ERR;
+							} else {
+/* Start of critical section where an interruption can cause a cross-link */
+								res = move_window(fs, dw);
+								dir = fs->win + SZDIRE * 1;	/* Ptr to .. entry */
+								if (res == FR_OK && dir[1] == '.') {
+									st_clust(fs, dir, djn.obj.sclust);
+									fs->wflag = 1;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (res == FR_OK) {
+				res = dir_remove(&djo);		/* Remove old entry */
+				if (res == FR_OK) {
+					res = sync_fs(fs);
+				}
+			}
+/* End of the critical section */
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_FS_MINIMIZE == 0 */
+#endif /* FF_FS_MINIMIZE <= 1 */
+#endif /* FF_FS_MINIMIZE <= 2 */
+
+
+
+#if FF_USE_CHMOD && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+	const TCHAR* path,	/* Pointer to the file path */
+	BYTE attr,			/* Attribute bits */
+	BYTE mask			/* Attribute mask to change */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	DEF_NAMBUF
+
+
+	res = find_volume(&path, &fs, FA_WRITE);	/* Get logical drive */
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;	/* Check object validity */
+		if (res == FR_OK) {
+			mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;	/* Valid attribute mask */
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {
+				fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask);	/* Apply attribute change */
+				res = store_xdir(&dj);
+			} else
+#endif
+			{
+				dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask);	/* Apply attribute change */
+				fs->wflag = 1;
+			}
+			if (res == FR_OK) {
+				res = sync_fs(fs);
+			}
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+	const TCHAR* path,	/* Pointer to the file/directory name */
+	const FILINFO* fno	/* Pointer to the timestamp to be set */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	DEF_NAMBUF
+
+
+	res = find_volume(&path, &fs, FA_WRITE);	/* Get logical drive */
+	if (res == FR_OK) {
+		dj.obj.fs = fs;
+		INIT_NAMBUF(fs);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;	/* Check object validity */
+		if (res == FR_OK) {
+#if FF_FS_EXFAT
+			if (fs->fs_type == FS_EXFAT) {
+				st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+				res = store_xdir(&dj);
+			} else
+#endif
+			{
+				st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+				fs->wflag = 1;
+			}
+			if (res == FR_OK) {
+				res = sync_fs(fs);
+			}
+		}
+		FREE_NAMBUF();
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif	/* FF_USE_CHMOD && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LABEL
+/*-----------------------------------------------------------------------*/
+/* Get Volume Label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getlabel (
+	const TCHAR* path,	/* Path name of the logical drive number */
+	TCHAR* label,		/* Pointer to a buffer to store the volume label */
+	DWORD* vsn			/* Pointer to a variable to store the volume serial number */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	UINT si, di;
+#if (FF_LFN_UNICODE && FF_USE_LFN) || FF_FS_EXFAT
+	WCHAR w;
+#endif
+
+	/* Get logical drive */
+	res = find_volume(&path, &fs, 0);
+
+	/* Get volume label */
+	if (res == FR_OK && label) {
+		dj.obj.fs = fs; dj.obj.sclust = 0;	/* Open root directory */
+		res = dir_sdi(&dj, 0);
+		if (res == FR_OK) {
+		 	res = dir_read(&dj, 1);			/* Find a volume label entry */
+		 	if (res == FR_OK) {
+#if FF_FS_EXFAT
+				if (fs->fs_type == FS_EXFAT) {
+					for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) {	/* Extract volume label from 83 entry */
+						w = ld_word(dj.dir + XDIR_Label + si * 2);
+#if !FF_LFN_UNICODE		/* ANSI/OEM API */
+						w = ff_uni2oem(w, CODEPAGE);	/* Unicode -> OEM */
+						if (w == 0) w = '?';			/* Replace wrong char with '?' */
+						if (w >= 0x100) label[di++] = (char)(w >> 8);
+#endif
+						label[di++] = (TCHAR)w;
+					}
+					label[di] = 0;
+				} else
+#endif
+				{
+					si = di = 0;		/* Extract volume label from AM_VOL entry with code comversion */
+					do {
+#if FF_LFN_UNICODE && FF_USE_LFN	/* Unicode API */
+						w = (si < 11) ? dj.dir[si++] : ' ';
+						if (dbc_1st((BYTE)w) && si < 11 && dbc_2nd(dj.dir[si])) {
+							w = w << 8 | dj.dir[si++];
+						}
+						label[di++] = ff_oem2uni(w, CODEPAGE);	/* OEM -> Unicode */
+#else								/* ANSI/OEM API */
+						label[di++] = dj.dir[si++];
+#endif
+					} while (di < 11);
+					do {				/* Truncate trailing spaces */
+						label[di] = 0;
+						if (di == 0) break;
+					} while (label[--di] == ' ');
+				}
+			}
+		}
+		if (res == FR_NO_FILE) {	/* No label entry and return nul string */
+			label[0] = 0;
+			res = FR_OK;
+		}
+	}
+
+	/* Get volume serial number */
+	if (res == FR_OK && vsn) {
+		res = move_window(fs, fs->volbase);
+		if (res == FR_OK) {
+			switch (fs->fs_type) {
+			case FS_EXFAT:
+				di = BPB_VolIDEx; break;
+
+			case FS_FAT32:
+				di = BS_VolID32; break;
+
+			default:
+				di = BS_VolID;
+			}
+			*vsn = ld_dword(fs->win + di);
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Set Volume Label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setlabel (
+	const TCHAR* label	/* Pointer to the volume label to set */
+)
+{
+	FRESULT res;
+	DIR dj;
+	FATFS *fs;
+	BYTE dirvn[22];
+	UINT i, j, slen;
+	WCHAR w;
+	static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F";
+
+
+	/* Get logical drive */
+	res = find_volume(&label, &fs, FA_WRITE);
+	if (res != FR_OK) LEAVE_FF(fs, res);
+	dj.obj.fs = fs;
+
+	/* Get length of given volume label */
+	for (slen = 0; (UINT)label[slen] >= ' '; slen++) ;	/* Get name length */
+
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {	/* On the exFAT volume */
+		for (i = j = 0; i < slen; ) {	/* Create volume label in directory form */
+			w = label[i++];
+#if !FF_LFN_UNICODE	/* ANSI/OEM API */
+			if (dbc_1st((BYTE)w)) {
+				w = (i < slen && dbc_2nd((BYTE)label[i])) ? w << 8 | (BYTE)label[i++] : 0;
+			}
+			w = ff_oem2uni(w, CODEPAGE);
+#endif
+			if (w == 0 || chk_chr(badchr, w) || j == 22) {	/* Check validity check validity of the volume label */
+				LEAVE_FF(fs, FR_INVALID_NAME);
+			}
+			st_word(dirvn + j, w); j += 2;
+		}
+		slen = j;
+	} else
+#endif
+	{	/* On the FAT/FAT32 volume */
+		for ( ; slen && label[slen - 1] == ' '; slen--) ;	/* Remove trailing spaces */
+		if (slen != 0) {		/* Is there a volume label to be set? */
+			dirvn[0] = 0; i = j = 0;	/* Create volume label in directory form */
+			do {
+#if FF_LFN_UNICODE && FF_USE_LFN	/* Unicode API */
+				w = ff_uni2oem(ff_wtoupper(label[i++]), CODEPAGE);
+#else								/* ANSI/OEM API */
+				w = (BYTE)label[i++];
+				if (dbc_1st((BYTE)w)) {
+					w = (j < 10 && i < slen && dbc_2nd((BYTE)label[i])) ? w << 8 | (BYTE)label[i++] : 0;
+				}
+#if FF_USE_LFN
+				w = ff_uni2oem(ff_wtoupper(ff_oem2uni(w, CODEPAGE)), CODEPAGE);
+#else
+				if (IsLower(w)) w -= 0x20;			/* To upper ASCII characters */
+#if FF_CODE_PAGE == 0
+				if (ExCvt && w >= 0x80) w = ExCvt[w - 0x80];	/* To upper extended characters (SBCS cfg) */
+#elif FF_CODE_PAGE < 900
+				if (w >= 0x80) w = ExCvt[w - 0x80];	/* To upper extended characters (SBCS cfg) */
+#endif
+#endif
+#endif
+				if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) {	/* Reject invalid characters for volume label */
+					LEAVE_FF(fs, FR_INVALID_NAME);
+				}
+				if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8);
+				dirvn[j++] = (BYTE)w;
+			} while (i < slen);
+			while (j < 11) dirvn[j++] = ' ';	/* Fill remaining name field */
+			if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME);	/* Reject illegal name (heading DDEM) */
+		}
+	}
+
+	/* Set volume label */
+	dj.obj.sclust = 0;		/* Open root directory */
+	res = dir_sdi(&dj, 0);
+	if (res == FR_OK) {
+		res = dir_read(&dj, 1);	/* Get volume label entry */
+		if (res == FR_OK) {
+			if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+				dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2);	/* Change the volume label */
+				mem_cpy(dj.dir + XDIR_Label, dirvn, slen);
+			} else {
+				if (slen != 0) {
+					mem_cpy(dj.dir, dirvn, 11);	/* Change the volume label */
+				} else {
+					dj.dir[DIR_Name] = DDEM;	/* Remove the volume label */
+				}
+			}
+			fs->wflag = 1;
+			res = sync_fs(fs);
+		} else {			/* No volume label entry or an error */
+			if (res == FR_NO_FILE) {
+				res = FR_OK;
+				if (slen != 0) {	/* Create a volume label entry */
+					res = dir_alloc(&dj, 1);	/* Allocate an entry */
+					if (res == FR_OK) {
+						mem_set(dj.dir, 0, SZDIRE);	/* Clear the entry */
+						if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+							dj.dir[XDIR_Type] = 0x83;		/* Create 83 entry */
+							dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2);
+							mem_cpy(dj.dir + XDIR_Label, dirvn, slen);
+						} else {
+							dj.dir[DIR_Attr] = AM_VOL;		/* Create volume label entry */
+							mem_cpy(dj.dir, dirvn, 11);
+						}
+						fs->wflag = 1;
+						res = sync_fs(fs);
+					}
+				}
+			}
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_LABEL */
+
+
+
+#if FF_USE_EXPAND && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Allocate a Contiguous Blocks to the File                              */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_expand (
+	FIL* fp,		/* Pointer to the file object */
+	FSIZE_t fsz,	/* File size to be expanded to */
+	BYTE opt		/* Operation mode 0:Find and prepare or 1:Find and allocate */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD n, clst, stcl, scl, ncl, tcl, lclst;
+
+
+	res = validate(&fp->obj, &fs);		/* Check validity of the file object */
+	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+	if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
+#if FF_FS_EXFAT
+	if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED);	/* Check if in size limit */
+#endif
+	n = (DWORD)fs->csize * SS(fs);	/* Cluster size */
+	tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0);	/* Number of clusters required */
+	stcl = fs->last_clst; lclst = 0;
+	if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;
+
+#if FF_FS_EXFAT
+	if (fs->fs_type == FS_EXFAT) {
+		scl = find_bitmap(fs, stcl, tcl);			/* Find a contiguous cluster block */
+		if (scl == 0) res = FR_DENIED;				/* No contiguous cluster block was found */
+		if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;
+		if (res == FR_OK) {	/* A contiguous free area is found */
+			if (opt) {		/* Allocate it now */
+				res = change_bitmap(fs, scl, tcl, 1);	/* Mark the cluster block 'in use' */
+				lclst = scl + tcl - 1;
+			} else {		/* Set it as suggested point for next allocation */
+				lclst = scl - 1;
+			}
+		}
+	} else
+#endif
+	{
+		scl = clst = stcl; ncl = 0;
+		for (;;) {	/* Find a contiguous cluster block */
+			n = get_fat(&fp->obj, clst);
+			if (++clst >= fs->n_fatent) clst = 2;
+			if (n == 1) { res = FR_INT_ERR; break; }
+			if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+			if (n == 0) {	/* Is it a free cluster? */
+				if (++ncl == tcl) break;	/* Break if a contiguous cluster block is found */
+			} else {
+				scl = clst; ncl = 0;		/* Not a free cluster */
+			}
+			if (clst == stcl) { res = FR_DENIED; break; }	/* No contiguous cluster? */
+		}
+		if (res == FR_OK) {	/* A contiguous free area is found */
+			if (opt) {		/* Allocate it now */
+				for (clst = scl, n = tcl; n; clst++, n--) {	/* Create a cluster chain on the FAT */
+					res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);
+					if (res != FR_OK) break;
+					lclst = clst;
+				}
+			} else {		/* Set it as suggested point for next allocation */
+				lclst = scl - 1;
+			}
+		}
+	}
+
+	if (res == FR_OK) {
+		fs->last_clst = lclst;		/* Set suggested start cluster to start next */
+		if (opt) {	/* Is it allocated now? */
+			fp->obj.sclust = scl;		/* Update object allocation information */
+			fp->obj.objsize = fsz;
+			if (FF_FS_EXFAT) fp->obj.stat = 2;	/* Set status 'contiguous chain' */
+			fp->flag |= FA_MODIFIED;
+			if (fs->free_clst <= fs->n_fatent - 2) {	/* Update FSINFO */
+				fs->free_clst -= tcl;
+				fs->fsi_flag |= 1;
+			}
+		}
+	}
+
+	LEAVE_FF(fs, res);
+}
+
+#endif /* FF_USE_EXPAND && !FF_FS_READONLY */
+
+
+
+#if FF_USE_FORWARD
+/*-----------------------------------------------------------------------*/
+/* Forward Data to the Stream Directly                                   */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_forward (
+	FIL* fp, 						/* Pointer to the file object */
+	UINT (*func)(const BYTE*,UINT),	/* Pointer to the streaming function */
+	UINT btf,						/* Number of bytes to forward */
+	UINT* bf						/* Pointer to number of bytes forwarded */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD clst, sect;
+	FSIZE_t remain;
+	UINT rcnt, csect;
+	BYTE *dbuf;
+
+
+	*bf = 0;	/* Clear transfer byte counter */
+	res = validate(&fp->obj, &fs);		/* Check validity of the file object */
+	if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+	if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED);	/* Check access mode */
+
+	remain = fp->obj.objsize - fp->fptr;
+	if (btf > remain) btf = (UINT)remain;			/* Truncate btf by remaining bytes */
+
+	for ( ;  btf && (*func)(0, 0);					/* Repeat until all data transferred or stream goes busy */
+		fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
+		csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));	/* Sector offset in the cluster */
+		if (fp->fptr % SS(fs) == 0) {				/* On the sector boundary? */
+			if (csect == 0) {						/* On the cluster boundary? */
+				clst = (fp->fptr == 0) ?			/* On the top of the file? */
+					fp->obj.sclust : get_fat(&fp->obj, fp->clust);
+				if (clst <= 1) ABORT(fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+				fp->clust = clst;					/* Update current cluster */
+			}
+		}
+		sect = clst2sect(fs, fp->clust);			/* Get current data sector */
+		if (sect == 0) ABORT(fs, FR_INT_ERR);
+		sect += csect;
+#if FF_FS_TINY
+		if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR);	/* Move sector window to the file data */
+		dbuf = fs->win;
+#else
+		if (fp->sect != sect) {		/* Fill sector cache with file data */
+#if !FF_FS_READONLY
+			if (fp->flag & FA_DIRTY) {		/* Write-back dirty sector cache */
+				if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+				fp->flag &= (BYTE)~FA_DIRTY;
+			}
+#endif
+			if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+		}
+		dbuf = fp->buf;
+#endif
+		fp->sect = sect;
+		rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);	/* Number of bytes left in the sector */
+		if (rcnt > btf) rcnt = btf;					/* Clip it by btr if needed */
+		rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt);	/* Forward the file data */
+		if (rcnt == 0) ABORT(fs, FR_INT_ERR);
+	}
+
+	LEAVE_FF(fs, FR_OK);
+}
+#endif /* FF_USE_FORWARD */
+
+
+
+#if FF_USE_MKFS && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create an FAT/exFAT volume                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkfs (
+	const TCHAR* path,	/* Logical drive number */
+	BYTE opt,			/* Format option */
+	DWORD au,			/* Size of allocation unit (cluster) [byte] */
+	void* work,			/* Pointer to working buffer */
+	UINT len			/* Size of working buffer */
+)
+{
+	const UINT n_fats = 1;		/* Number of FATs for FAT/FAT32 volume (1 or 2) */
+	const UINT n_rootdir = 512;	/* Number of root directory entries for FAT volume */
+	static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0};	/* Cluster size boundary for FAT volume (4Ks unit) */
+	static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0};	/* Cluster size boundary for FAT32 volume (128Ks unit) */
+	BYTE fmt, sys, *buf, *pte, pdrv, part;
+	WORD ss;
+	DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;
+	DWORD b_vol, b_fat, b_data;				/* Base LBA for volume, fat, data */
+	DWORD sz_vol, sz_rsv, sz_fat, sz_dir;	/* Size for volume, fat, dir, data */
+	UINT i;
+	int vol;
+	DSTATUS stat;
+#if FF_USE_TRIM || FF_FS_EXFAT
+	DWORD tbl[3];
+#endif
+
+
+	/* Check mounted drive and clear work area */
+	vol = get_ldnumber(&path);					/* Get target logical drive */
+	if (vol < 0) return FR_INVALID_DRIVE;
+	if (FatFs[vol]) FatFs[vol]->fs_type = 0;	/* Clear the volume */
+	pdrv = LD2PD(vol);	/* Physical drive */
+	part = LD2PT(vol);	/* Partition (0:create as new, 1-4:get from partition table) */
+
+	/* Check physical drive status */
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+	if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1;	/* Erase block to align data area */
+#if FF_MAX_SS != FF_MIN_SS		/* Get sector size of the medium if variable sector size cfg. */
+	if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
+	if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
+#else
+	ss = FF_MAX_SS;
+#endif
+	if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER;	/* Check if au is valid */
+	au /= ss;	/* Cluster size in unit of sector */
+
+	/* Get working buffer */
+	buf = (BYTE*)work;		/* Working buffer */
+	sz_buf = len / ss;		/* Size of working buffer (sector) */
+	szb_buf = sz_buf * ss;	/* Size of working buffer (byte) */
+	if (szb_buf == 0) return FR_MKFS_ABORTED;
+
+	/* Determine where the volume to be located (b_vol, sz_vol) */
+	if (FF_MULTI_PARTITION && part != 0) {
+		/* Get partition information from partition table in the MBR */
+		if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;	/* Load MBR */
+		if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;	/* Check if MBR is valid */
+		pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
+		if (!pte[PTE_System]) return FR_MKFS_ABORTED;	/* No partition? */
+		b_vol = ld_dword(pte + PTE_StLba);		/* Get volume start sector */
+		sz_vol = ld_dword(pte + PTE_SizLba);	/* Get volume size */
+	} else {
+		/* Create a single-partition in this function */
+		if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR;
+		b_vol = (opt & FM_SFD) ? 0 : 63;		/* Volume start sector */
+		if (sz_vol < b_vol) return FR_MKFS_ABORTED;
+		sz_vol -= b_vol;						/* Volume size */
+	}
+	if (sz_vol < 128) return FR_MKFS_ABORTED;	/* Check if volume size is >=128s */
+
+	/* Pre-determine the FAT type */
+	do {
+		if (FF_FS_EXFAT && (opt & FM_EXFAT)) {	/* exFAT possible? */
+			if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) {	/* exFAT only, vol >= 64Ms or au > 128s ? */
+				fmt = FS_EXFAT; break;
+			}
+		}
+		if (au > 128) return FR_INVALID_PARAMETER;	/* Too large au for FAT/FAT32 */
+		if (opt & FM_FAT32) {	/* FAT32 possible? */
+			if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) {	/* FAT32 only or no-FAT? */
+				fmt = FS_FAT32; break;
+			}
+		}
+		if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER;	/* no-FAT? */
+		fmt = FS_FAT16;
+	} while (0);
+
+#if FF_FS_EXFAT
+	if (fmt == FS_EXFAT) {	/* Create an exFAT volume */
+		DWORD szb_bit, szb_case, sum, nb, cl;
+		WCHAR ch, si;
+		UINT j, st;
+		BYTE b;
+
+		if (sz_vol < 0x1000) return FR_MKFS_ABORTED;	/* Too small volume? */
+#if FF_USE_TRIM
+		tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1;	/* Inform the device the volume area may be erased */
+		disk_ioctl(pdrv, CTRL_TRIM, tbl);
+#endif
+		/* Determine FAT location, data location and number of clusters */
+		if (au == 0) {	/* au auto-selection */
+			au = 8;
+			if (sz_vol >= 0x80000) au = 64;		/* >= 512Ks */
+			if (sz_vol >= 0x4000000) au = 256;	/* >= 64Ms */
+		}
+		b_fat = b_vol + 32;										/* FAT start at offset 32 */
+		sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss;			/* Number of FAT sectors */
+		b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1);	/* Align data area to the erase block boundary */
+		if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED;		/* Too small volume? */
+		n_clst = (sz_vol - (b_data - b_vol)) / au;				/* Number of clusters */
+		if (n_clst <16) return FR_MKFS_ABORTED;					/* Too few clusters? */
+		if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED;			/* Too many clusters? */
+
+		szb_bit = (n_clst + 7) / 8;						/* Size of allocation bitmap */
+		tbl[0] = (szb_bit + au * ss - 1) / (au * ss);	/* Number of allocation bitmap clusters */
+
+		/* Create a compressed up-case table */
+		sect = b_data + au * tbl[0];	/* Table start sector */
+		sum = 0;						/* Table checksum to be stored in the 82 entry */
+		st = si = i = j = szb_case = 0;
+		do {
+			switch (st) {
+			case 0:
+				ch = ff_wtoupper(si);	/* Get an up-case char */
+				if (ch != si) {
+					si++; break;		/* Store the up-case char if exist */
+				}
+				for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ;	/* Get run length of no-case block */
+				if (j >= 128) {
+					ch = 0xFFFF; st = 2; break;	/* Compress the no-case block if run is >= 128 */
+				}
+				st = 1;			/* Do not compress short run */
+				/* go to next case */
+			case 1:
+				ch = si++;		/* Fill the short run */
+				if (--j == 0) st = 0;
+				break;
+
+			default:
+				ch = (WCHAR)j; si += j;	/* Number of chars to skip */
+				st = 0;
+			}
+			sum = xsum32(buf[i + 0] = (BYTE)ch, sum);		/* Put it into the write buffer */
+			sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
+			i += 2; szb_case += 2;
+			if (si == 0|| i == szb_buf) {		/* Write buffered data when buffer full or end of process */
+				n = (i + ss - 1) / ss;
+				if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
+				sect += n; i = 0;
+			}
+		} while (si);
+		tbl[1] = (szb_case + au * ss - 1) / (au * ss);	/* Number of up-case table clusters */
+		tbl[2] = 1;										/* Number of root dir clusters */
+
+		/* Initialize the allocation bitmap */
+		sect = b_data; nsect = (szb_bit + ss - 1) / ss;	/* Start of bitmap and number of sectors */
+		nb = tbl[0] + tbl[1] + tbl[2];					/* Number of clusters in-use by system */
+		do {
+			mem_set(buf, 0, szb_buf);
+			for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;
+			for (b = 1; nb != 0 && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;
+			n = (nsect > sz_buf) ? sz_buf : nsect;		/* Write the buffered data */
+			if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
+			sect += n; nsect -= n;
+		} while (nsect);
+
+		/* Initialize the FAT */
+		sect = b_fat; nsect = sz_fat;	/* Start of FAT and number of FAT sectors */
+		j = nb = cl = 0;
+		do {
+			mem_set(buf, 0, szb_buf); i = 0;	/* Clear work area and reset write index */
+			if (cl == 0) {	/* Set entry 0 and 1 */
+				st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
+				st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
+			}
+			do {			/* Create chains of bitmap, up-case and root dir */
+				while (nb != 0 && i < szb_buf) {			/* Create a chain */
+					st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
+					i += 4; cl++; nb--;
+				}
+				if (nb == 0 && j < 3) nb = tbl[j++];	/* Next chain */
+			} while (nb != 0 && i < szb_buf);
+			n = (nsect > sz_buf) ? sz_buf : nsect;	/* Write the buffered data */
+			if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
+			sect += n; nsect -= n;
+		} while (nsect);
+
+		/* Initialize the root directory */
+		mem_set(buf, 0, szb_buf);
+		buf[SZDIRE * 0 + 0] = 0x83;		/* 83 entry (volume label) */
+		buf[SZDIRE * 1 + 0] = 0x81;		/* 81 entry (allocation bitmap) */
+		st_dword(buf + SZDIRE * 1 + 20, 2);
+		st_dword(buf + SZDIRE * 1 + 24, szb_bit);
+		buf[SZDIRE * 2 + 0] = 0x82;		/* 82 entry (up-case table) */
+		st_dword(buf + SZDIRE * 2 + 4, sum);
+		st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]);
+		st_dword(buf + SZDIRE * 2 + 24, szb_case);
+		sect = b_data + au * (tbl[0] + tbl[1]);	nsect = au;	/* Start of the root directory and number of sectors */
+		do {	/* Fill root directory sectors */
+			n = (nsect > sz_buf) ? sz_buf : nsect;
+			if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
+			mem_set(buf, 0, ss);
+			sect += n; nsect -= n;
+		} while (nsect);
+
+		/* Create two set of the exFAT VBR blocks */
+		sect = b_vol;
+		for (n = 0; n < 2; n++) {
+			/* Main record (+0) */
+			mem_set(buf, 0, ss);
+			mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT   ", 11);	/* Boot jump code (x86), OEM name */
+			st_dword(buf + BPB_VolOfsEx, b_vol);					/* Volume offset in the physical drive [sector] */
+			st_dword(buf + BPB_TotSecEx, sz_vol);					/* Volume size [sector] */
+			st_dword(buf + BPB_FatOfsEx, b_fat - b_vol);			/* FAT offset [sector] */
+			st_dword(buf + BPB_FatSzEx, sz_fat);					/* FAT size [sector] */
+			st_dword(buf + BPB_DataOfsEx, b_data - b_vol);			/* Data offset [sector] */
+			st_dword(buf + BPB_NumClusEx, n_clst);					/* Number of clusters */
+			st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]);	/* Root dir cluster # */
+			st_dword(buf + BPB_VolIDEx, GET_FATTIME());				/* VSN */
+			st_word(buf + BPB_FSVerEx, 0x100);						/* Filesystem version (1.00) */
+			for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ;	/* Log2 of sector size [byte] */
+			for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ;	/* Log2 of cluster size [sector] */
+			buf[BPB_NumFATsEx] = 1;					/* Number of FATs */
+			buf[BPB_DrvNumEx] = 0x80;				/* Drive number (for int13) */
+			st_word(buf + BS_BootCodeEx, 0xFEEB);	/* Boot code (x86) */
+			st_word(buf + BS_55AA, 0xAA55);			/* Signature (placed here regardless of sector size) */
+			for (i = sum = 0; i < ss; i++) {		/* VBR checksum */
+				if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
+			}
+			if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;
+			/* Extended bootstrap record (+1..+8) */
+			mem_set(buf, 0, ss);
+			st_word(buf + ss - 2, 0xAA55);	/* Signature (placed at end of sector) */
+			for (j = 1; j < 9; j++) {
+				for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;	/* VBR checksum */
+				if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;
+			}
+			/* OEM/Reserved record (+9..+10) */
+			mem_set(buf, 0, ss);
+			for ( ; j < 11; j++) {
+				for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;	/* VBR checksum */
+				if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;
+			}
+			/* Sum record (+11) */
+			for (i = 0; i < ss; i += 4) st_dword(buf + i, sum);		/* Fill with checksum value */
+			if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR;
+		}
+
+	} else
+#endif	/* FF_FS_EXFAT */
+	{	/* Create an FAT/FAT32 volume */
+		do {
+			pau = au;
+			/* Pre-determine number of clusters and FAT sub-type */
+			if (fmt == FS_FAT32) {	/* FAT32 volume */
+				if (pau == 0) {	/* au auto-selection */
+					n = sz_vol / 0x20000;	/* Volume size in unit of 128KS */
+					for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ;	/* Get from table */
+				}
+				n_clst = sz_vol / pau;	/* Number of clusters */
+				sz_fat = (n_clst * 4 + 8 + ss - 1) / ss;	/* FAT size [sector] */
+				sz_rsv = 32;	/* Number of reserved sectors */
+				sz_dir = 0;		/* No static directory */
+				if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED;
+			} else {				/* FAT volume */
+				if (pau == 0) {	/* au auto-selection */
+					n = sz_vol / 0x1000;	/* Volume size in unit of 4KS */
+					for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ;	/* Get from table */
+				}
+				n_clst = sz_vol / pau;
+				if (n_clst > MAX_FAT12) {
+					n = n_clst * 2 + 4;		/* FAT size [byte] */
+				} else {
+					fmt = FS_FAT12;
+					n = (n_clst * 3 + 1) / 2 + 3;	/* FAT size [byte] */
+				}
+				sz_fat = (n + ss - 1) / ss;		/* FAT size [sector] */
+				sz_rsv = 1;						/* Number of reserved sectors */
+				sz_dir = (DWORD)n_rootdir * SZDIRE / ss;	/* Rootdir size [sector] */
+			}
+			b_fat = b_vol + sz_rsv;						/* FAT base */
+			b_data = b_fat + sz_fat * n_fats + sz_dir;	/* Data base */
+
+			/* Align data base to erase block boundary (for flash memory media) */
+			n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data;	/* Next nearest erase block from current data base */
+			if (fmt == FS_FAT32) {		/* FAT32: Move FAT base */
+				sz_rsv += n; b_fat += n;
+			} else {					/* FAT: Expand FAT size */
+				sz_fat += n / n_fats;
+			}
+
+			/* Determine number of clusters and final check of validity of the FAT sub-type */
+			if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED;	/* Too small volume */
+			n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau;
+			if (fmt == FS_FAT32) {
+				if (n_clst <= MAX_FAT16) {	/* Too few clusters for FAT32 */
+					if (au == 0 && (au = pau / 2) != 0) continue;	/* Adjust cluster size and retry */
+					return FR_MKFS_ABORTED;
+				}
+			}
+			if (fmt == FS_FAT16) {
+				if (n_clst > MAX_FAT16) {	/* Too many clusters for FAT16 */
+					if (au == 0 && (pau * 2) <= 64) {
+						au = pau * 2; continue;		/* Adjust cluster size and retry */
+					}
+					if ((opt & FM_FAT32)) {
+						fmt = FS_FAT32; continue;	/* Switch type to FAT32 and retry */
+					}
+					if (au == 0 && (au = pau * 2) <= 128) continue;	/* Adjust cluster size and retry */
+					return FR_MKFS_ABORTED;
+				}
+				if  (n_clst <= MAX_FAT12) {	/* Too few clusters for FAT16 */
+					if (au == 0 && (au = pau * 2) <= 128) continue;	/* Adjust cluster size and retry */
+					return FR_MKFS_ABORTED;
+				}
+			}
+			if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED;	/* Too many clusters for FAT12 */
+
+			/* Ok, it is the valid cluster configuration */
+			break;
+		} while (1);
+
+#if FF_USE_TRIM
+		tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1;	/* Inform the device the volume area can be erased */
+		disk_ioctl(pdrv, CTRL_TRIM, tbl);
+#endif
+		/* Create FAT VBR */
+		mem_set(buf, 0, ss);
+		mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */
+		st_word(buf + BPB_BytsPerSec, ss);				/* Sector size [byte] */
+		buf[BPB_SecPerClus] = (BYTE)pau;				/* Cluster size [sector] */
+		st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv);	/* Size of reserved area */
+		buf[BPB_NumFATs] = (BYTE)n_fats;				/* Number of FATs */
+		st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir));	/* Number of root directory entries */
+		if (sz_vol < 0x10000) {
+			st_word(buf + BPB_TotSec16, (WORD)sz_vol);	/* Volume size in 16-bit LBA */
+		} else {
+			st_dword(buf + BPB_TotSec32, sz_vol);		/* Volume size in 32-bit LBA */
+		}
+		buf[BPB_Media] = 0xF8;							/* Media descriptor byte */
+		st_word(buf + BPB_SecPerTrk, 63);				/* Number of sectors per track (for int13) */
+		st_word(buf + BPB_NumHeads, 255);				/* Number of heads (for int13) */
+		st_dword(buf + BPB_HiddSec, b_vol);				/* Volume offset in the physical drive [sector] */
+		if (fmt == FS_FAT32) {
+			st_dword(buf + BS_VolID32, GET_FATTIME());	/* VSN */
+			st_dword(buf + BPB_FATSz32, sz_fat);		/* FAT size [sector] */
+			st_dword(buf + BPB_RootClus32, 2);			/* Root directory cluster # (2) */
+			st_word(buf + BPB_FSInfo32, 1);				/* Offset of FSINFO sector (VBR + 1) */
+			st_word(buf + BPB_BkBootSec32, 6);			/* Offset of backup VBR (VBR + 6) */
+			buf[BS_DrvNum32] = 0x80;					/* Drive number (for int13) */
+			buf[BS_BootSig32] = 0x29;					/* Extended boot signature */
+			mem_cpy(buf + BS_VolLab32, "NO NAME    " "FAT32   ", 19);	/* Volume label, FAT signature */
+		} else {
+			st_dword(buf + BS_VolID, GET_FATTIME());	/* VSN */
+			st_word(buf + BPB_FATSz16, (WORD)sz_fat);	/* FAT size [sector] */
+			buf[BS_DrvNum] = 0x80;						/* Drive number (for int13) */
+			buf[BS_BootSig] = 0x29;						/* Extended boot signature */
+			mem_cpy(buf + BS_VolLab, "NO NAME    " "FAT     ", 19);	/* Volume label, FAT signature */
+		}
+		st_word(buf + BS_55AA, 0xAA55);					/* Signature (offset is fixed here regardless of sector size) */
+		if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR;	/* Write it to the VBR sector */
+
+		/* Create FSINFO record if needed */
+		if (fmt == FS_FAT32) {
+			disk_write(pdrv, buf, b_vol + 6, 1);		/* Write backup VBR (VBR + 6) */
+			mem_set(buf, 0, ss);
+			st_dword(buf + FSI_LeadSig, 0x41615252);
+			st_dword(buf + FSI_StrucSig, 0x61417272);
+			st_dword(buf + FSI_Free_Count, n_clst - 1);	/* Number of free clusters */
+			st_dword(buf + FSI_Nxt_Free, 2);			/* Last allocated cluster# */
+			st_word(buf + BS_55AA, 0xAA55);
+			disk_write(pdrv, buf, b_vol + 7, 1);		/* Write backup FSINFO (VBR + 7) */
+			disk_write(pdrv, buf, b_vol + 1, 1);		/* Write original FSINFO (VBR + 1) */
+		}
+
+		/* Initialize FAT area */
+		mem_set(buf, 0, (UINT)szb_buf);
+		sect = b_fat;		/* FAT start sector */
+		for (i = 0; i < n_fats; i++) {			/* Initialize FATs each */
+			if (fmt == FS_FAT32) {
+				st_dword(buf + 0, 0xFFFFFFF8);	/* Entry 0 */
+				st_dword(buf + 4, 0xFFFFFFFF);	/* Entry 1 */
+				st_dword(buf + 8, 0x0FFFFFFF);	/* Entry 2 (root directory) */
+			} else {
+				st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8);	/* Entry 0 and 1 */
+			}
+			nsect = sz_fat;		/* Number of FAT sectors */
+			do {	/* Fill FAT sectors */
+				n = (nsect > sz_buf) ? sz_buf : nsect;
+				if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
+				mem_set(buf, 0, ss);
+				sect += n; nsect -= n;
+			} while (nsect);
+		}
+
+		/* Initialize root directory (fill with zero) */
+		nsect = (fmt == FS_FAT32) ? pau : sz_dir;	/* Number of root directory sectors */
+		do {
+			n = (nsect > sz_buf) ? sz_buf : nsect;
+			if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
+			sect += n; nsect -= n;
+		} while (nsect);
+	}
+
+	/* Determine system ID in the partition table */
+	if (FF_FS_EXFAT && fmt == FS_EXFAT) {
+		sys = 0x07;			/* HPFS/NTFS/exFAT */
+	} else {
+		if (fmt == FS_FAT32) {
+			sys = 0x0C;		/* FAT32X */
+		} else {
+			if (sz_vol >= 0x10000) {
+				sys = 0x06;	/* FAT12/16 (large) */
+			} else {
+				sys = (fmt == FS_FAT16) ? 0x04 : 0x01;	/* FAT16 : FAT12 */
+			}
+		}
+	}
+
+	/* Update partition information */
+	if (FF_MULTI_PARTITION && part != 0) {	/* Created in the existing partition */
+		/* Update system ID in the partition table */
+		if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;	/* Read the MBR */
+		buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys;		/* Set system ID */
+		if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;	/* Write it back to the MBR */
+	} else {								/* Created as a new single partition */
+		if (!(opt & FM_SFD)) {	/* Create partition table if in FDISK format */
+			mem_set(buf, 0, ss);
+			st_word(buf + BS_55AA, 0xAA55);		/* MBR signature */
+			pte = buf + MBR_Table;				/* Create partition table for single partition in the drive */
+			pte[PTE_Boot] = 0;					/* Boot indicator */
+			pte[PTE_StHead] = 1;				/* Start head */
+			pte[PTE_StSec] = 1;					/* Start sector */
+			pte[PTE_StCyl] = 0;					/* Start cylinder */
+			pte[PTE_System] = sys;				/* System type */
+			n = (b_vol + sz_vol) / (63 * 255);	/* (End CHS may be invalid) */
+			pte[PTE_EdHead] = 254;				/* End head */
+			pte[PTE_EdSec] = (BYTE)(n >> 2 | 63);	/* End sector */
+			pte[PTE_EdCyl] = (BYTE)n;			/* End cylinder */
+			st_dword(pte + PTE_StLba, b_vol);	/* Start offset in LBA */
+			st_dword(pte + PTE_SizLba, sz_vol);	/* Size in sectors */
+			if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;	/* Write it to the MBR */
+		}
+	}
+
+	if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR;
+
+	return FR_OK;
+}
+
+
+
+#if FF_MULTI_PARTITION
+/*-----------------------------------------------------------------------*/
+/* Create Partition Table on the Physical Drive                          */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+	BYTE pdrv,			/* Physical drive number */
+	const DWORD* szt,	/* Pointer to the size table for each partitions */
+	void* work			/* Pointer to the working buffer */
+)
+{
+	UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+	BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+	DSTATUS stat;
+	DWORD sz_disk, sz_part, s_part;
+
+
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+	if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+	/* Determine the CHS without any consideration of the drive geometry */
+	for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+	if (n == 256) n--;
+	e_hd = n - 1;
+	sz_cyl = 63 * n;
+	tot_cyl = sz_disk / sz_cyl;
+
+	/* Create partition table */
+	mem_set(buf, 0, FF_MAX_SS);
+	p = buf + MBR_Table; b_cyl = 0;
+	for (i = 0; i < 4; i++, p += SZ_PTE) {
+		p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;	/* Number of cylinders */
+		if (p_cyl == 0) continue;
+		s_part = (DWORD)sz_cyl * b_cyl;
+		sz_part = (DWORD)sz_cyl * p_cyl;
+		if (i == 0) {	/* Exclude first track of cylinder 0 */
+			s_hd = 1;
+			s_part += 63; sz_part -= 63;
+		} else {
+			s_hd = 0;
+		}
+		e_cyl = b_cyl + p_cyl - 1;	/* End cylinder */
+		if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
+
+		/* Set partition table */
+		p[1] = s_hd;						/* Start head */
+		p[2] = (BYTE)((b_cyl >> 2) + 1);	/* Start sector */
+		p[3] = (BYTE)b_cyl;					/* Start cylinder */
+		p[4] = 0x07;						/* System type (temporary setting) */
+		p[5] = e_hd;						/* End head */
+		p[6] = (BYTE)((e_cyl >> 2) + 63);	/* End sector */
+		p[7] = (BYTE)e_cyl;					/* End cylinder */
+		st_dword(p + 8, s_part);			/* Start sector in LBA */
+		st_dword(p + 12, sz_part);			/* Number of sectors */
+
+		/* Next partition */
+		b_cyl += p_cyl;
+	}
+	st_word(p, 0xAA55);
+
+	/* Write it to the MBR */
+	return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK;
+}
+
+#endif /* FF_MULTI_PARTITION */
+#endif /* FF_USE_MKFS && !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a String from the File                                            */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+	TCHAR* buff,	/* Pointer to the string buffer to read */
+	int len,		/* Size of string buffer (characters) */
+	FIL* fp			/* Pointer to the file object */
+)
+{
+	int n = 0;
+	TCHAR c, *p = buff;
+	BYTE s[2];
+	UINT rc;
+
+
+	while (n < len - 1) {	/* Read characters until buffer gets filled */
+#if FF_LFN_UNICODE && FF_USE_LFN	/* Unicode API */
+#if FF_STRF_ENCODE == 3		/* Read a character in UTF-8 */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (c >= 0x80) {
+			if (c < 0xC0) continue;	/* Skip stray trailer */
+			if (c < 0xE0) {			/* Two-byte sequence (0x80-0x7FF) */
+				f_read(fp, s, 1, &rc);
+				if (rc != 1) break;
+				c = (c & 0x1F) << 6 | (s[0] & 0x3F);
+				if (c < 0x80) c = '?';	/* Reject invalid code range */
+			} else {
+				if (c < 0xF0) {		/* Three-byte sequence (0x800-0xFFFF) */
+					f_read(fp, s, 2, &rc);
+					if (rc != 2) break;
+					c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);
+					if (c < 0x800) c = '?';	/* Reject invalid code range */
+				} else {			/* Reject four-byte sequence */
+					c = '?';
+				}
+			}
+		}
+#elif FF_STRF_ENCODE == 2		/* Read a character in UTF-16BE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[1] + (s[0] << 8);
+#elif FF_STRF_ENCODE == 1		/* Read a character in UTF-16LE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[0] + (s[1] << 8);
+#else							/* Read a character in ANSI/OEM */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (dbc_1st((BYTE)c)) {
+			f_read(fp, s, 1, &rc);
+			if (rc != 1) break;
+			c = (c << 8) + s[0];
+		}
+		c = ff_oem2uni(c, CODEPAGE);	/* OEM -> Unicode */
+		if (!c) c = '?';
+#endif
+#else						/* ANSI/OEM API: Read a character without conversion */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+#endif
+		if (FF_USE_STRFUNC == 2 && c == '\r') continue;	/* Strip '\r' */
+		*p++ = c;
+		n++;
+		if (c == '\n') break;		/* Break on EOL */
+	}
+	*p = 0;
+	return n ? buff : 0;			/* When no data read (eof or error), return with error. */
+}
+
+
+
+
+#if !FF_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a Character to the File                                           */
+/*-----------------------------------------------------------------------*/
+
+typedef struct {
+	FIL *fp;		/* Ptr to the writing file */
+	int idx, nchr;	/* Write index of buf[] (-1:error), number of chars written */
+	BYTE buf[64];	/* Write buffer */
+} putbuff;
+
+
+static
+void putc_bfd (		/* Buffered write with code conversion */
+	putbuff* pb,
+	TCHAR c
+)
+{
+	UINT bw;
+	int i;
+
+
+	if (FF_USE_STRFUNC == 2 && c == '\n') {	 /* LF -> CRLF conversion */
+		putc_bfd(pb, '\r');
+	}
+
+	i = pb->idx;		/* Write index of pb->buf[] */
+	if (i < 0) return;
+
+#if FF_LFN_UNICODE && FF_USE_LFN	/* Unicode API */
+#if FF_STRF_ENCODE == 3			/* Write a character in UTF-8 */
+	if (c < 0x80) {				/* 7-bit */
+		pb->buf[i++] = (BYTE)c;
+	} else {
+		if (c < 0x800) {		/* 11-bit */
+			pb->buf[i++] = (BYTE)(0xC0 | c >> 6);
+		} else {				/* 16-bit */
+			pb->buf[i++] = (BYTE)(0xE0 | c >> 12);
+			pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));
+		}
+		pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));
+	}
+#elif FF_STRF_ENCODE == 2			/* Write a character in UTF-16BE */
+	pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#elif FF_STRF_ENCODE == 1			/* Write a character in UTF-16LE */
+	pb->buf[i++] = (BYTE)c;
+	pb->buf[i++] = (BYTE)(c >> 8);
+#else							/* Write a character in ANSI/OEM */
+	c = ff_uni2oem(c, CODEPAGE);	/* Unicode -> OEM */
+	if (!c) c = '?';
+	if (c >= 0x100)
+		pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#endif
+#else							/* ANSI/OEM API: Write a character without conversion */
+	pb->buf[i++] = (BYTE)c;
+#endif
+
+	if (i >= (int)(sizeof pb->buf) - 3) {	/* Write buffered characters to the file */
+		f_write(pb->fp, pb->buf, (UINT)i, &bw);
+		i = (bw == (UINT)i) ? 0 : -1;
+	}
+	pb->idx = i;
+	pb->nchr++;
+}
+
+
+static
+int putc_flush (		/* Flush left characters in the buffer */
+	putbuff* pb
+)
+{
+	UINT nw;
+
+	if (   pb->idx >= 0	/* Flush buffered characters to the file */
+		&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
+		&& (UINT)pb->idx == nw) return pb->nchr;
+	return EOF;
+}
+
+
+static
+void putc_init (		/* Initialize write buffer */
+	putbuff* pb,
+	FIL* fp
+)
+{
+	pb->fp = fp;
+	pb->nchr = pb->idx = 0;
+}
+
+
+
+int f_putc (
+	TCHAR c,	/* A character to be output */
+	FIL* fp		/* Pointer to the file object */
+)
+{
+	putbuff pb;
+
+
+	putc_init(&pb, fp);
+	putc_bfd(&pb, c);	/* Put the character */
+	return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a String to the File                                              */
+/*-----------------------------------------------------------------------*/
+
+int f_puts (
+	const TCHAR* str,	/* Pointer to the string to be output */
+	FIL* fp				/* Pointer to the file object */
+)
+{
+	putbuff pb;
+
+
+	putc_init(&pb, fp);
+	while (*str) putc_bfd(&pb, *str++);		/* Put the string */
+	return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a Formatted String to the File                                    */
+/*-----------------------------------------------------------------------*/
+
+int f_printf (
+	FIL* fp,			/* Pointer to the file object */
+	const TCHAR* fmt,	/* Pointer to the format string */
+	...					/* Optional arguments... */
+)
+{
+	va_list arp;
+	putbuff pb;
+	BYTE f, r;
+	UINT i, j, w;
+	DWORD v;
+	TCHAR c, d, str[32], *p;
+
+
+	putc_init(&pb, fp);
+
+	va_start(arp, fmt);
+
+	for (;;) {
+		c = *fmt++;
+		if (c == 0) break;			/* End of string */
+		if (c != '%') {				/* Non escape character */
+			putc_bfd(&pb, c);
+			continue;
+		}
+		w = f = 0;
+		c = *fmt++;
+		if (c == '0') {				/* Flag: '0' padding */
+			f = 1; c = *fmt++;
+		} else {
+			if (c == '-') {			/* Flag: left justified */
+				f = 2; c = *fmt++;
+			}
+		}
+		while (IsDigit(c)) {		/* Precision */
+			w = w * 10 + c - '0';
+			c = *fmt++;
+		}
+		if (c == 'l' || c == 'L') {	/* Prefix: Size is long int */
+			f |= 4; c = *fmt++;
+		}
+		if (!c) break;
+		d = c;
+		if (IsLower(d)) d -= 0x20;
+		switch (d) {				/* Type is... */
+		case 'S' :					/* String */
+			p = va_arg(arp, TCHAR*);
+			for (j = 0; p[j]; j++) ;
+			if (!(f & 2)) {						/* Right pad */
+				while (j++ < w) putc_bfd(&pb, ' ');
+			}
+			while (*p) putc_bfd(&pb, *p++);		/* String body */
+			while (j++ < w) putc_bfd(&pb, ' ');	/* Left pad */
+			continue;
+
+		case 'C' :					/* Character */
+			putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+
+		case 'B' :					/* Binary */
+			r = 2; break;
+
+		case 'O' :					/* Octal */
+			r = 8; break;
+
+		case 'D' :					/* Signed decimal */
+		case 'U' :					/* Unsigned decimal */
+			r = 10; break;
+
+		case 'X' :					/* Hexdecimal */
+			r = 16; break;
+
+		default:					/* Unknown type (pass-through) */
+			putc_bfd(&pb, c); continue;
+		}
+
+		/* Get an argument and put it in numeral */
+		v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
+		if (d == 'D' && (v & 0x80000000)) {
+			v = 0 - v;
+			f |= 8;
+		}
+		i = 0;
+		do {
+			d = (TCHAR)(v % r); v /= r;
+			if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+			str[i++] = d + '0';
+		} while (v && i < sizeof str / sizeof *str);
+		if (f & 8) str[i++] = '-';
+		j = i; d = (f & 1) ? '0' : ' ';
+		if (!(f & 2)) {
+			while (j++ < w) putc_bfd(&pb, d);	/* Right pad */
+		}
+		do {
+			putc_bfd(&pb, str[--i]);			/* Number body */
+		} while (i);
+		while (j++ < w) putc_bfd(&pb, d);		/* Left pad */
+	}
+
+	va_end(arp);
+
+	return putc_flush(&pb);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_STRFUNC */
+
+
+
+#if FF_CODE_PAGE == 0
+/*-----------------------------------------------------------------------*/
+/* Set Active Codepage for the Path Name                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setcp (
+	WORD cp		/* Value to be set as active code page */
+)
+{
+	static const WORD       validcp[] = {  437,   720,   737,   771,   775,   850,   852,   857,   860,   861,   862,   863,   864,   865,   866,   869,   932,   936,   949,   950, 0};
+	static const BYTE *const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0};
+	UINT i;
+
+
+	for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ;	/* Find the code page */
+	if (validcp[i] != cp) return FR_INVALID_PARAMETER;
+
+	CodePage = cp;
+	if (cp >= 900) {	/* DBCS */
+		ExCvt = 0;
+		DbcTbl = tables[i];
+	} else {			/* SBCS */
+		ExCvt = tables[i];
+		DbcTbl = 0;
+	}
+	return FR_OK;
+}
+#endif	/* FF_CODE_PAGE == 0 */
+
+

Some files were not shown because too many files changed in this diff